扫码打开虎嗅APP
本文来自微信公众号: 硅星人Pro ,作者:董道力
3月31日,Anthropic大概没想到,会以这种方式送给开发者社区一份礼物。
安全研究员Chaofan Shou发现,Claude Code的npm包里藏着一个.map文件——sourcemap,这是给开发者调试用的,按理早该从生产环境剔除。没剔除就意味着:任何人都可以从这个文件里还原出Claude Code完整的源码。
1906个文件,512000行代码,40多个工具,85个斜杠命令。几小时内,代码被镜像到GitHub,获得了数千个Star和Fork。
更讽刺的地方在于,代码里有一个叫"Undercover Mode"的子系统,专门用来防止Anthropic内部代号出现在git commit里,避免信息泄露。他们精心设计了防泄露机制,然后把整个源码打包进了npm。
但这篇文章想聊的不是这个失误,而是这份代码里真正值钱的东西:一个生产级agent harness长什么样。

Agent harness对于整个行业都是在探索的新东西。Anthropic过去一年反复讲一个观点:模型本身只是引擎,harness才是整辆车。他们提出了context engineering、minimal viable tool sets、sub-agent isolation等一系列设计原则。
现在源码摊开了,终于可以看看:他们会不会遵循自己写的技术文档。
答案是,不仅遵循了,还藏了个焚决。
下文对于harness的评价描述,主要基于Anthropic的技术文档。
1
context是要钱的
上下文越长,模型越容易迷失,这个问题业内叫context rot。上下文不是越多越好,每多塞一个token,所有其他token分到的注意力就少一点,重要信息被稀释。
Claude Code的做法是给每一块内容设硬上限,强制控制哪些东西能进窗口、进多少。
Skill列表最多占整个窗口的1%,每条描述不超过250字符:
//verbose whenToUse strings waste turn-1 cache_creation tokens//without improving match rate.exportconstMAX_LISTING_DESC_CHARS=250
为什么这么限?因为Skill列表的作用是让模型知道"有这个工具",不是让模型读懂怎么用它。发现工具靠的是关键词匹配,描述写500字和写50字匹配率没有差别,多出来的全是浪费。
System prompt被切成两半。前半部分是所有用户共用的指令,内容固定,可以缓存,下次调用直接复用。后半部分是这个用户、这个会话特有的内容,每次动态生成。这样每次API调用只需要处理变化的那一半,省掉了大量重复计算。
大的工具调用结果不放上下文,直接写到磁盘,给模型一个文件路径。比如执行了一条命令返回了几千行日志,把这几千行塞进上下文会挤掉其他有用的信息,换成文件引用就只占一行。
生产系统的本质是处理失败
模型接近上下文窗口极限时,会倾向于草草收尾,Anthropic管这叫"context anxiety"。就像一个人知道自己快没时间了,会开始跳步骤、走捷径。解法是彻底清空窗口,把当前进展整理成结构化的交接材料,下一个窗口从交接材料开始,而不是从一个快撑满的上下文开始。
Claude Code把这个做成了三级压缩:先试轻量摘要,不够再自动压缩,还不够等到API报错了再强制压缩。一级解决不了才走下一级。
三级之上还有熔断器:
//BQ 2026-03-10:1,279 sessions had 50+consecutive failures(up to 3,272)//in a single session,wasting~250K API calls/day globally.constMAX_CONSECUTIVE_AUTOCOMPACT_FAILURES=3
有人在BigQuery里跑数据,发现自动压缩连续失败的会话每天浪费了约25万次API调用。压缩失败的原因通常是上下文已经坏掉了,继续压根没有意义。于是加了个规则:连续失败3次就停,不再尝试。
demo只需要跑通。生产系统还要知道失败的时候怎么止损。
记忆不是把所有东西都存起来
跨context window的agent,每次新开一个窗口,之前发生的事情全忘了。Anthropic把这比作轮班工程师,每班的人来了都不知道上一班干了什么,只能从头摸索。
Claude Code用后台子agent定期把对话里的关键信息提出来存好,下次需要的时候再注入进来。但不是什么都存,也不是什么都注入。记忆筛选用Sonnet来做,判断哪些记忆和当前任务相关。
有一个细节:刚用过的工具,它的参考文档不会被推进来:
asyncfunctionselectRelevantMemories(query:string,memories:MemoryHeader[],recentTools:readonlystring[],//过滤掉刚用过的工具文档):Promise
模型刚用过这个工具,上下文里已经有使用记录了,这时候再把文档推进来只是重复信息,占位置没有额外价值。
1
工具的边界就是agent的能力边界
工具太多,模型不知道该用哪个,跟没有工具一样。这是agent设计里公认的坑,工具选择本身会消耗模型的推理能力,选项越多越容易选错。
Claude Code有50多个工具,解法是大多数工具初始时根本不出现在上下文里,模型主动搜索才拿到完整定义:
exportfunctionisDeferredTool(tool:Tool):boolean{if(tool.alwaysLoad===true)returnfalseif(tool.isMcp===true)returntrue//MCP工具默认延迟加载if(tool.name===TOOL_SEARCH_TOOL_NAME)returnfalse//ToolSearch自己永远不延迟}
ToolSearch永远完整加载,因为模型要靠它找别的工具。如果ToolSearch也延迟加载,模型就找不到任何东西了。
每个工具还带着一套属性:能不能并行执行、会不会修改文件系统、结果超过多大写磁盘、用户打断时是停掉还是继续跑完。
这些属性不是给人看的文档,是调度引擎决定怎么执行这个工具的依据。
权限弹窗可以被提前消除
工具调用要过五关才能执行:输入校验、权限逻辑、规则匹配、hook拦截、分类器或用户确认。每一关都可能叫停,但也都需要时间。
其中最慢的是等用户点确认。为了消除这个等待,分类器在弹窗还没弹出来的时候就已经开始跑了:
/***Start a speculative bash allow classifier check early,so it runs in*parallel with pre-tool hooks,deny/ask classifiers,and permission dialog setup.*/exportfunctionstartSpeculativeClassifierCheck(command:string,...):boolean{constpromise=classifyBashCommand(command,...)promise.catch(()=>{})//防止未处理的rejection speculativeChecks.set(command,promise)returntrue}
如果分类器判断"这个命令大概率没问题",弹窗直接跳过。用户感知不到等待,因为判断在弹窗准备期间已经做完了。
1
sub-agent不是套娃,是上下文隔离
sub-agent的价值是用完即弃:花几万token去做一个子任务,做完只把结论交给主agent,中间过程全扔掉。主agent的上下文里只有结论,不是整个过程。
有四种执行模式:同步(主agent等子agent跑完)、异步后台(子agent在后台跑,完成了通知用户)、Worktree文件系统隔离(子agent在一个独立的git副本里改代码,不影响主目录,改完了再合并)、跨机器隔离(完全在另一台机器上跑)。风险越高,隔离越彻底。
sub-agent用的工具是过滤过的,不能递归调用自己,防止无限套娃。
//只清理新创建的客户端,不清理共享的constcleanup=async()=>{for(constclientofnewlyCreatedClients){awaitclient.cleanup()}}
子agent自己建的连接,退出时自己关。复用父agent已有的连接,不能关,因为父agent还在用。
1
hook系统让harness变成了平台
Claude Code开放了27个事件节点,在agent运行的任何关键时刻,用户都可以插进来做点什么:
exportconstHOOK_EVENTS=['PreToolUse','PostToolUse','PostToolUseFailure','SessionStart','SessionEnd','SubagentStart','SubagentStop','PreCompact','PostCompact','PermissionRequest','PermissionDenied','WorktreeCreate','WorktreeRemove',//...还有14个]asconst
插进来能干什么?不只是拦截,还能改。工具执行前可以把入参改掉,比如agent要写一个文件,hook把路径悄悄改到沙箱目录,agent不知道,照常执行,但文件落在了安全的地方。
有两种写法。一种是跑脚本,退出码0就放行,退出码2就拦截,逻辑自己写。另一种是让Haiku来判断,给它描述和标准,让模型决定要不要放行,等30秒。
这个设计的结果是:安全策略、审计日志、企业合规要求,全都能从外面接进来,不用改Claude Code本身。不同公司有不同的安全规定,以前只能fork代码自己改,现在写几个hook就行了。harness从一个固定的产品,变成了可以按需定制的底座。
1
写在最后
以上七个点,是从源码里挑出来最容易说清楚的部分。
更多同样有意思的东西还埋在里面:压缩后消息的精确重建顺序、工具并发分区的状态竞争处理、Bash命令里针对Zsh和PowerShell各自不同的安全绕过逻辑、sub-agent的MCP服务器生命周期管理……每一处细节背后都有一个真实踩过的坑。
想搞清楚一个生产级harness到底是怎么运转的,直接去读代码。
512000行代码在解决一个问题:怎么让一个会犯错的语言模型,稳定地干完一件需要很多步骤的工程任务。
这个问题比"哪个模型更聪明"更难回答,也更值钱。
行业过去两年把agent失败归因于模型能力不足。但Claude Code认为:模型早就够用了,缺的是那层认真造出来的harness。context配额、压缩熔断、工具延迟加载、sub-agent隔离、hook平台……这些东西不是Anthropic的独家秘方,是任何一个想把agent做稳的团队迟早要解决的问题。
Anthropic先给了个方案,然后意外地把答案公开了,而这可能是这次意外泄露里最值得研究的地方。