news 2026/9/29 18:26:31

Harness 上下文压缩实战:为 Claude Code Agent 配置可复现的压缩策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Harness 上下文压缩实战:为 Claude Code Agent 配置可复现的压缩策略

1. 长会话里 Claude Code 为什么会“失忆”:Harness 上下文压缩要解决的真实问题

如果你用 Claude Code 跑过稍微长一点的任务,比如让它连续改十几个文件、反复跑测试、来回修 bug,大概率遇到过这种情况:前面几轮它还清楚记得“我们正在把UserService从回调改成 async/await”,跑到第二十轮突然开始重新问你“你想改哪个文件”,或者干脆把已经改好的接口又改回旧写法。这不是模型变笨了,而是上下文窗口被工具输出、代码片段和历史对话塞满了,早期的关键信息被挤出了有效注意力范围。

Claude Code 这类 Agent 的上下文消耗速度和普通聊天完全不是一个量级。一次Bash命令可能吐出几千行日志,一次Read可能带回整个文件,一次Grep可能命中上百条结果。这些内容单看都有用,但累积起来会迅速吃掉 token 配额。我实测过一个中等规模的重构任务,二十多轮之后上下文就逼近 150K token,响应明显变慢,而且模型开始丢细节。

Harness 的上下文压缩就是针对这个瓶颈设计的。它不是一个单点功能,而是一套分层策略:规则驱动的微压缩负责低成本清理,会话记忆压缩负责复用已有摘要,完全 LLM 压缩负责高精度兜底。你可以把它理解成一个“流量调节阀”——不是等上下文爆了才处理,而是在不同压力档位用不同成本的手段逐步释放空间。

这篇文章面向的是已经在本地跑 Claude Code Agent、并且希望把上下文管理做成可复现、可观测流程的开发者。我会从触发阈值、摘要粒度、保留窗口三个维度逐项拆解,给出可以直接复制的配置片段,并且用压缩前后的 token 对比来验证效果。整套流程在本地就能复现,不需要改 Claude Code 源码。

先说清楚一个前提:Harness 的压缩策略是围绕AgentState.contextMutable()这个可变上下文区来操作的。所有压缩动作最终都是把[summary] + [recent tail]写回这个区域,原始消息则落到永不压缩的会话日志里。理解这一点,后面配置和排障都会顺很多。

2. TaoToken 前置准备:给 Harness 压缩链路配好模型入口

Harness 的三层压缩里,第一层微压缩是纯规则、不调模型的,但第二层会话记忆压缩和第三层完全 LLM 压缩都需要一个稳定的模型入口。尤其是第三层,它要调用 LLM 生成结构化摘要,如果这个入口不稳定,压缩本身就会失败,反而把 Agent 卡死。所以先把模型接入配好,是整条链路能跑通的前提。

我这边用的是 TaoToken 作为模型入口。它的 API 地址是https://taotoken.net/api,兼容 OpenAI 风格的调用方式,Harness 里配置base_url和api_key就能接上。官网在https://taotoken.net/?utm_source=taotoken_aicg_blog_end,注册后在控制台生成 Key 即可。

具体操作步骤:

第一步,打开控制台https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite,创建一个 API Key。建议给这个 Key 起个明确的名字,比如harness-compaction,方便后面区分是压缩链路在用还是主对话在用。

第二步,在 API Keys 页面https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite复制刚生成的 Key。注意 Key 只在创建时完整显示一次,复制后妥善保存。

第三步,确认你要用的模型 ID。Harness 压缩摘要对模型的要求是“指令遵循强、输出稳定”,不需要最强的推理模型,但也不能用太弱的,否则摘要会丢关键信息。我一般用 Claude 系列做摘要,因为它在长文本压缩上对“保留标识符原文”这类约束执行得比较稳。

这里有个容易踩的坑:很多人把主对话的模型和压缩用的模型配成同一个,结果压缩时消耗的 token 和主任务抢配额。建议压缩链路单独配一个模型入口,哪怕用同一个 Key,也在配置里区分开,方便后面观测压缩成本。

配好之后,你可以先用模型对话页面https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite手动测一下,发一段长文本让它摘要,确认返回格式符合预期。这一步别跳过,因为 Harness 的完全压缩对摘要格式有要求,提前验证能省很多排障时间。

如果你打算长期跑 Agent 任务,压缩调用会持续发生,建议直接上 Coding Planhttps://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite,配额更稳,不会因为压缩调用把主任务的额度挤掉。

3. 可复制的 Harness 压缩配置:触发阈值、摘要粒度与保留窗口

