为什么 Agent 跑过第二个上下文窗口就开始退化
先说清楚长时程任务与交互式对话的根本差别,再用一次跑穿两个窗口的实验把退化现象亲手复现出来:第二个窗口把第一个窗口做过的事又做了一遍,而它并不是变笨了,只是在上下文里找不到任何做过的证据。
今日目标
- 能说出长时程无人值守任务比交互式对话多出的三个难点,并各举一个人不在场时无人兜底的具体后果
- 能用窗口、状态、清单、绿点、干预五个词准确描述一次无人值守运行,并说清窗口与状态为什么必须分开
- 能解释为什么模型接口的签名里不该出现任何 harness 配置参数,并说出一旦出现会让哪一类结论变成假绿
这七天要建的东西叫 harness,也就是模型之外那一层脚手架:谁来决定下一步做什么、进度记在哪、说做完了算不算做完、卡住了谁来叫停。今天先把问题摆在台面上,并亲手把退化现象跑出来一次。读完回到页面顶部把三条目标勾掉。
小白版讲解
交接班:他下班了,而交代只说了一遍
想象一条流水线的夜班交接。老张干到晚上十点,把手上的活交给小李:哪几台设备今天修好了、哪一台拆到一半、扭矩扳手放在第三个抽屉。交代完他下班回家。
现在问一个问题:这份交代存在哪里?
它存在小李的脑子里。如果小李中途换成小王,而老张已经走了,那这份交代就没了。小王只能看着车间从头猜:哪台设备修好了?我要不要再拆一遍看看?
这就是本课要解决的全部问题。老张那一次口头交代,对应的是 Agent 的一个上下文窗口;小李换成小王,对应的是窗口换了一个。人在旁边时这不成问题——你随时能补一句「那台修好了别动」。但如果车间从十点到八点一个人都没有,那份交代丢了就是真的丢了。
所以正解不是「把交代说得更清楚」,而是在墙上挂一块白板,谁接班都先看白板。白板上的东西才是权威的,脑子里记的只是当班的便利。这块白板就是本课的主角。
长时程难在哪:没人补话、没人叫停、没人验收
做过 Agent 的人对交互式对话很熟:你发一句,它回一句,你看着不对就纠正。无人值守跑一整夜,多出来三个难点,而每一个都是因为人不在场。
第一,没人补话。 交互式对话里,Agent 漏了一个前提你会补上;上下文被截断了你会再贴一遍需求。无人值守时没有这个人。具体后果是:窗口一换,它把三小时前做完的事又做了一遍,而没有任何人在场发现这件事——今天的实验就是这个。
第二,没人叫停。 你看着它第五次修同一个文件、修法一模一样,你会喊停。无人值守时它会修第五十次。具体后果是:一夜八小时的算力和预算,全部烧在同一条改不动的任务上,早上你拿到的进度是零,账单是满的。
第三,没人验收。 交互式对话里它说「已经改好了」,你会去跑一下。无人值守时这句话没人核。具体后果是:早上它告诉你四十条任务全部完成,你打开服务,它起不来。
Anthropic 的工程博客把长时程 Agent 的失败模式归成四类,并且每一类都配了一个具体的机关。这张表是本课七天的骨架来源,注意失败模式与机关必须成对看:
| 失败模式 | 配套的机关 |
|---|---|
| 过早宣布胜利 | 一份 feature 清单文件,逐条验证通过才允许翻成通过 |
| 在环境里留下缺陷且没有文档 | 初始 git 仓库加一份进度笔记,每次收尾都提交 |
| feature 被标记完成但根本没真测 | 端到端验证通过,才允许改那条的状态 |
| 浪费时间摸索怎么把应用跑起来 | 一个 init.sh,会话开头先读它 |
四个机关分别落在本课的 D4、D2、D5、D3。今天不做任何一个,今天只负责让你相信它们有必要。
五个术语,先立规矩
后面六天会反复用到五个词。现在把它们钉死,避免到了第六天大家说的还不是同一件事。
| 词 | 含义 | 最容易搞混的地方 |
|---|---|---|
| 窗口 | 一个上下文窗口 | 它是易失的:会被压缩、截断、重置,也会整个换掉 |
| 状态 | 磁盘上的权威进度记录 | 它跨窗口存活,是唯一可信的那一份 |
| 清单 | feature 清单,每条一个端到端可验证的条目,带通过与否 | 它是驱动任务选取的数据,不是给人看的进度条 |
| 绿点 | 一个通过端到端验证并且已经提交的点 | 回滚的目标只能是绿点,不能是「刚才那一下」 |
| 干预 | 停滞被检测到之后 harness 主动做的事 | 只有三种:换任务、回滚到上一个绿点、停机报警 |
窗口是易失的:它会被压缩、截断、重置,也会整个换掉
「上下文窗口有上限」这件事人人都知道,但多数人只想到一种死法。实际上它有四种死法,而且你的代码一行都不会报错。
- 被压缩:框架发现快到上限了,自己做了一次摘要,把前面二十步压成五行。压缩掉的部分里可能正好有那条最关键的约束。
- 被截断:更粗暴的做法,直接砍掉最早的几条消息。你以为还在上下文里的东西已经不在了。
- 被重置:进程崩了、机器重启了、任务重排了,新一轮从零开始。
- 整个换掉:一次长任务本来就要跑好几个窗口,第二个窗口是一次全新的会话。
前三种是运行环境替你做的决定,第四种是任务长度决定的必然。共同点是:它们都不会抛异常,也不会打日志说「我把你的进度扔了」。 你只会看到 Agent 的行为忽然变得莫名其妙。
所以本课的主线只有一句话:上下文是易失的,状态才是权威的。 harness 的全部工作,是让进度活在窗口之外。
一次跑穿两个窗口:退化不是模型变笨,是证据没了
今天的实验把这件事变成一次可复现的运行。运行器叫 nightrun,靶子是一个待建的笔记服务 notekeeper,随课自带四十条 feature 清单,全部初始为未通过。配置是两个窗口、每个窗口三步。
窗口边界上只做一件事:把这一轮累积的对话痕迹清空。
for (let w = 1; w <= config.windows; w += 1) {
// 窗口边界唯一做的事。这一行就是「窗口是易失的」本身——
// 把它注释掉,今天的退化现象立刻消失。
transcript.length = 0
for (let i = 1; i <= config.stepsPerWindow; i += 1) {
// 开场上下文只有任务描述与清单,一个字的进度都没有
const context = transcript.length === 0 ? base : `${base}\n\n${transcript.join('\n')}`
const reply = await askModel(context)
const parsed = parseReply(reply)
const applied = applyPatch(repoDir, parsed.featureId, parsed.code)
const verdict = verifyShallow(repoDir, parsed.featureId, applied)
// 「已完成」三个字是模型认得的唯一完成证据。
// 它被写进 transcript,也就是说:它住在窗口里,窗口一换就没了。
const mark = verdict.passed ? '已完成' : '未通过'
transcript.push(`[第 ${i} 步] 验证:${parsed.featureId} ${mark}`)
}
}for w in range(1, config.windows + 1):
# 窗口边界唯一做的事。这一行就是「窗口是易失的」本身——
# 把它注释掉,今天的退化现象立刻消失。
transcript.clear()
for i in range(1, config.steps_per_window + 1):
# 开场上下文只有任务描述与清单,一个字的进度都没有
context = base if not transcript else base + "\n\n" + "\n".join(transcript)
reply = await ask_model(context)
parsed = parse_reply(reply)
applied = apply_patch(repo_dir, parsed.feature_id, parsed.code)
verdict = verify_shallow(repo_dir, parsed.feature_id, applied)
# 「已完成」三个字是模型认得的唯一完成证据。
# 它被写进 transcript,也就是说:它住在窗口里,窗口一换就没了。
mark = "已完成" if verdict.passed else "未通过"
transcript.append(f"[第 {i} 步] 验证:{parsed.feature_id} {mark}")跑起来是这样的:
窗口 1 做了:F01 F02 F03
窗口 2 做了:F01 F02 F03 ← 其中 F01 F02 F03 是重复劳动
清单实际进度:3/40 [F01 F02 F03]
跑掉的步数:6 步
其中白干的:3 步
窗口 1 内部重复:无
窗口 2 内部重复:无
两个窗口第 1 步看到的上下文:
逐字相同(各 2891 字符)。窗口 2 开场时,窗口 1 干的活一个字都不在。六步换来三条进度,一半的算力是白烧的。注意最后那两行:两个窗口第一步看到的上下文逐字相同,各 2891 字符。窗口 1 辛苦干完的三条,在窗口 2 的开场里一个字都没有。
这里是本课的立论,值得慢慢读一遍:第二个窗口从头乱猜,不是模型变笨了,是证据没了。 模型这一侧从头到尾只有一条规则——挑第一条没有完成证据的 feature 去做。窗口 1 里它挑了 F01,因为清单第一条就是它;窗口 2 里它又挑了 F01,因为在它能看到的那 2891 个字符里,F01 依然没有任何完成证据。同一条规则,同一个输入,同一个输出。它的行为完全一致,是证据变了。
顺着这个结论还能多看出一层:进度其实一直存在,就在 harness 的内存里躺着——上面那行「清单实际进度:3/40」就是从它算出来的。问题不是没人记,是没人把它写进下一个窗口的开场。窗口 1 内部之所以没有重复,恰恰是因为那三步的证据还在同一个窗口里累积着。
为什么模型接口只该收一个字符串
现在讲全课的方法论支点。模型的接口长这样,只有一行:
AskModel = (context: string) => Promise<string>
一个字符串进,一个字符串出。签名里没有、也永远不会有任何 harness 配置参数——没有 withState、没有 options、没有 config。
这不是洁癖。因为这门课七天都在证明「某个 harness 机关有用」,而一旦模型能感知到当前开了哪些机关,它就可以配合演出:
// 对的签名:模型只能看到一个字符串。
// harness 开了什么、关了什么,它一个字都拿不到。
async function askModel(context) {
// 规则只有一条:挑第一条没有完成证据的 feature 去做
const queue = listedFeatures(context)
const done = featuresMarkedDone(context)
const next = queue.find((id) => !done.has(id))
return next === undefined ? 'ALL_DONE' : writePatch(next)
}
// 错的签名:多一个参数,整门课的结论全废。
// 它可以开了状态层就好好干、关了就装傻,而你永远看不出来。
async function askModelTainted(context, harness) {
if (!harness.stateEnabled) return 'IMPLEMENT F01'
return askModel(context)
}# 对的签名:模型只能看到一个字符串。
# harness 开了什么、关了什么,它一个字都拿不到。
async def ask_model(context: str) -> str:
# 规则只有一条:挑第一条没有完成证据的 feature 去做
queue = listed_features(context)
done = features_marked_done(context)
nxt = next((fid for fid in queue if fid not in done), None)
return "ALL_DONE" if nxt is None else write_patch(nxt)
# 错的签名:多一个参数,整门课的结论全废。
# 它可以开了状态层就好好干、关了就装傻,而你永远看不出来。
async def ask_model_tainted(context: str, harness) -> str:
if not harness.state_enabled:
return "IMPLEMENT F01"
return await ask_model(context)用那个被污染的签名跑七天,每一条曲线都会很漂亮:开状态层的运行完成度高,关状态层的运行完成度低,结论「状态层有用」看起来被实测证实了。但它是喂出来的假绿——你测的不是 harness 的效果,是模型有多配合。
请把这条判据背下来,它对你自己的项目同样成立:任何一次对照实验,被测系统都不该知道自己正在被测。 你评估的对象一旦能感知实验条件,你得到的就不是效果,是演出。反过来说,正因为今天这个模型只收一个字符串,它对 harness 完全无感,所以「第二窗口重复劳动」这个现象只能真实涌现——它不可能是剧本写死的,因为剧本里连「窗口」这个概念都不存在。
换 harness 的收益可以大于换模型
这门课凭什么值得单独学七天?有一个很硬的论据。
LangChain 公开过一次实测:同一个模型、不换模型,只改 harness,在 Terminal Bench 2.0 上从 52.8 分提到 66.5 分,涨了 13.7 点,排名从 Top 30 进了 Top 5。
十三点七分是个什么量级?它通常是一代模型的差距。而这一次一行模型都没换,改的全是模型之外那一层:什么时候允许退出、发现改同一个文件太多次要怎么办、怎么让 Agent 一进来就知道环境里有什么工具。
他们那次改了四处,其中三处正好是本课的主线:
| 那次的改动 | 做的事 | 本课对应 |
|---|---|---|
| 退出前的强制检查 | 拦住 Agent 声称完成,先跑一遍验证 | D5 端到端闸门 |
| 打转检测 | 追踪文件编辑,同一文件改太多次就建议换思路 | D5 打转检测与干预 |
| 本地环境上下文 | 映射目录结构、发现环境里有哪些工具可用 | D2 状态层与上下文重建 |
| 推理预算分配 | 规划与验证阶段多给推理,实现阶段给中等 | 本课不讲(属模型调参) |
第四处刻意不讲,因为它是模型调参,不是 harness 结构——这条边界本课会守到最后一天。
人在旁边,对比人不在场
如果你学过手搓 Coding Agent 那门课,会发现那二十一天已经把 harness 的零件逐个手搓过了:工具循环、会话恢复、停滞信号、上下文压缩。那本课还剩什么?
差别只有一条,但它改变了几乎每个结论:那门课全程是人坐在旁边的交互式 REPL,本课是人不在场跑一整夜。 同一个问题在两个前提下有不同的正解,这里有三处最典型的:
| 同一个话题 | 人在旁边(那门课的结论) | 人不在场(本课的结论) |
|---|---|---|
| git | 不用 git 提交,不能污染用户的仓库历史 | git commit 就是进度的权威记录,agent 自己提交 |
| 压缩 | 拿早期事实当探针,压完再问一遍,确认没丢 | 探针验的是「记不记得」,验不出「还听不听话」 |
| 打转 | 清单十轮不动就报一个停滞信号 | 报了之后自动怎么办:换任务、回滚、还是停机 |
这三处不是谁对谁错,是前提不同。git 那一条的反转最干脆:那门课操作的是用户的仓库,往里面塞 commit 是越权;本课操作的是 agent 自己的工作区,那里面的 commit 就是进度本身,不提交反而没有绿点可回滚。这个反转是 D4 的开场。
压缩那一条更值得记:探针法验证的是事实保真——早期提过的订单号压完还记得吗。但约束保真是另一件事,一个还记得全部事实的 Agent 完全可能已经不再遵守「未经确认不许删文件」这条规则。探针法对后者是盲的。这是 D6 的核心。
源码导读
今天的 lab 里有四个冻结文件(D1 定稿、后面六天逐字复制),先读两个最短的。
src/core/types.ts 是全课的名词表,六个类型对应前面那五个术语:Feature 是清单里的一条,RunState 是磁盘上的状态,Step 是窗口里的一步,VerifyResult 是一次端到端验证的结论,Outcome 是一次运行的结局(跑完、停滞、预算用尽、崩了)。重点读第六个:AskModel = (context: string) => Promise<string>。上一节讲的全部内容,都压在这一行签名里。读的时候留意 Feature.passes 上面那句注释——「只有端到端验证通过才允许翻成 true」,那条纪律是 D5 的全部内容。
src/core/git.ts 只有一个函数,但它是本课的红线所在。本课是全仓库唯一让 agent 真实执行 git 的课,而工作目录一旦解析错,commit 会打进课程仓库自己的历史。
// 全 lab 唯一允许调用 git 的地方。workdir 是必填的第一个参数。
function runGit(workdir, args) {
if (!workdir) throw new Error('runGit: workdir 必填,禁止依赖继承的 cwd')
// 每次都显式传 -C,绝不依赖进程继承下来的当前目录
return execFileSync('git', ['-C', workdir, ...args], { encoding: 'utf8' })
}
// 反面写法:不传目录,靠继承的 cwd。
// 它在 work/repo 里跑是对的,在任何别处跑就是往你自己的仓库里提交。
function runGitUnsafe(args) {
return execFileSync('git', args, { encoding: 'utf8' })
}def run_git(workdir: str, args: list[str]) -> str:
"""全 lab 唯一允许调用 git 的地方。workdir 是必填的第一个参数。"""
if not workdir:
raise ValueError("run_git: workdir 必填,禁止依赖继承的 cwd")
# 每次都显式传 -C,绝不依赖进程继承下来的当前目录
done = subprocess.run(["git", "-C", workdir, *args], capture_output=True, text=True, check=True)
return done.stdout
def run_git_unsafe(args: list[str]) -> str:
"""反面写法:不传目录,靠继承的 cwd。
它在 work/repo 里跑是对的,在任何别处跑就是往你自己的仓库里提交。"""
return subprocess.run(["git", *args], capture_output=True, text=True, check=True).stdout三条纪律配套:git 只在 work/repo 这个独立仓库里用;所有调用经这一个出口;一致性脚本会断言 git 这个字面量在这个文件之外出现即失败。最后那条是机器闸门——靠自觉守不住这种红线。
动手实验
今天要建的是 nightrun 的地基:类型层、唯一的 git 出口、四十条 feature 清单的靶子、离线剧本引擎,最后跑两个窗口把重复劳动打出来。四个冻结文件已经给全了,今天的练习只有一处——src/core/loop.ts 里被挖空的两层循环。零件都在上面备好了,缺的只是把它们串起来。
两个最容易做错的地方先说:每个窗口开头要把 transcript 清空(挪到循环外面,今天的现象就消失了);验证通过那一行必须带「已完成」三个字,那是模型认完成证据的唯一标记,写成别的词模型永远看不见进度。
- 读 src/core/types.ts,把六个类型和正文的五个术语对上号,特别是 AskModel 那一行。
- 读 src/core/git.ts,确认 workdir 必填、每次显式传 -C,这是今天唯一的红线。
- 跑 pnpm target:all 给靶子做一次体检:四十条补丁一次全打上、真起服务打二十多个请求,证明这四十条合起来是个能跑的服务。
- 补上 src/core/loop.ts 的两层循环,跑 MOCK=1 pnpm selftest 到十三项全绿。
- 跑 MOCK=1 pnpm start,看到窗口 2 把 F01 F02 F03 又做了一遍、六步只换来三条进度。
做完别急着收工,README 里那条变异检验才是今天的关键一步:把窗口循环开头 transcript.length = 0 那一行注释掉再跑一次,输出会变成这样。
窗口 1 做了:F01 F02 F03
窗口 2 做了:F04 F05 F06
清单实际进度:6/40 [F01 F02 F03 F04 F05 F06]
其中白干的:0 步重复消失了,进度翻倍。这一步证明了两件事:重复现象确实由窗口边界的上下文重置引起,不是剧本写死的;而「窗口 1 内部没有重复」那条断言在两次运行里都是绿的,说明它在真的区分情况,不是一条恒真的废断言。
面试题
今天四道题围绕三件事:长时程任务的状态归属、模型接口的边界设计、以及对「已完成」这句话的怀疑态度。展开后先看分析过程再看要点——照着推导练,比背要点管用。
第三道题几乎一定会以某种形式出现在真实面试里,因为它就是这个岗位每天的工作:一个跑了一夜的东西说它做完了,你凭什么相信。答「我去抽查一下」只能拿一半分。
检查清单与明日预告
- 能说出长时程无人值守任务比交互式对话多出的三个难点,并各举一个人不在场时无人兜底的具体后果
- 能用窗口、状态、清单、绿点、干预五个词准确描述一次无人值守运行,并说清窗口与状态为什么必须分开
- 能解释为什么模型接口的签名里不该出现任何 harness 配置参数,并说出一旦出现会让哪一类结论变成假绿
- 能说出上下文窗口的四种死法,并指出它们的共同点是一行都不报错
- 让 MOCK=1 pnpm selftest 十三项全绿,并亲眼看到两个窗口的开场上下文逐字相同
- 做过一次变异检验,能说清它证明了哪两件事
- 四道面试题不看要点也能答出至少三道
明天是 D2《状态与上下文的分界:把权威状态搬出窗口》。今天那个退化的修法其实已经摆在眼前了——进度一直在内存里躺着,只是没人把它写进下一个窗口的开场。所以明天的重点不是怎么写文件,而是这条分界线该画在哪里:哪三类信息必须持久化、哪两类不该持久化,以及一个反直觉的判据——重建上下文的目标是等效,不是还原。把一夜的对话原样喂回去,只会再烧一遍钱,而且很快又会撞上窗口上限。
面试题库
上下文窗口和状态有什么区别?为什么长时程 Agent 的权威进度不能只存在窗口里?What is the difference between the context window and state? Why can a long-horizon agent's authoritative progress not live only in the window?
国内高频海外高频进阶#long-horizon#state-management#context-window分析过程 · 先想清楚再作答
- 这题考的是「有没有真的跑过长任务」。只答「窗口有 token 上限,所以要压缩」是把它当成一道容量题——区分度在于你能不能说出窗口的**易失性**,以及易失之后谁来兜底。
- 先给一个能迁移的判据:问一句「这条信息如果现在丢了,还有别的地方能查到吗」。查得到的是易失副本,查不到的就是权威记录,必须落盘。窗口是前者,状态是后者,两者的区别不在容量而在**归属**。
- 然后点出窗口有四种死法,而且它们都不报错:被框架自动压缩、被截断、进程重启后重置、以及长任务本来就要跨好几个窗口。前三种是运行环境替你做的决定,第四种是任务长度的必然。共同点是没有任何异常、没有任何日志说「你的进度被扔了」,你只会看到 Agent 的行为忽然变得莫名其妙。
- 接着说清后果,这一段是面试官真正想听的。进度只活在窗口里,窗口一换,Agent 在新上下文里找不到任何做过的证据,于是从清单第一条重新开始。**这不是模型变笨了,是证据没了**——同一条规则、同一个输入,必然同一个输出。代价是双份的:算力白烧一半,而且重复写入会把工作区弄脏,后面依赖它的任务会给出你预料不到的结果。
- 交互式对话之所以感觉不到这个问题,是因为人就是那个兜底的状态层:你随时能补一句「那条昨天做完了」。无人值守时这个人不在场,所以必须有一个磁盘上的权威记录替他站着。
- 可预期的追问是「那把整夜的对话原样存下来、下个窗口再喂回去行不行」。不行,两个理由:一是窗口上限本来就装不下,二是那样做等于把一夜的推理再烧一遍钱。重建上下文的目标是**等效**而不是还原——只需要「做完了什么、正在做什么、试了几次」,中间推理和一次性的工具回执不该持久化。
How to reason about it · think before answering
- This tests whether you have actually run a long task. Answering only 'the window has a token limit, so compact it' treats it as a capacity question. The discriminator is whether you can name the window's volatility and say who covers for it when it disappears.
- Offer a transferable test: ask 'if this piece of information vanished right now, could I look it up anywhere else?' If yes it is a volatile copy; if no it is the authoritative record and must hit disk. The window is the former, state is the latter. The distinction is ownership, not size.
- Then note that the window dies in four ways and none of them raise an error: the framework auto-compacts it, it gets truncated, the process restarts and resets it, and a long task spans several windows by construction. The first three are decisions the runtime makes for you, the fourth is arithmetic. What they share is silence - no exception, no log line saying your progress was discarded. You only see the agent behaving inexplicably.
- Now the consequence, which is what the interviewer is waiting for. If progress lives only in the window, the next window contains no evidence of prior work, so the agent restarts from the top of the checklist. That is not the model getting dumber - the evidence is gone, and the same rule on the same input must produce the same output. The cost is double: half the compute is wasted, and duplicate writes dirty the workspace so later dependent tasks behave unpredictably.
- Interactive chat hides this because the human *is* the state layer: you can always add 'that one was finished yesterday'. Unattended, that human is absent, so an authoritative record on disk has to stand in for them.
- Expected follow-up: can you just save the whole night's transcript and replay it into the next window? No, for two reasons. It will not fit, and replaying it pays for a night of reasoning twice. The goal of rebuilding context is equivalence, not restoration - you need what is done, what is in flight, and how many attempts have been made. Intermediate reasoning and one-shot tool receipts do not belong on disk.
答题要点
- 窗口是易失副本,状态是磁盘上的权威记录;区别在归属而不在容量。
- 窗口有四种死法:压缩、截断、重置、整个换掉,而且一行都不报错。
- 判据:这条信息现在丢了还能不能在别处查到,查不到就必须落盘。
- 进度只活在窗口里,换窗口后 Agent 找不到证据就从头重做——不是变笨,是证据没了。
- 交互式对话里人就是那个状态层,无人值守时必须有磁盘记录替他站着。
- 重建的目标是等效不是还原:只留做完了什么、正在做什么、试了几次。
Key points
- The window is a volatile copy; state is the authoritative record on disk. The difference is ownership, not size.
- The window dies four ways - compaction, truncation, reset, replacement - and none of them raise an error.
- The test: if this vanished now, could I look it up elsewhere? If not, it must be persisted.
- With progress only in the window, the next window has no evidence and restarts from the top. The model did not get dumber.
- In interactive chat the human is the state layer; unattended you need a disk record to stand in.
- Rebuild for equivalence, not restoration: what is done, what is in flight, how many attempts.
为什么给模型的接口不该知道当前开了哪些 harness 功能?如果知道了会发生什么?Why should the model-facing interface not know which harness features are switched on? What happens if it does?
国内高频海外高频深入#experiment-design#interface-boundary#false-green分析过程 · 先想清楚再作答
- 这题的题眼在后半句。能说出「接口要干净」的人很多,能说出「一旦知道了,你的全部实验结论都作废」的人很少——区分度全在这里。
- 先把场景摆清楚:你在做一个对照实验,想证明某个 harness 机关(状态层、验证闸门、打转检测)有用。做法是开一次、关一次,比两次运行的完成度。这个设计的隐含前提是:**除了那个机关,两次运行的其他一切都相同。**
- 现在假设模型接口的签名里多了一个配置参数。那个前提立刻不成立了——被测系统能看见实验条件,于是它可以配合演出:开了状态层就好好干,关了就装傻。跑出来的曲线非常漂亮,结论「状态层有用」看起来被实测证实了,但你测的不是机关的效果,是模型有多配合。这类结果有个专门的名字:**假绿**。
- 更麻烦的是假绿不会报错,它长得跟真结论一模一样,而且是往你想要的方向偏。一个会自己报错的 bug 是运气,一个让你更自信的 bug 才是灾难。
- 所以正解是把签名收到最窄:一个字符串进,一个字符串出,模型只能看到喂给它的那段上下文,harness 开了什么一个字都拿不到。这样一来现象就只能真实涌现——第二个窗口之所以重复劳动,是因为它的上下文里确实没有完成证据,而不是因为有人告诉它「现在状态层关了,你装傻吧」。
- 这条判据可以抽象成一句通用原则:**任何一次对照实验,被测系统都不该知道自己正在被测。** 它在评估、A/B 实验、安全红队里是同一句话,答的时候点出来能显著加分。
- 可预期的追问是「那模型的行为数据怎么传进去,比如换一份更差的剧本」。答案是区分**模型侧数据**与**harness 配置**:换剧本改的是「模型会写出什么代码」,那本来就属于模型这一侧;而「当前开没开状态层」属于 harness 侧,前者可以传,后者必须挡在签名外。
How to reason about it · think before answering
- The real question is the second half. Plenty of candidates can say 'keep the interface clean'; few can say 'the moment it knows, every experimental conclusion you drew is void'. That is the whole discriminator.
- Set up the scenario. You are running a controlled experiment to show some harness mechanism - a state layer, a verification gate, a loop detector - actually helps. You run once with it on and once with it off and compare completion. The design assumes that apart from that one mechanism, everything else about the two runs is identical.
- Now add a config parameter to the model-facing signature. The assumption collapses: the system under test can see the experimental condition, so it can play along - work properly when the state layer is on, play dumb when it is off. The curves look beautiful and 'the state layer helps' appears to be empirically confirmed, but you measured cooperation, not effect. There is a name for that result: a false green.
- The nastier part is that a false green raises no error. It looks exactly like a real finding, and it leans in the direction you were hoping for. A bug that crashes is luck; a bug that makes you more confident is a disaster.
- So narrow the signature to the bone: one string in, one string out. The model sees only the context you fed it and learns nothing about harness configuration. Then the phenomenon can only emerge for real - the second window repeats work because its context genuinely holds no completion evidence, not because something told it to act forgetful.
- Generalize it into a principle worth stating out loud: in any controlled experiment, the system under test must not know it is being tested. Evaluation, A/B testing and security red-teaming are all the same sentence, and naming that earns credit.
- Expected follow-up: how do you inject model-side behavior then, such as a worse script? Separate model-side data from harness configuration. Swapping the script changes what code the model would write, which belongs to the model side. Whether the state layer is currently enabled belongs to the harness side and must stay outside the signature.
答题要点
- 对照实验的隐含前提是除被测机关外其他一切相同,配置参数会直接打破它。
- 模型一旦感知实验条件就能配合演出:开了好好干、关了装傻。
- 那样跑出来的曲线是假绿——测的不是机关效果,是模型有多配合。
- 假绿不报错、还往你想要的方向偏,比会崩的 bug 危险得多。
- 正解是签名收窄到一个字符串进一个字符串出,现象只能真实涌现。
- 通用原则:任何对照实验,被测系统都不该知道自己正在被测。模型侧数据可以换,harness 配置必须挡在签名外。
Key points
- A controlled experiment assumes everything but the mechanism is identical; a config parameter breaks that immediately.
- Once the model can see the condition it can play along - work when the feature is on, act dumb when it is off.
- The resulting curves are a false green: you measured cooperation, not the mechanism.
- False greens raise no error and lean the way you hoped, which is worse than a crash.
- Fix: narrow the signature to one string in, one string out, so phenomena can only emerge for real.
- General principle: the system under test must not know it is under test. Model-side data may vary; harness configuration stays out of the signature.
一个无人值守跑了一夜的 Agent 早上告诉你全部任务已完成。你会怎么验证这句话?An agent that ran unattended overnight tells you in the morning that every task is complete. How do you verify that claim?
国内高频海外高频进阶#verification#long-horizon#reporting分析过程 · 先想清楚再作答
- 这题考的是对完成声明的怀疑态度。答「我去抽查几条」只能拿一半分——抽查是手段不是方法,而且抽到的大概率是它做得最像的那几条。面试官想听的是你怎么把「它说完成了」变成一个不依赖它自述的判据。
- 第一步先把两件事分开:**它声称的状态**和**环境的真实状态**。前者是它自己写的一行字,后者是可以用代码去查的事实。所有验证都必须落在后者上。这一步做不到,后面所有努力都在验一份自述。
- 第二步给出可验证的形式。清单里的每条 feature 必须是端到端可验证的,也就是能写出一段不看 Agent 任何输出、只对着运行中的系统发请求就能判通过与否的检查。这一条决定了清单该怎么写:「优化一下搜索」不可验证,「搜索标题时不区分大小写地做子串匹配」可验证。
- 第三步是回归地跑,不是只跑最后那条。无人值守一夜会做几十条,后做的很容易踩坏先做的——只验最后一条等于没验。正确做法是把清单**整套**重跑一遍,并且看清单的通过标记是不是由验证结果翻的,而不是由 Agent 自己声称翻的。这条纪律的机器形式是:只有端到端验证通过才允许把那条改成通过。
- 第四步查一遍环境有没有被弄脏:同一段代码有没有被写两遍、有没有留下临时文件与调试开关、进度笔记有没有跟上。这一类问题不会让任何验证转红,但会在下一个人接手时炸。
- 最后要承认能力边界,这一点比全都答对更能显出工程经验:端到端验证走的是接口层(发请求看响应),测不到渲染类与观感类的缺陷。这类缺陷要么接浏览器自动化,要么就明确写进报告说「这一类没覆盖」——不许悄悄当成覆盖了。
- 可预期的追问是「那它把验证脚本本身改坏了怎么办」。答案是验证的定义不能和实现放在同一只手里:清单与验证脚本属于 harness 侧、进版本控制、每次运行前核对;Agent 只被允许改被实现的那部分。
How to reason about it · think before answering
- This is about whether you distrust completion claims. 'I'd spot-check a few' earns half credit - spot-checking is a tactic, not a method, and you will most likely sample the tasks it faked best. The interviewer wants to hear how you turn 'it says it is done' into a criterion that does not rely on its own account.
- First, separate two things: the status it claims and the actual state of the environment. The first is a line it wrote itself; the second is a fact code can query. All verification must land on the second. Skip this and everything after it is auditing a self-report.
- Second, make the claim verifiable in form. Every checklist item must be end-to-end verifiable, meaning you can write a check that ignores all agent output and simply drives the running system. That constrains how items are written: 'improve search' is unverifiable; 'title search does case-insensitive substring matching' is verifiable.
- Third, re-run the whole suite rather than the last item. A night unattended produces dozens of changes and later work commonly breaks earlier work, so verifying the tail verifies nothing. Re-run the entire checklist, and check whether the pass flags were flipped by verification results or by the agent's own assertion. The machine form of that discipline: only a passing end-to-end verification may flip an item to passing.
- Fourth, look for a dirtied environment: the same code appended twice, leftover temp files and debug switches, progress notes that fell behind. None of these turn a check red, but they detonate for whoever picks the work up next.
- Finally, state the limits of your own method - this shows more experience than a perfect answer. End-to-end checks that go through the interface layer cannot see rendering or look-and-feel defects. Either add browser automation or write explicitly in the report that this class is uncovered. Never quietly count it as covered.
- Expected follow-up: what if it edited the verification scripts themselves? Verification definitions must not sit in the same hands as the implementation. The checklist and check scripts belong to the harness, live in version control, and are reconciled before each run; the agent may only touch what is being implemented.
答题要点
- 先分开它声称的状态与环境的真实状态,所有验证只落在后者上。
- 清单每条都必须端到端可验证:不看 Agent 输出、只对运行中的系统发请求就能判。
- 整套回归重跑,不只验最后一条——后做的很容易踩坏先做的。
- 通过标记只能由验证结果翻,不能由 Agent 自己声称翻。
- 查环境有没有被弄脏:重复写入、临时文件、进度笔记是否跟上。
- 承认能力边界:接口层验证测不到渲染类缺陷,要么接浏览器自动化,要么在报告里写明未覆盖。
Key points
- Separate the claimed status from the real state of the environment and verify only the latter.
- Every checklist item must be end-to-end verifiable by driving the running system, ignoring agent output.
- Re-run the whole suite, not just the last item - later work routinely breaks earlier work.
- Only a verification result may flip an item to passing, never the agent's own assertion.
- Check for a dirtied environment: duplicate writes, leftover temp files, stale progress notes.
- State the limits: interface-level checks miss rendering defects, so add browser automation or declare the gap in the report.
同一个模型、同一套提示词,换一个 harness 成绩提升十几个点。这说明了什么?Same model, same prompts, but swapping the harness lifts the benchmark score by more than ten points. What does that tell you?
国内高频海外高频进阶#harness-engineering#benchmarks#model-capability分析过程 · 先想清楚再作答
- 这题是道开放题,考的是你心里有没有一张「模型能力与脚手架」的分工图。答「说明 harness 很重要」等于没答,得说清它重要在哪一段、以及这个结论能推到哪里、不能推到哪里。
- 先给事实和量级:LangChain 公开过一次实测,不换模型只改 harness,Terminal Bench 2.0 从 52.8 提到 66.5,涨了 13.7 点,排名从 Top 30 进了 Top 5。十三点七分通常是一代模型的差距,而这次一行模型都没换。
- 第一层结论:**模型的能力和它在基准上表现出来的能力,是两件事。** 分数是「模型能力 × 脚手架能不能把这份能力用出来」的乘积。脚手架那一项如果是 0.7,换一个更强的模型也只能拿到 0.7 倍的增益,而把脚手架从 0.7 提到 0.9 却不需要重新评测一整代模型——**这是成本结构完全不同的两条路**。
- 第二层结论是看它到底改了什么。那三处有用的改动都不是让模型更聪明,而是**在模型犯已知错误的地方加一道机关**:退出前强制跑一遍验证(治谎报完成)、追踪文件编辑次数太多就建议换思路(治打转)、一进来先映射目录与可用工具(治摸索环境)。共同形状是「已知失败模式 + 一道针对它的闸门」,这也正是本课七天的结构。
- 第三层是这个结论的边界,别过度推广。换 harness 的收益大小取决于原来的脚手架有多差、以及任务有多长:交互式的一问一答里人就是那个 harness,涨不了十几个点;真正的收益出现在无人值守的长任务里,因为那里每一个失败模式都没有人兜底。同时那次实验里第四个改动是推理预算分配,那属于模型调参而不是 harness 结构——把它一起算进「harness 的功劳」就是把两件事混了。
- 最后给出可执行的落点,面试里这一句最值钱:**定位收益之前先问这个 Agent 有没有人在旁边看着。** 无人值守的系统,先查它有没有权威状态、有没有完成闸门、有没有停滞干预;这三样缺一样,换模型的钱大概率白花。
- 可预期的追问是「那你怎么证明是 harness 的功劳而不是别的」。答案是对照实验的纪律:只动一个变量、模型与提示词逐字冻结,而且被测的模型侧不能感知当前开了哪些机关——否则你拿到的是假绿。
How to reason about it · think before answering
- This is open-ended and tests whether you carry a mental map dividing model capability from scaffolding. 'It shows the harness matters' is a non-answer; say which part matters, and where the conclusion does and does not generalize.
- Lead with the fact and the magnitude: LangChain published a run where, with no model change and harness changes only, Terminal Bench 2.0 went from 52.8 to 66.5, a gain of 13.7 points, moving from Top 30 into the Top 5. Thirteen points is typically a model generation, and not a line of the model changed.
- First conclusion: a model's capability and the capability it demonstrates on a benchmark are two different things. The score is capability multiplied by how well the scaffolding lets that capability out. If the scaffolding term is 0.7, a stronger model only buys you 0.7 of the gain - while lifting the scaffolding from 0.7 to 0.9 costs nothing like re-evaluating a model generation. The two paths have completely different cost structures.
- Second conclusion: look at what actually changed. None of the three effective changes made the model smarter. Each put a gate where the model was known to fail - force a verification pass before exit (against false completion), track repeated edits to one file and suggest a new approach (against looping), map the directory and available tooling up front (against flailing in an unfamiliar environment). The shape is always 'known failure mode plus a gate aimed at it', which is exactly this course's structure.
- Third, the limits - do not over-generalize. The size of the win depends on how bad the previous scaffolding was and how long the task is. In interactive turn-taking the human is the harness, so you will not find ten points there. The wins live in unattended long-horizon runs, where no failure mode has a human backstop. Also note that the fourth change in that experiment was reasoning-budget allocation, which is model tuning rather than harness structure; folding it into 'the harness did it' conflates two things.
- Then land it somewhere actionable, which is the most valuable sentence in the answer: before locating the win, ask whether anyone is watching this agent. For unattended systems, first check for authoritative state, a completion gate, and stall intervention. Miss any of the three and the money spent on a better model is probably wasted.
- Expected follow-up: how would you prove the harness gets the credit? Controlled-experiment discipline - change exactly one variable, freeze model and prompts verbatim, and make sure the model side cannot perceive which mechanisms are enabled, or what you have is a false green.
答题要点
- 给量级:不换模型只改 harness,Terminal Bench 2.0 从 52.8 到 66.5,涨 13.7 点,Top 30 进 Top 5。
- 分数是模型能力与脚手架的乘积,模型能力与它表现出来的能力是两件事。
- 改脚手架和换模型的成本结构完全不同,前者不需要重新评测一整代模型。
- 三处有用的改动形状相同:已知失败模式加一道针对它的闸门,而不是让模型更聪明。
- 边界:收益出现在无人值守的长任务里;交互式场景人就是 harness,涨不了这么多;推理预算属模型调参不算 harness。
- 落点:先问有没有人在旁边看着,再查权威状态、完成闸门、停滞干预这三样。
Key points
- Give the magnitude: harness-only changes moved Terminal Bench 2.0 from 52.8 to 66.5, a 13.7 point gain, Top 30 into Top 5.
- A score is capability times scaffolding; demonstrated capability is not the same as capability.
- Improving scaffolding and swapping models have different cost structures - the former needs no model re-evaluation.
- All three effective changes share a shape: a known failure mode plus a gate aimed at it, not a smarter model.
- Limits: the win lives in unattended long-horizon work; in interactive use the human is the harness, and reasoning-budget tuning is not harness structure.
- Actionable close: ask whether anyone is watching, then check for authoritative state, a completion gate, and stall intervention.