agent不好用的原因,被我找到了
语义被切碎之后,我找到了缝回去的方法
原创 build in Public MingBuilds

上一篇我聊到,AI 产品真正的单位可能不是 Function,而是语义连续性。
那个认知,是我用 Coding Agent 去做 Agent 的时候,慢慢感觉到的。
但"感觉到"和"解决掉"之间,隔着一道巨大的鸿沟。
意识到语义在被切碎之后,我做的第一件事不是去翻论文,而是回到自己的项目里,一行一行地看——到底是哪一行代码,把语义切断的。
我盯着一个 if intent == "complaint" 看了很久。
然后我意识到:模型在上一步明明理解了"这个用户语气里带着疲惫,他其实不是在投诉,他只是想要一个解释"。但这段理解,在进入我的 if-else 的那一瞬间,就被压缩成了一个字符串 "complaint"。
所有的温度、所有的语气、所有说不清道不明的东西——全没了。
那天晚上我开始动手改。改着改着,逐渐摸出了一套方法。
这一篇,就是把这套方法交出来。
不是玄学,不是"你应该转变思维"。
是三层诊断、四条原则、五个开源项目。
是你今天就能拿去审视自己代码的东西。
01 先搞清楚:语义到底是在哪一层断掉的
在动手之前,我们得先诊断。
我在上一篇提到"低级语义在强行控制高级语义",但这句话太抽象了。翻译成工程语言,语义断裂通常发生在三个地方:
第一层:控制流层。
你用 Python 写了一棵决策树。if intent == "complaint": route_to_support()。模型原本理解的是"这个用户语气里带着失望,但他其实是想要一个解释",但你的代码只看到了 complaint 这个字符串。
语义在进入 if-else 的那一瞬间,就从一段连续的理解,变成了一个离散的标签。
第二层:记忆层。
用户跟你的 Agent 聊了 40 分钟。你的系统把这 40 分钟压缩成了三行 summary:“用户讨论了项目进度,表达了对延期的担忧,希望加速推进。”
原文里那句"其实我不是怪你们慢,我是怕老板下周问我的时候我没法交代"——没了。
语义在 summary 的那一刀下,从一段有温度的叙事,变成了一张冷冰冰的摘要卡片。
第三层:传输层。
Agent A 完成了一个子任务,需要把结果传给 Agent B。你的代码把结果序列化成了 {"status": "success", "result": "migration completed", "errors": 0}。
但 Agent A 在执行过程中发现了一个"虽然没报错但感觉不太对"的边缘情况。这个直觉没有字段可以装。
语义在 JSON 序列化的那一刻,从一个有直觉、有判断的过程叙事,变成了三个平坦的键值对。
不知道你有没有这种感觉?
这三层,恰好对应了三个解决方案。
02 控制流层:把方向盘还给模型

问题的本质
传统 Agent 架构里,代码是导演,模型是演员。
代码说"现在分析意图",模型就分析意图。代码说"现在调用工具",模型就调用工具。代码说"现在生成回复",模型就生成回复。
模型永远只看到"这一步"的剧本,看不到"整出戏"的脉络。
每一次代码的 await llm.call(),都是一次语义的截断。模型在上一步理解的那些微妙的、模糊的、说不清楚的东西,在函数返回值里全部丢失了。
解决方案:让模型自己开车
2025 年 3 月,OpenAI 把实验性的 Swarm 框架升级为了正式的 Agents SDK。虽然 Swarm 已经废弃,但它留下的核心理念被完整继承了下来:
Conversation as Control Flow — 用对话代替代码来驱动流程。
什么意思?
以前你写的是:
# 旧方式:代码做导演
intent = await classify_intent(user_message)
if intent == "refund":
result = await handle_refund(order_id)
response = await generate_response(result)
现在应该写的是:
# 新方式:模型做导演
refund_agent = Agent(
name="退款专家",
instructions="""你是退款处理专家。
当用户的问题超出退款范围时,
把完整的对话交给通用客服。""",
functions=[process_refund, check_order],
handoffs=[general_support_agent]
)
看出区别了吗?
第一种写法,你在决定什么时候调用什么。模型只是一个被调用的函数。
第二种写法,模型在决定什么时候调用什么。而且更关键的是——当它把任务"交接(Handoff)"给另一个 Agent 时,传递的不是一个 JSON,而是完整的对话历史。
整段对话的语义,一个字都没丢。
这就是"Conversation as Control Flow"的本质:不是模型更强了,而是你不再人为地打断它的思考了。
话说回来,这不是说完全不要代码控制。关键是要分清楚:确定性的操作(查数据库、调 API)交给代码,不确定性的判断(理解意图、决定下一步)交给模型。
📌 落地资源:OpenAI Agents SDK
Swarm 的生产级继任者。核心概念只有三个:Agent、Tool、Handoff。
它最性感的设计是:Agent 之间的交接,传递的是完整的 Messages 数组,而不是结构化的中间状态。
GitHub:
openai/openai-agents-python
03 记忆层:别再切片,开始写日记

