上下文反模式
AI Agent 上下文工程中常见的五种反模式:只传当前消息、全量塞入、记忆全放 messages、没有 pending_question、UI 状态与 Agent 状态分裂。
#type / concept
#status / evergreen
#tech / ai
[!info] related notes
- 所属 MOC: Context Engineering MOC
- 正确做法: Context Engineering, Context Builder 模式
- 状态管理: 问诊状态 Schema
- 信息采集: 结构化信息采集
上下文反模式
上下文工程中常见的五种反模式,以及对应的正确做法。
反模式 1:只传当前消息
表现
messages := []service.ChatMessage{
{Role: "user", Content: contentText},
}
每次只传当前用户消息,Python 端每次都像第一次见到用户。
后果
用户:膝盖有下坠感
AI:请问持续多久?有没有疼痛?
用户:一周了,走路会明显
AI:请问你能补充持续多久、有没有疼痛吗?
这不是模型”不聪明”,而是它每次收到的上下文都是断的。
正确做法
加载历史 messages 并传给模型。至少包含当前会话的最近几轮原始消息。详见 Context Builder 模式。
反模式 2:把所有历史无脑塞进去
表现
把所有 messages 全量传给模型,不做任何过滤或压缩。
后果
| 问题 | 说明 |
|---|---|
| token 成本高 | 每轮都传全量历史,token 消耗线性增长 |
| 模型注意力分散 | 无关的早期对话干扰当前判断 |
| 旧信息污染 | 早期的错误信息可能影响新判断 |
| 半截消息污染 | SSE 断流产生的 partial message 进入上下文 |
| 工具结果过长 | 完整的 RAG 检索结果或 tool result 占用大量 token |
| 重复问答 | 模型可能重复问已经回答过的问题 |
正确做法
采用分层策略:
最近 6~10 轮:保留原始 messages
更早内容:进入 conversation_summary
结构化症状:永远单独保留
知识库检索:每轮动态注入 top 3~5
工具结果:只保留摘要,不保留全部原文
详见 上下文窗口预算。
反模式 3:把”记忆”全部放在 messages 里
表现
用户第一轮说”我膝盖有下坠感,一周了,走路明显”,系统完全依赖这句话在 messages 里,不抽取结构化数据。
后果
- 模型每轮都要从自然语言里重新推理”膝盖""下坠感""一周""走路”
- 如果 messages 被 trim 或 summary,这些信息可能丢失或变得模糊
- 代码无法根据结构化字段驱动逻辑(如 missing_slots)
正确做法
把关键信息抽成结构化状态:
{
"body_part": "膝盖",
"symptom": "下坠感",
"duration": "一周",
"trigger": "走路"
}
messages 是证据,structured state 是工作记忆。 详见 问诊状态 Schema。
反模式 4:没有 pending_question
表现
AI 问”是否伴随疼痛或肿胀?“,用户答”没有”,系统不知道”没有”指的是什么。
后果
模型可能把”没有”理解为:
- 没有疼痛
- 没有肿胀
- 两个都没有
- 没有其他症状
下一轮可能重复问同样的问题,或者做出错误推断。
正确做法
状态里维护 last_question 和 pending_answer_for:
{
"last_question": "是否伴随疼痛或肿胀?",
"pending_answer_for": ["pain", "swelling"]
}
用户回答”没有”时,结合 pending_answer_for 解析:
{
"pain": "none",
"swelling": "none"
}
这比纯文本问答稳定很多。详见 结构化信息采集。
反模式 5:UI 状态与 Agent 状态分裂
表现
前端人体 SVG 已经高亮”膝盖”,但模型上下文里没有 body_part: knee。用户看起来在一个问诊流程里,模型实际不知道 UI 发生了什么。
后果
- 用户点击了膝盖,AI 还问”你想咨询哪个部位?”
- 右侧看板显示”膝盖”,AI 回复里提到”肩部”
- 前端和后端的状态不一致,用户体验割裂
正确做法
UI 展示状态 = 后端 consultation_state 的投影
模型上下文 = consultation_state 的精选版本
前端不要自己维护一套不可追踪的”隐式状态”。所有 UI 可见的状态都应该来自后端 consultation_state,或者同步回后端。
总结
| 反模式 | 核心问题 | 正确做法 |
|---|---|---|
| 只传当前消息 | 模型失忆 | 加载历史 messages |
| 全量塞入 | token 浪费、注意力分散 | 分层策略 + 预算控制 |
| 记忆全在 messages | 无法驱动逻辑 | 结构化 state 独立维护 |
| 没有 pending_question | 回答歧义 | 维护 last_question + pending_answer_for |
| UI/Agent 状态分裂 | 前后端不一致 | UI 是 state 的投影 |