写完一个 AI 编程助手之后,我才确定 prompt 工程不是重点

写完一个 AI 编程助手之后,我才确定 prompt 工程不是重点 写完一个 AI 编程助手之后我才确定 prompt 工程不是重点代码开源在 GitHub从零开始可读code-agent。欢迎拍砖,点点star。用 Claude Code 用着用着我有个怪念头这东西底下是不是就一个 while 循环试着拆了一下发现真的就是用户消息 → 模型 → 文字结束 → 工具执行 → 把结果塞回去 → 继续代码写出来不到 20 行while(true){constresponseawaitmodel.chat({messages,tools})if(response.typetext)returnresponse.contentconstresultsawaitexecuteTools(response.tools)messages.push({role:assistant,content:response.tools})messages.push({role:user,content:results})}跑通它不难。难的是让它好用。我花了两个月把这个骨架做成能日常用的 Agent。过程中最反常识的发现是模型已经够强了prompt 工程不是瓶颈真正决定一个 Agent 是玩具还是工具的是下面这四个工程细节。一、流式工具调用是碎片化的非流式很爽一次拿完整响应分类型处理就行。但响应延迟感很差。切到流式之后第一件事就翻车工具调用是分散在多个 chunk 里到达的。chunk 1: tool_name Read chunk 2: input { \file_path\: chunk 3: input \/tmp/a.t chunk 4: input xt\ } chunk 5: done最容易踩的坑收到第一个tool_usechunk 就去执行。拿到的 input 要么是空对象要么是不合法的 JSON工具立刻炸。正确做法是先收集等 done 再执行并且用 Map不要用数组constcompletedToolsnewMapnumber,ToolCall()forawait(constchunkofstream){if(chunk.typetool_use){if(Object.keys(chunk.tool.input).length0){completedTools.set(chunk.toolIndex,chunk.tool)}}if(chunk.typedone){constresultsawaitexecuteTools([...completedTools.values()])// ...}}为什么用 Mapchunk 不保证按序到达toolIndex才是稳定的 key。这种细节读 SDK 文档读不到看官方 example 也学不到要自己踩进去才知道。二、跨进程的上下文你不显式传就一定丢记忆系统我做成独立的 Worker 进程Agent 把事件 POST 给 WorkerWorker 异步落 SQLite 跑 embedding。架构上是对的。但调试一周后我发现召回率低得离谱几乎什么都查不到。最后定位到这一行代码awaithooks.fire(post-tool-use,{TOOL_NAME:tool.name,TOOL_RESULT:result,// SESSION_ID 没传})Worker 收到 observation 之后session_id 是空的写进数据库就成了孤儿数据下次查询永远找不到。加一行就好SESSION_ID:sessionManager?.getCurrentSession()?.id??unknown教训不是下次别忘了传字段。是更普遍的规则跨进程边界上任何隐式上下文都不存在。Trace id、session id、user id —— 不显式传对方就拿不到。单进程内你还能靠闭包、AsyncLocalStorage、全局变量糊弄过去。一旦跨进程这些全部失效。所有上下文必须当成数据跟随消息一起发出去。三、语义召回阈值是调出来的不是算出来的记忆 v1 我用 SQL LIKE 做关键词匹配。上次说重构认证模块这次问auth 改了什么——找不到“认证” 和 “auth” 字面上不命中。v2 换成 embedding 向量搜索。本地跑all-MiniLM-L6-v250MB单次召回 50–200ms不花 API 钱。真正难的不是接入向量库是相似度阈值定多少。我试了三档阈值现象0.7几乎召不回任何东西。“修了 auth.ts” 和 “auth 模块有问题” 在向量空间也对不上0.1召回一堆噪音。system prompt 被撑到几千 token模型反而被干扰回答质量下降0.3相关的能召回明显不相关的被过滤掉0.3 不是算出来的是用真实对话逐档试出来的。更一般的结论任何涉及相关性的阈值都不要相信论文或博客里的数字。要在你自己的数据上跑出来。模型在你的领域上的向量分布、你的会话长度、你的 chunking 策略都会让阈值偏移。别人的 0.7 在你这里可能等价于 0.3。四、并发安全要工具自己说不要让框架去猜AI 经常一口气甩三个工具读三个文件或者 grep read write。全串行慢体验差。全并行两个写操作撞到一起就炸。让框架去猜哪些能并行是死胡同——它不知道工具的语义。干脆让每个工具自己声明classReadTool{isConcurrencySafe(){returntrue}// 只读永远安全}classWriteTool{isConcurrencySafe(input){returnfalse// 写操作永远不安全}}调度逻辑一行判断constallSafetools.every(tregistry.get(t.name)?.isConcurrencySafe(t.input))returnallSafe?Promise.all(tools.map(run)):runSerial(tools)这个设计的关键不是省时间是职责分配把语义留给工具把调度留给框架。框架不需要知道 Read 和 Write 在做什么只需要问一句你能并行吗。新增工具不用改调度器改调度器不用动工具。所以呢看 LangChain、AutoGPT 这些 Agent 框架的文档你会以为 Agent 的难点在 prompt 工程、在 chain 抽象、在 memory 设计。自己从零写一遍之后我的真实判断是这些抽象都是表象。真正决定一个 Agent 好不好用的是上面这种工程问题——流式状态机、跨进程上下文、阈值调参、并发安全。每一个都不需要 AI 知识全部是普通后端工程师该会的东西。这也是为什么大部分套壳产品体验都很差他们解决了 prompt 问题把工程问题留给了用户的耐心去消化。代码开源在 GitHub从零开始可读code-agent。欢迎拍砖,点点star。