问题的本质
现在大多数 Agent 的"记忆",本质上是一个向量数据库。
用户说过的话,被切成 512 个 token 一段的 chunk,算一个 embedding,存进去。下次需要的时候,用余弦相似度捞出来。
这个流程有一个致命的问题:
它只能记住"用户说过什么",记不住"用户是什么样的人"。
你把一个人说的 100 句话切成 100 个片段存进向量库,每个片段都是孤立的。你能检索到"用户提到过他喜欢简洁的设计",但你检索不到"这个用户是一个注重效率、讨厌废话、但又偶尔需要情感确认的技术管理者"。
后者是一段连续的、整合性的理解。它不在任何一个 chunk 里,它在 chunk 和 chunk 之间的关系里。
向量数据库存储了语义的碎片,但丢失了语义的连续性。
解决方案:让 Agent 自己写日记
2026 年 4 月,Mem0 发布了一次重大更新。它做了一件很反直觉的事:
放弃了 UPDATE 和 DELETE 操作。改用 ADD-Only(只追加)的记忆模式。
为什么?因为他们发现,当系统允许"覆盖"旧记忆时,很多微妙的语境信息会被新信息冲掉。
比如,用户三个月前说"我喜欢 React",上周说"我最近在学 Svelte"。旧系统会把"喜欢 React"更新为"喜欢 Svelte"。但真实的语义是:这个用户的技术品味在演化,他可能正在从 React 生态向更轻量的方向迁移。
ADD-Only 模式保留了这个演化轨迹。
更关键的是 Mem0 的三重检索机制:
语义相似度(向量搜索)
×
关键词匹配(BM25)
×
实体关联(Entity Linking)
↓
融合打分 → 最终结果
它不只是"找到最相关的记忆",而是"理解记忆之间的关系"。
当用户说"那个项目"时,系统不仅能找到"项目"相关的记忆碎片,还能通过实体关联追溯到这个项目涉及的人、时间线、用户对它的情感态度。
这就是我说的"写日记"——不是把信息存成表格,而是让 Agent 像写日记一样,记录带有时间、情感和因果关系的连续叙事。
Mem0 在 LoCoMo 基准测试上的得分从 71.4 跳到了 91.6,LongMemEval 从 67.8 跳到了 94.8。这不是小数点后的微调,这是质变。
📌 落地资源:Mem0 — The Memory Layer for AI
不是又一个 RAG 封装。它是一个独立的记忆架构组件,支持 User、Session、Agent 三个作用域。
最新的 ADD-Only 算法 + 多信号融合检索,平均每次检索只消耗 7000 tokens,远低于全量上下文方案的 25000+。
GitHub:
mem0ai/mem0
另一个值得关注的项目是 Letta。
Letta 的思路更极端——它直接借鉴了操作系统的内存管理模型,给 Agent 设计了一个分层的记忆体系:
- 核心记忆(Core Memory):始终驻留在上下文窗口里的关键信息,类似 CPU 寄存器
- 归档记忆(Archival Memory):大容量的长期存储,类似硬盘
- 回忆记忆(Recall Memory):对话历史的检索层,类似内存
Agent 自己决定什么时候把信息从"寄存器"搬到"硬盘",什么时候从"硬盘"调回"内存"。
这就是把"记忆管理"从开发者的手里,交到了模型自己手里。
📌 落地资源:Letta(前身 MemGPT)
OS 启发的分层记忆架构。Agent 自主管理自己的记忆生命周期。
GitHub:
letta-ai/letta
04 传输层:别翻译,直接递原文