这一节是核心。Harness 的压缩配置分散在几个地方:Agent 构造时的.compaction(...)、中间件的阈值参数、以及摘要 Prompt 的约束。我把它整理成一份可以直接抄的配置,路径和字段名保持和实际一致。

先看 Agent 构造部分。Harness 的压缩能力是通过.compaction(...)开启的,不开的话,上下文溢出时recoverFromOverflow()不会生效,错误会原样抛回上层。配置片段如下:

{ "agent": { "id": "claude-code-agent", "model": "claude-sonnet", "base_url": "https://taotoken.net/api", "api_key": "sk-your-taotoken-key", "compaction": { "enabled": true, "triggerMessages": 40, "triggerTokens": 100000, "keepRecentMessages": 8, "summaryModel": "claude-sonnet", "summaryMaxTokens": 4000, "toolResultEviction": { "enabled": true, "maxChars": 80000, "keepHeadChars": 2000, "keepTailChars": 2000, "excludeTools": [ "read_file", "write_file", "edit_file", "grep_files", "glob_files", "list_files", "memory_search", "session_search" ] } } } }

逐项解释关键参数。triggerMessages: 40表示消息条数达到 40 条时触发摘要压缩;triggerTokens: 100000表示估算 token 达到 10 万时触发。两个条件是“或”的关系,谁先到谁触发。keepRecentMessages: 8是保留窗口,压缩后尾部 8 条最近消息原文不动,这是保证“近因效应”的关键,别设太小,否则模型会丢当前任务上下文。

toolResultEviction是大工具结果卸载,和摘要压缩独立。maxChars: 80000对应约 20K token,超过这个长度的工具结果会被写到工作区目录,上下文里只留首尾各 2000 字符加一个read_file路径提示。excludeTools里排除的都是自带分页或返回值很小的工具,Shell execute故意不排除,因为命令输出可能非常大。

再看摘要 Prompt 的约束。Harness 在生成摘要时会注入一段强约束,防止关键信息丢失。你可以把这段作为自定义 Prompt 模板:

[compaction.summary_prompt] system = """ 你是一个专业的对话历史压缩专家。你的任务是将冗长的对话历史浓缩为一段精炼的摘要,以便后续模型能够无缝接续当前任务。 在执行压缩时,你必须严格遵守以下约束: 1. 核心信息保留: - 当前活跃的任务:准确记录用户当前正在执行的核心任务及进度。 - 重要决策与结论:保留对话中达成的关键共识、架构决策或最终结论。 - 待办事项(TODO):完整提取并列出所有尚未完成的待办事项。 - 做出的承诺:保留模型或用户做出的明确承诺。 2. 标识符绝对保真: - 所有不透明标识符(如 UUID、哈希值、Token、API Key 等)必须逐字原文保留。 - 严禁对这类标识符进行任何修改、缩写、推测或重新生成。 3. 内容边界限制: - 仅基于提供的对话历史进行总结,不要引入外部知识。 - 保持摘要的客观性,不要遗漏关键的上下文转折。 """

这段 Prompt 里有两个容易被忽视但很重要的点。一是“标识符绝对保真”,Harness 压缩时如果模型把 UUID 或哈希值改写了,后续read_file或session_search就会找不到对应内容。二是“仅基于提供的对话历史”,防止模型在摘要时引入外部知识产生幻觉。

还有一个细节:Harness 在把内容送入摘要模型之前,会先调用stripToolResultDetails()移除工具输出里的details字段。这是因为工具结果里可能包含冗长的元数据,不适合直接进摘要模型。这个动作是自动的,不需要你配置,但排障时要知道它存在,否则会疑惑为什么摘要里看不到某些字段。

如果你用的是 Claude Code 的 settings 文件,压缩相关配置可以放在~/.claude/settings.json里,字段名和上面 JSON 一致。Cline MCP 场景下,则是在 MCP server 配置里加compaction段。Codex 的auth.json只负责认证,压缩配置走单独的 agent 配置文件,别混在一起。

4. 验证压缩效果:压缩前后 token 对比与成功结果确认

配好之后必须验证,否则你不知道压缩到底有没有生效、丢了多少信息。Harness 提供了几个可观测点,我按从易到难的顺序说。

最直接的是看压缩日志。Harness 在每次压缩后会记录压缩前后的 token 估算值。你可以在 Agent 运行目录下找compaction.log,里面会有类似这样的记录:

[compaction] trigger=token threshold=100000 before=118432 after=41200 kept_messages=8 summary_tokens=1840 evicted_tools=3

