王者荣耀怎么发布王者状态
2026-07-29 3432827
2026-07-29 0
你在终端输入一句话并按下回车,屏幕便逐字显示内容;其间还会自动执行几个命令、读取几份文件、修改一两个文件,最后给出一段总结。整个过程有时只需一两秒,有时则要几十秒。
那么,这一秒内究竟发生了什么?
模型是先一次想好全部内容再输出,还是一边思考一边行动?
那些“自动执行的命令”由谁决定,又是在何时决定执行的?
这些命令与最后的文字总结之间,又有怎样的关系?
如果模型中途改变决定——原本准备读取 A 文件,读到一半却转而修改 B——这一变化如何发生?
理解 Claude Code 这类 Agent,关键恰恰在于这些问题。那么答案究竟是什么?
答案其实并不神秘:把完整流程展开,就是一条普通流水线,再加上一个 while 循环。让模型能够实际动手的这套脚手架,名为 Harness。
系统先把你的输入处理成一条结构化消息,再送入一个名为 query 的异步生成器;
query 自身几乎不处理工作,而是通过 yield* 把全部控制权交给 queryLoop——一个 while 循环。
在循环中,模型与工具交替接力:模型提出“我要读这个文件”,工具随即运行并把结果放回去;模型再次提出要求,工具继续执行,直到模型表示“我说完了”。
每一步都会通过 yield 将事件以流的形式送回 REPL,再由 REPL 使用 Ink,把内容逐字绘制到终端上。
概括成一句话:一次回合 = 一个 while 循环 + 一条流式事件管道。
这里需要记住两个关键词:其一是“循环”,Agent 的全部“自主性”由此产生;其二是“流式”,打字机效果、工具实时进度以及可被 Ctrl+C 中断的能力,都建立在 yield 这个字之上。接下来的三节,将逐层拆解并说明这句话。
要拆解这套机制,最省力的切入点并不是直接阅读源码—src/query.ts 它有 1700+ 行,仅 queryLoop 就超过上千行,直接读下去很容易迷失。应先抽出骨架:移除 hook、权限校验、压缩、计费与日志等外壳后,其本质与一个 8 行玩具 Agent 并无不同。
以下内容就是 Harness 单次回合的最小骨架:
# 极简版:harness 行动回路的最小实现messages = [{"role": "user", "content": prompt}]while True:resp = client.messages.create(model=MODEL, messages=messages, tools=tools)messages.append({"role": "assistant", "content": resp.content})if resp.stop_reason != "tool_use": # 模型不再要工具 → 说完了breakresults = run_tools(resp.content)# 执行模型要的工具messages.append({"role": "user", "content": results})# 结果塞回去
盯住三个关键点:
while True:这是没有固定次数的循环。结束与否不由计数器决定,而由模型自己表示“完了”。Agent 的“自主性”正源于此——Harness 并未写下这种“自主”,它完全存在于模型中。stop_reason:模型每轮都会给出停止原因。需要工具时就是 tool_use,完成表达时就是 end_turn。整个 Agent 的行为分支,都取决于模型输出的这个字段。messages.append(...):工具结果不会被暗中使用,而会作为新的 user 消息加入对话历史。在模型眼中,始终存在一段连续对话——工具的“动手”被转换为对话中的“轮到你了”。记住这三点,后续开展Harness工程实践时,就不容易被各类 hook、权限、压缩和子 Agent 干扰;它们只是 Harness 为骨架增加的“器官”,骨架本身仅有这三行。
还需注意一个细节:这个极简版本采用“同步”方式——create 调用结束并取得完整 resp 后才继续执行。Claude Code 的真实实现则采用流式方式,模型一边输出 token,循环一边接收。不过骨架并未改变,只是把“一次性 resp”替换为按时间顺序抵达的一连串事件。流式影响体验与响应速度,却不改变结构。
理解骨架后,再把 Claude Code 的真实实现叠加上去。下图呈现一次完整回合的全景,也概览了 Harness 五脏在这一秒中的工作。每个节点都对应一个“器官”,后文将分别放大说明;此处先记住这张图,因为它是后续内容的导览。
下面逐一查看节点,同时辨认各个器官:
handlePromptSubmit(src/screens/REPL.tsx),这一回合的"启动按钮"。Message,塞进对话历史。这一步会拼上 system prompt、CLAUDE.md、memory——那是上下文工程这个器官的活,后面详聊,这里只当成"造一条消息"。query(src/query.ts)。请注意其声明中的 *——async function*,它是异步生成器,能够边运行边向外输出事件。query 自身几乎不处理工作,而是通过 yield* queryLoop(...)(src/query.ts)将全部控制权交给 queryLoop(src/query.ts)。这里才是行动回路真正的核心。stop_reason。tool_use 分支——运行工具、将结果放回消息,再返回循环顶部继续询问模型。这条回路正是 Agent “自动执行命令”的来源,也是工具系统(Part 2)接入行动回路的接口。end_turn 分支——完成收尾,并将整条路径中积累的事件一路 yield 输出。位于中间的 Stop Hook,是安全护栏插入流程的拦截点。query 的 yield* 回到 REPL 的 for await (const event of query({ ... }))(src/screens/REPL.tsx),onQueryEvent 把它翻译成 React state,Ink 把新 state 渲染到终端——这是人机交互的一端。一句话概括该图:回车 → 加工 → query → queryLoop(模型与工具反复接力)→ 流式 yield → 渲染。生产级系统的全部关键机制都集中在中间的循环,而这个循环正是行动回路。
现在重新审视第 1 节留下的悬念——“Harness 究竟做了什么”。答案分布在图中的各个节点,并呈现为你能观察到的三类现象:
yield。模型每产生一个 token、工具每输出一行,都会形成一帧事件;REPL 收到后立即把它绘制到屏幕。所谓“打字机效果”并非前端动画,而是事件确实按时序抵达,屏幕显示速度就是模型生成速度。也因为事件采用流式传递,你按下 Ctrl+C 才能在任意一帧将其打断。tool_use 分支。模型判断“我需要读取这个文件”后,queryLoop 便让对应工具运行,将输出加入对话历史,再返回循环顶部继续询问模型。你看到它“自己读了三个文件、改了一个”,实际是这条回路重复运行,每次都让模型依据新结果重新决策。模型不会预先规划“读三个文件”,而是在每一轮根据最新信息临时确定下一步。end_turn。只有模型主动给出这个 stop_reason,循环才会结束。在此之前,queryLoop 绝不会“自行决定结束”——Agent 的终点始终由模型选择。可以发现,这三类现象没有一项由 Harness “思考”产生。Harness 只把模型决策转换成动作,再把动作结果送回模型;智能始终位于模型一侧。
可以将整套 Harness 理解成一个值班室。
你作为送件人,把工单(输入)交到窗口。值班员(queryLoop)收到工单后只做一件事:致电专家(模型)询问:“这个该如何处理?”
专家回答:“我需要查看 X 文件。”值班员不会亲自查,而是安排助理(工具)查询并带回结果,随后再次致电专家:“已经查到,内容如下,接下来呢?”专家又说:“再看看 Y。”流程如此循环。
直到某一次,专家表示:“可以了,我明白了,答案就是这个。”——end_turn。值班员整理最终答案后,再从窗口递交给你。
关键在于明确分工:值班员和助理都属于 Harness,专家才是 Agent(模型)。值班员不负责思考,只负责传话与派活(行动回路);助理不做判断,只负责执行(工具系统);真正决策的只有专家。循环驱动且职责清晰,这就是 Harness 的本质。Claude Code 的 query.ts 那 1700+ 行代码,主要是在为值班员、专家和助理分别增加护栏与助手:为值班员配置恢复机制(避免循环崩溃)、为专家整理上下文(工作记忆)、为助理增加权限审批(护栏)。
还有一个常被忽视的细节:值班员与专家之间采用双工通话——专家边说,值班员边向外传递(流式 yield),而不是等专家结束通话后再统一汇报。这就是终端内容逐字出现的原因。这个比喻恰好对应 TypeScript 中的 async function* 和 for await 这组语法:它们本来就是为“边生产、边消费”的流式管道设计的。
回到最初的错觉:Claude Code 的智能不在代码内部。代码属于 Harness,Agent则是模型。一次回合只是 Harness 借助一个 while 循环把模型决策转化为动作,再以流式形式显示到屏幕——query.ts 那 1700+ 行代码,都是为这个循环配置的器官与护栏。
而 Harness 的这些器官,恰好构成整个系列的地图:
请记住这张地图,它就是整个Harness的五脏六腑。
重新回顾一次会话的处理过程,会发现其中还有一个问题:如果专家始终要求调用工具,从不说“说完了”,该怎么办?更现实的情况是,对话过长、上下文窗口即将耗尽、工具运行失败或权限遭到拒绝,此时循环如何自救?那个 8 行的 while True 完全 hold 不住这些情况,要么会无限循环,要么会崩出一段 stack trace。
8 行的 demo 循环与 1700+ 行的生产级 queryLoop之间,差异就在于“如何避免崩溃”。这正是 Harness 工程的核心难题之一:可靠性。
下一篇,我们将深入 queryLoop 这个节点,查看一个循环中的 7 种 continue——其中每一行 continue 都代表一种自救动作:工具失败后如何重试、上下文接近上限时怎样压缩、配额超限后如何降级、模型越界时如何纠正。只有理解这 7 条路径,你才会真正相信:Agent 不会在执行途中卡死。
Let's go!