线上 Agent 一跑久了就会遇到同一个坑:上下文越来越长,延迟飙升、费用失控,最后还更容易答偏。
这不是模型“变笨”了,通常是上下文治理没做:该留的没留、该删的没删、该摘要的摘要坏了。
症状先对齐(你是不是也这样)
- p95 延迟随会话轮次持续上升
- 单会话 token 成本后期指数增长
- 同类问题回答不稳定,幻觉上升
- 高峰期 429/超时明显增加
一套能落地的治理策略(先做这 3 层)
1) 硬截断:窗口上限 + 重要消息白名单
核心原则:永远给模型一个可控上限。
- 设定
max_context_tokens(比如 24k) - 系统提示词、策略约束、用户画像放白名单,永不丢
- 普通历史消息按“最近优先”滑窗保留
Go 伪代码:
type Msg struct {
Role string
Content string
Important bool
Tokens int
}
func TrimByBudget(msgs []Msg, budget int) []Msg {
keep := make([]Msg, 0, len(msgs))
used := 0
// 1) 先保留重要消息
for _, m := range msgs {
if m.Important {
keep = append(keep, m)
used += m.Tokens
}
}
// 2) 再从后往前补最近消息
for i := len(msgs)-1; i >= 0; i-- {
m := msgs[i]
if m.Important { continue }
if used+m.Tokens > budget { continue }
keep = append([]Msg{m}, keep...)
used += m.Tokens
}
return keep
}
2) 摘要回填:把“远历史”压缩成结构化记忆
不要用一段散文摘要,后续会越来越失真。建议固定结构:
- 已确认事实(Facts)
- 未解决问题(Open Issues)
- 用户偏好(Preferences)
- 决策与原因(Decisions + Why)
这样下一轮可做增量更新,不用每次全量重算。
3) 成本上限:会话级预算 + 降级开关
给每个会话加预算(比如 $0.5/session):
- 超 70%:降低
max_output_tokens - 超 90%:禁用高成本工具调用
- 超 100%:只保留“摘要 + 最新问题”,进入降级模式
Responses API 在 Go 里的实践建议
- 把“历史构建器”做成独立模块,别散落在 handler
- 每次请求都打点:
input_tokens/output_tokens/latency/cost - 给摘要器单独版本号(
summary_schema=v2),方便灰度 - 工具调用结果不要原样全塞回上下文,先压缩成结论
故障排查清单(最容易漏)
- 重复拼接历史:中间件和业务层都 append 一次
- 工具输出过长:日志/HTML 原文直接回灌
- 摘要回填失真:把猜测当事实写入 Facts
- 预算只看 token 不看价格:不同模型单价差异很大
最小可用配置(建议起步值)
max_context_tokens: 24ksummary_trigger_tokens: 16kmax_output_tokens: 1ksession_cost_cap_usd: 0.5tool_output_hard_cap_chars: 4000
总结
上下文爆炸本质是工程问题,不是玄学调参。
先把截断、摘要、预算三件事做扎实,通常就能把延迟和成本压回可控区间,再谈效果优化才有意义。