这行日志告诉你:触发原因是 token 阈值,压缩前 118432 token,压缩后 41200 token,保留了 8 条最近消息,摘要本身 1840 token,卸载了 3 个大工具结果。压缩比大约 2.87:1,效果很明显。

如果没有日志文件,可以用 Harness 的session_history工具手动查。这个工具读的是永不压缩的会话日志<workspace>/agents/<agentId>/sessions/<sessionId>.log.jsonl,所以即使上下文已经压成摘要,你依然能查到原始消息。调用方式:

session_history agentId="claude-code-agent" sessionId="sess-20250101-001" lastN=20

对比压缩前后的消息,重点看三件事:当前活跃任务有没有丢、关键标识符有没有被改写、TODO 列表是否完整。我一般会故意在对话里埋一个 UUID 和一个待办事项,压缩后检查它们是否原文保留。

第二个验证手段是主动触发一次溢出恢复。你可以构造一个超长上下文,让模型返回context_length_exceeded错误,然后观察recoverFromOverflow()是否自动走了一次triggerMessages=1的极端压缩并重试。这个兜底链路只要.compaction(...)开了就自动生效,不需要额外配置。验证时注意看日志里有没有recoverFromOverflow字样。

第三个手段是 token 对比脚本。Harness 的 token 估算用的是近似算法,你可以用同样的算法在压缩前后各跑一次,得到精确对比。下面是一个简单的 Python 验证片段:

import json def estimate_tokens(text): # 近似估算:英文约 4 字符/token,中文约 1.5 字符/token return len(text) // 3 with open("compaction.log") as f: for line in f: if "before=" in line: parts = dict(p.split("=") for p in line.split() if "=" in p) before = int(parts["before"]) after = int(parts["after"]) ratio = before / after print(f"压缩前 {before} -> 压缩后 {after}, 压缩比 {ratio:.2f}:1")

跑出来如果压缩比在 2:1 到 4:1 之间,说明配置合理。低于 2:1 可能是保留窗口设太大,高于 5:1 则要警惕信息丢失。

成功结果的判断标准有三条:一是压缩后 Agent 能继续当前任务,不需要你重新交代背景;二是关键标识符和 TODO 在摘要里能找到;三是响应延迟明显下降。三条都满足,说明压缩链路是健康的。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 报错对照

压缩链路跑起来之后,报错基本集中在模型入口和配置格式上。我把几个高频错误和对应排查方法列出来,都是实际踩过的。

401 Unauthorized:最常见。先检查api_key有没有复制完整,TaoToken 的 Key 是sk-开头的一长串,中间不能有空格。再检查base_url是不是https://taotoken.net/api,注意结尾不要多加/v1,Harness 会自己拼路径。如果 Key 和 URL 都对还是 401,去控制台确认这个 Key 有没有被禁用或额度耗尽。

local proxy failed:这个报错通常出现在你本地配了代理但代理没起来,或者代理配置和 Harness 的请求路径冲突。Harness 的压缩调用走的是标准 HTTP,不需要额外代理层。排查方法是先确认环境变量里没有残留的HTTP_PROXY/HTTPS_PROXY,如果有就清掉再试。另外检查base_url有没有被本地 hosts 或 DNS 劫持。

reading choices 报错:这个一般出现在摘要模型返回格式不符合预期时。Harness 期望模型返回结构化的summary字段,如果模型返回了纯文本或者格式错乱,解析就会失败。排查两步:一是确认summaryModel用的是指令遵循强的模型;二是检查摘要 Prompt 里有没有明确要求输出格式。如果模型在压缩时试图调用工具,也会导致reading choices异常,这时候要确认 Prompt 头部的NO_TOOLS_PREAMBLE约束有没有生效。

OAuth 相关报错:如果你用的是 Claude Code 官方客户端,它可能走 OAuth 认证而不是 API Key。这种情况下 Harness 的压缩配置要单独指定auth_type: "api_key",否则会拿 OAuth token 去请求 API 入口,导致认证失败。Codex 的auth.json里如果同时有 OAuth 和 API Key 字段,要明确指定用哪个。

还有一个隐蔽的坑:toolResultEviction的excludeTools列表如果写错了工具名,比如把read_file写成readFile,那个工具的结果就不会被排除,可能被误卸载,导致 Agent 后续读不到文件内容。排查时对照 Harness 的工具注册名逐个核对。

最后提醒一点:压缩配置改完之后要重启 Agent 才生效,热更新不支持。我一开始改完配置没重启,排查了半天以为配置没写对。

