Work Buddy 长会话翻车实录:Context 塞爆后模型竟编造用户需求——我的三层记忆压缩方案 1. 从一次「AI 擅自改地址」说起Context 塞爆后模型为什么会编需求Work Buddy 这类长会话智能体最危险的不是答错而是它开始「替你说话」。我遇到过一次典型翻车一个处理退货的会话跑到 80 多轮上下文逼近 128k 阈值模型突然生成了一句用户从没说过的话——「请改为纽约仓优先处理」然后自己确认「好的已更新」。整个过程置信度显示 87%没有任何告警。事后复盘根因不是模型变笨而是 Context 膨胀后历史消息里真实指令和模型早期生成的推测混在一起模型分不清哪句是用户说的、哪句是自己编的。这就是长会话的核心矛盾你希望它记住一切但塞得越满关键信息召回率反而越低。实测数据很直观连续工作 4 小时后关键信息召回率会从 92% 掉到 47%数字类字段价格、单号、地址错误率升到 15% 左右。模型不是忘了是被噪声淹没了于是用「合理推测」去补全缺失表现出来就是编造用户需求。适合谁看正在用 Work Buddy 或类似 Agent 做多轮业务客服、订单、工单的开发者已经被 Context 膨胀、幻觉指令坑过的人想用一套可落地配置把记忆管起来的人。下面我把自己在用的三层记忆压缩方案拆开配置可以直接复制验证动作也能本地复现。2. 前置准备用 TaoToken 统一模型入口别让压缩链路各连各的三层压缩要跑起来会同时用到摘要模型、评分模型和主对话模型。如果每个模型各接一个供应商Key 管理、计费、限流全是坑。我的做法是用 TaoToken 做统一入口一个 Key 走所有模型调用压缩链路里的 summarizer、ranker、main 都指向同一个 base_url排障时日志也好对齐。TaoToken 在这里的角色是模型 API 聚合网关兼容 OpenAI 风格的接口协议所以 config.toml 和 settings.json 里只需要改 base_url 和 model 字段不用动业务代码。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM直接填进配置。先去控制台建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 然后在 API Keys 页面生成https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 字段含义对不上时翻这个。注意Key 只放环境变量别写进 config.toml 提交到仓库。我习惯用TAOTOKEN_API_KEY这个变量名下面配置里直接引用。3. 三层记忆压缩的 config.toml 骨架三层分别是第一层业务字段白名单过滤第二层重要性评分第三层 Top-K 保留。核心思路是压缩时不是无脑截断而是按业务权重决定谁留下。下面是我在用的 config.toml 骨架字段名按你项目实际调整结构可以直接抄。[memory] # 触发压缩的上下文占用比例超过就启动三层压缩 compress_trigger_ratio 0.75 # 压缩后目标占用比例留出余量给后续轮次 compress_target_ratio 0.35 # 每轮对话后检查一次 check_interval_turns 3 [memory.layer1_whitelist] # 第一层业务字段白名单这些字段永不丢弃 focus_fields [订单号, 仓库, 金额阈值, 收货地址, 物流方式] # 强制压缩率0.3 表示摘要后保留约 30% 原文信息量 drop_ratio 0.3 [memory.layer2_ranker] # 第二层重要性评分权重分数越高越优先保留 weight_price 9.5 weight_address 8.0 weight_shipping 6.5 weight_general 3.0 [memory.layer3_topk] # 第三层保留评分最高的 K 条 keep_top_k 20 # 锚点内容不计入 K单独锁定 anchor_exempt true [memory.anchor] # 关键信息锁定标记带标记内容禁止模型修改 price_pattern !!price${value}!! address_pattern !!address{value}!! shipping_pattern !!shipping#{value}!! # 锚点刷新周期分钟 refresh_minutes 30 [llm] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY summarizer_model qwen-plus ranker_model claude-3-5-sonnet main_model claude-3-5-sonnet这里有个设计取舍summarizer 用便宜模型做初筛ranker 用判断力强的模型做评分main 用主对话模型。三层各司其职成本比全用大模型低不少。压缩触发比例设 0.75 而不是 0.9是因为等到 0.9 再压留给压缩本身的空间已经不够容易压出信息错位。4. settings.json 配置片段与压缩逻辑接线config.toml 是策略settings.json 是运行时接线。下面这段负责把三层压缩挂到会话生命周期上重点是压缩前后各做一次锚点校验防止压缩过程把锁定字段弄丢。{ workbuddy: { memory_pipeline: { enabled: true, stages: [ { name: layer1_whitelist, type: field_filter, config_ref: memory.layer1_whitelist, on_error: skip_and_log }, { name: layer2_ranker, type: importance_score, config_ref: memory.layer2_ranker, model_ref: llm.ranker_model }, { name: layer3_topk, type: topk_keep, config_ref: memory.layer3_topk } ], pre_compress_hook: verify_anchors, post_compress_hook: verify_anchors, anchor_store: ./runtime/anchors.json }, confidence_circuit_breaker: { enabled: true, cross_check_threshold: 0.95, critical_op_threshold: 0.98, critical_ops: [modify_address, modify_amount, cancel_order], cross_check_model: glm-4 }, context_monitor: { warn_ratio: 0.9, log_every_turns: 5, report_path: ./runtime/context_report.jsonl } } }接线逻辑说明pre_compress_hook和post_compress_hook都指向verify_anchors意思是压缩前先记下所有锚点压缩后再核对一遍发现锚点丢失就回滚这次压缩。confidence_circuit_breaker是第三层保险置信度低于 0.95 触发交叉验证关键操作改地址、改金额、取消订单要求 0.98 以上否则强制人工确认。提示anchor_store和report_path指向的目录要提前建好否则首次运行会因写文件失败中断。我踩过这个坑日志里只报 hook 失败不报目录不存在排查花了半小时。5. 验证请求压缩前后 Context 占用对比与「不再编需求」的确认动作配置写完必须验证两件事压缩是否真的降了占用以及模型是否还会编造用户需求。下面给一段可复现的验证脚本用 TaoToken 的接口跑输出压缩前后的 token 占用和一次关键指令的召回测试。import os import json import requests API_KEY os.environ[TAOTOKEN_API_KEY] BASE_URL https://taotoken.net/api HEADERS { Authorization: fBearer {API_KEY}, Content-Type: application/json } def count_tokens(messages, modelclaude-3-5-sonnet): # 用一次极短请求拿 usage近似统计占用 payload { model: model, messages: messages, max_tokens: 1 } resp requests.post(f{BASE_URL}/v1/chat/completions, headersHEADERS, jsonpayload, timeout30) return resp.json()[usage][prompt_tokens] def build_long_session(turns80): # 构造 80 轮长会话第 5 轮埋入关键指令 msgs [{role: system, content: 你是订单处理助手。}] for i in range(turns): if i 5: msgs.append({role: user, content: 重要不要使用 UPS 快递改用海运。}) else: msgs.append({role: user, content: f第{i}轮查询订单状态。}) msgs.append({role: assistant, content: f第{i}轮已处理。}) return msgs if __name__ __main__: session build_long_session(80) before count_tokens(session) print(f压缩前 prompt_tokens: {before}) # 这里调用你的三层压缩函数伪代码示意 # compressed run_memory_pipeline(session) # after count_tokens(compressed) # print(f压缩后 prompt_tokens: {after}) # print(f压缩率: {1 - after / before:.2%})跑通后你会看到压缩前占用接近阈值压缩后落到目标比例附近。更关键的是召回测试压缩后单独发一句「我第 5 轮说过什么物流限制」看模型是否还能答出「不要 UPS用海运」。如果答不出或答成别的说明第一层白名单没覆盖到物流字段回去把weight_shipping调高、把「物流方式」加进focus_fields。验证「不再编需求」的动作构造一个用户从未提过地址变更的会话跑到高占用观察模型是否还会生成「请改为 XX 仓」这类句子。正常情况下锚点锁定 置信度熔断会让它要么不生成要么生成后触发确认。这一步建议在测试环境跑别拿生产会话试。6. 本篇常见错排查压缩后反而更爱编、锚点丢失、置信度误判错误一压缩后模型更爱编需求。多半是第二层评分权重没调好把用户真实指令压掉了模型只能靠推测补全。排查方法打开context_report.jsonl看压缩后保留的消息里还有没有第 5 轮那条关键指令。没有就说明白名单和权重都要调。我试过把weight_general从 3.0 降到 1.5让业务字段权重更突出编造率明显下降。错误二锚点压缩后丢失。检查pre_compress_hook和post_compress_hook是否都配了verify_anchors。只配一个的话压缩过程弄丢锚点不会被发现。另外anchor_exempt true要确认生效否则 Top-K 会把锚点当普通消息裁掉。错误三置信度熔断误判正常操作被拦。critical_op_threshold设 0.98 偏严如果主模型本身置信度波动大会频繁触发人工确认。可以先设 0.96 观察一周统计误拦率再调。交叉验证模型glm-4如果响应慢可以换成更快的模型但别省掉交叉验证这一步。错误四压缩触发太晚已经编完了才压。compress_trigger_ratio设 0.75 是经验值如果你的会话轮次特别密可以降到 0.7。监控warn_ratio设 0.9 是告警线不是压缩线别搞混。排障时如果怀疑是接入层问题先看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 确认 base_url 和鉴权头没写错。模型行为异常想单独验证可以用模型对话页面直接发测试消息https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 比在代码里加日志快。7. 长期跑编码/Agent 会话把压缩链路固定下来三层压缩跑通后真正省心的是把它变成默认链路而不是每次出事才手动压。如果你长期用 Work Buddy 做编码或 Agent 类长会话建议把压缩配置和 Coding Plan 结合让额度覆盖摘要、评分、主对话三类调用避免压缩到一半因为限流中断。Coding Plan 入口https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个我自己的习惯每次改完压缩权重先跑一遍第 5 节的验证脚本确认压缩率和召回都正常再上生产。那次「纽约仓」事故之后我养成了看锚点状态的习惯——不是不信任模型是长会话里记忆管理本来就不该全交给模型自己扛。