问题的本质
Agent 和外部世界之间,隔着一层"翻译"。
你的 Agent 想读一个文件,代码会先把文件内容读出来,提取关键信息,组装成一段描述,再塞进 prompt。
你的 Agent 想查数据库,代码会先执行 SQL,把结果转成 JSON,过滤掉"不必要"的字段,再传给模型。
每一次"翻译",都是一次语义的有损压缩。
而且更要命的是——开发者永远不知道模型真正需要什么信息。
你觉得"不必要"的字段,可能恰恰是模型做出正确判断的关键线索。你觉得"太长了"的原始日志,可能包含着一个只有模型才能识别出来的异常模式。
解决方案:MCP — 让模型直接"看"到世界
Model Context Protocol(MCP)是 Anthropic 在 2024 年底推出的开源协议。到 2026 年 3 月,它的 SDK 月下载量已经超过 9700 万次。
MCP 做的事情很简单:
给模型提供一个标准化的接口,让它直接访问外部系统的原始数据——而不是经过开发者预处理的数据。
Anthropic 自己管它叫"AI 的 USB-C 接口"。
以前的做法:
用户提问 → 你的代码读取文件 → 你的代码提取关键信息
→ 你的代码组装 prompt → 模型看到你筛选过的信息
MCP 的做法:
用户提问 → 模型通过 MCP 直接读取文件
→ 模型看到原始信息 → 模型自己判断什么重要
中间那层"人工翻译",消失了。
这意味着模型获得的不再是开发者认为重要的信息,而是原始的、完整的、未经裁剪的上下文。
而且 MCP 不只是连接工具。它正在演化成一个上下文工程(Context Engineering)的基础设施——
Anthropic 最近在反复强调一个观点:
“Prompt Engineering 的时代已经过去了。我们进入的是 Context Engineering 的时代。”
什么是 Context Engineering?
就是不再只关注"你对模型说了什么",而是系统性地设计**“模型在思考的时候,能看到什么”**。
这包括:
- 系统指令(你是谁,你的规则是什么)
- 工具描述(你能做什么)
- 外部数据(你能看到什么)
- 对话历史(你之前经历了什么)
- 精炼知识(你应该记住什么)
MCP 负责的,就是让这五层信息以标准化、无损的方式,流入模型的上下文窗口。
📌 落地资源:Model Context Protocol (MCP)
Anthropic 开源的连接协议。已被 Claude、Cursor、VS Code 等主流工具原生支持。
不是一个框架,而是一个协议——它定义的是"模型和世界之间应该怎么对话"。
官方文档:modelcontextprotocol.io
05 四条可以立刻用在代码里的原则
框架和项目说完了。但我知道,很多人看完还是会问:
“我现在没有精力重构整个架构,有没有什么小改动,今天就能生效的?”
有。四条原则,从小到大。
原则一:延迟结构化(Late Structuring)
旧做法: 在流程的第一步就让模型输出 JSON。后续所有节点都只操作这个 JSON。
新做法: 让模型在中间过程里用自然语言思考。只在最后一步——需要入库、调 API、返回前端的那一刻——才强制输出结构化数据。
OpenAI 的 Agents SDK 已经原生支持这个模式:中间步骤自由推理,只在最终输出时通过 output_type 参数强制 schema 校验。
在一切可以保持自然语言的地方,绝不使用 JSON。
这一条,可能是信噪比最高的改动。
原则二:传草稿,不传结论
当 Agent 调用工具失败时,不要只传 {"error": "timeout"}。
把工具的原始报错信息、上一步的完整意图、甚至这是"第几次重试"的上下文,原封不动地拼接进对话历史。
模型非常擅长从"不完美"的上下文中恢复语义。但前提是你没有帮它做 summary。
你越替它总结,它就越"蠢"。
你越给它原始素材,它就越"聪明"。
这听起来反直觉,但真正用过 Coding Agent 长时间工作的人应该深有体会。
原则三:用 System Prompt 代替 If-Else
不要在代码里写:
if user_emotion == "angry":
response_style = "empathetic"
在 System Prompt 里写:
在回复之前,先在 <thought> 标签内分析用户话语中的
隐性情绪和真实诉求。然后根据你的分析,自然地调整
你的语气和回应方式。
前者把"理解情绪"变成了一个离散的分类任务。
后者把"理解情绪"留在了模型连续推理的过程里。
语义的连续性,就是在这些微小的设计选择中被保护或者被摧毁的。
原则四:给 Agent 一个"锚"
在长对话或多步任务中,每隔几轮,让模型自己用自然语言重述一遍:“我现在在做什么,我为什么在做这个,接下来我打算做什么。”
这不是给人看的 log。
这是给模型自己的"锚点"——防止它在漫长的上下文里漂流太远。
业内管这叫思维链锚点(CoT Anchor)。
简单到只需要在 System Prompt 末尾加一句话:
每完成一个阶段性步骤后,先用一句话总结当前进展和下一步计划,
再继续执行。
但效果是显著的。尤其在超过 20 个 tool call 的长链路任务里,它能把 Agent 的"目标漂移率"降低一半以上。
06 一个完整的落地清单
如果你正在做 Agent 产品,下面是一份你可以对照检查的清单:
控制流层 ✅
| 检查项 | 传统做法 | 语义连续做法 |
|---|---|---|
| 意图路由 | 代码 if-else 分发 | 模型自主决策 + Handoff |
| 多步任务 | 代码编排 Workflow | 模型内部推理链 |
| 错误处理 | 代码捕获 + 重试 | 原始错误传入上下文 |
| 子任务协作 | JSON 接口传参 | 完整对话历史传递 |
记忆层 ✅
| 检查项 | 传统做法 | 语义连续做法 |
|---|---|---|
| 历史存储 | 向量切片 | 分层记忆(短期/长期/情景) |
| 信息更新 | 覆盖旧记忆 | ADD-Only 追加 |
| 检索方式 | 单一向量搜索 | 多信号融合(向量+关键词+实体) |
| 记忆管理 | 开发者手动设计 | Agent 自主管理 |
传输层 ✅
| 检查项 | 传统做法 | 语义连续做法 |
|---|---|---|
| 外部数据接入 | 代码预处理后传入 | MCP 直连原始数据 |
| 工具描述 | 硬编码 schema | 按需动态发现 |
| 上下文管理 | 被动堆积 | 主动裁剪 + 分层注入 |
07 写在最后
其实回头看,这篇文章想说的核心就一件事:
语义连续性不是一种玄学,而是一种工程选择。
每一个 if-else,每一次 JSON.stringify(),每一刀 summary——你都在做一个选择:是保留语义的完整性,还是为了工程的确定性而牺牲它。
以前我们没得选。模型太弱,上下文窗口太小,你不切就装不下。
但 2026 年了。上下文窗口动辄 100K、200K。模型的推理能力已经足够强。
是时候把那些"不得已的妥协"还回去了。
不瞒你说,我自己重构 Agent 项目的时候,最大的阻力不是技术——而是心理。
你会本能地想控制一切。你会觉得"不用 if-else 我怎么保证流程正确"。你会担心"不做 summary 上下文会不会爆掉"。
但当你真正放手,让模型在一段完整的、未被切割的语义里自由游泳的时候——
你会发现它回复的质量,突然就不一样了。
不是更"正确"。
是更"懂"了。
那种感觉,就像你跟一个人聊天,他第一次真正听懂了你在说什么。
不是因为他更聪明了。
而是因为——你终于没再打断他。
精选金句:
- 语义在进入 if-else 的那一瞬间,就从一段连续的理解,变成了一个离散的标签
- 向量数据库存储了语义的碎片,但丢失了语义的连续性
- 你越替它总结,它就越"蠢"。你越给它原始素材,它就越"聪明"
- 在一切可以保持自然语言的地方,绝不使用 JSON
- 语义连续性不是一种玄学,而是一种工程选择
本文提及的项目与资源汇总:
| 资源 | 解决什么 | 链接 |
|---|---|---|
| OpenAI Agents SDK | 控制流层:对话即控制流 | github.com/openai/openai-agents-python |
| Mem0 | 记忆层:多信号融合记忆 | github.com/mem0ai/mem0 |
| Letta | 记忆层:OS 式分层记忆 | github.com/letta-ai/letta |
| MCP | 传输层:模型直连外部数据 | modelcontextprotocol.io |
| Anthropic Context Engineering | 方法论:上下文工程指南 | anthropic.com |
上一篇:[我终于意识到:AI 产品真正的单位不是 Function,而是语义连续性]
如果这篇对你有用,转发给你团队里正在做 Agent 的工程师。他可能正在某个 if-else 里,把语义切碎。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)