6. 把压缩策略沉淀成可复现流程:从配置到观测的闭环

走到这里,你已经有了完整的配置、验证手段和排障对照表。最后说怎么把这套东西沉淀成团队可复用的流程。

核心思路是把压缩配置和 Agent 配置分离。Agent 配置管模型、工具、权限,压缩配置单独一个文件,通过.compaction(configPath)加载。这样不同任务可以用不同压缩策略,比如短任务用激进压缩,长任务用保守压缩。

观测方面,建议把compaction.log接入你的日志系统,按trigger类型分类统计。如果发现recoverFromOverflow频繁触发,说明前面的阈值设得太松,要调低triggerTokens。如果evicted_tools数量一直很高,说明工具输出太大,可以考虑在工具层做分页。

如果你还在用 Claude Code 的默认压缩行为,建议至少把keepRecentMessages和triggerTokens显式配出来,默认值不一定适合你的任务规模。配好之后跑一个长任务,对比压缩前后的 token 和任务完成质量,你会对“上下文管理”这件事有完全不一样的理解。

需要长期跑 Agent 任务的,Coding Planhttps://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite的配额更适合压缩调用频繁的场景。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,里面有完整的 API 参数说明。Claude Code 相关的接入细节可以参考https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite。

整套流程跑通之后,你会发现上下文压缩不是“省 token”这么简单,它直接决定了 Agent 能不能稳定执行长程任务。把阈值、粒度、保留窗口这三件事调对,Agent 的“失忆”问题基本就解决了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 18:26:27

K8S节点磁盘写满引发502:原理、排查与处置全解析

先扔个场景&#xff1a;大白天线上突然冒出来一片 502&#xff0c;刷新几次又偶尔能通&#xff0c;再刷新又挂了。你第一反应是不是直接翻 Ingress 日志&#xff1f;我以前也这样&#xff0c;后来被现实教育过几次&#xff0c;发现很多 502 根本不是网关的问题&#xff0c;真正…

作者头像 李华
网站建设 2026/9/29 18:26:13

UE5 Slate与UMG底层机制解析:Widget生命周期与渲染管线

1. 为什么UE5的UMG/Slate不是“另一个Vue”——从热词误判切入的真实定位最近在几个技术社区里反复看到一句高频吐槽&#xff1a;“vue3引入所有的ui框架都不生效”&#xff0c;紧接着就有人把这句话生搬硬套到Unreal Engine上&#xff0c;发帖问“UMG是不是也像Vue3一样突然不…

作者头像 李华
网站建设 2026/9/29 18:26:13

大模型重构货运广告链路:货拉拉营销文案生成与智能投放实践

我刚接手“大模型在货拉拉营销广告的应用实践”这个项目时&#xff0c;心里其实没底。货拉拉的营销场景和传统电商完全不一样&#xff1a;用户不是“逛”出来的&#xff0c;而是被“要搬家、要拉货、要发急件”这种确定性需求推过来的。广告物料既要打动货车司机&#xff0c;又…

作者头像 李华
网站建设 2026/9/29 18:25:56

C#宿舍管理系统开发实战:表结构设计、WinForms实现与避坑指南

简介&#xff1a;一份面向C#课程设计场景的宿舍管理系统完整源码包&#xff0c;以Visual Studio项目为主体&#xff0c;配套文档、流程图与SQL数据库脚本&#xff0c;适用于需要完成同类课程设计或进行WinForm开发练习的初学者。系统按学生与宿管双角色设计&#xff0c;覆盖公告…

作者头像 李华
网站建设 2026/9/29 18:25:28

拟南芥根尖scATAC-seq实操指南:从染色质可及性到细胞类型注释

1. 这不是“高通量测序入门课”&#xff0c;而是一份根尖细胞核里真实发生的染色质松动地图 scATAC-seq——单细胞染色质可及性测序&#xff0c;这个词听起来像实验室黑板上的一行公式&#xff0c;但落到拟南芥根尖上&#xff0c;它讲的是一个活生生的生物学故事&#xff1a;当…

作者头像 李华
网站建设 2026/9/29 18:25:13

AgentScope实战指南:核心机制、Java 2.0与RAG服务化

1. 为什么我要把AgentScope放进推荐清单最近在选多智能体框架&#xff0c;前前后后对比了LangChain、CrewAI、AutoGen&#xff0c;还有微软的Semantic Kernel&#xff0c;最后让我停下脚步的是AgentScope。先说结论&#xff1a;如果团队里有人问你"多智能体项目该用什么框…

作者头像 李华