DeepSeek Harness:一次 Prompt 的完整执行轨迹
DeepSeek Harness:一次 Prompt 的完整执行轨迹
从输入、Context、Plan 到工具、子 Agent、反馈修复和最终验收
阅读方式:沿一个具体任务逐步前进,每一阶段都说明输入、处理、输出和背后的设计思想。
本文是“源码支持的教学执行轨迹”,不是一次实际项目运行录屏。 假设项目、Skill、模型响应、文件内容、job ID、测试结果均为教学数据;Harness 的机制来自当前源码核验。没有运行示例项目,没有调用示例 API,没有修改用户配置。实际 LLM 的调用顺序和回答可能不同。
先回答最重要的问题
Context 是否靠检索历史消息后拼接?
-
当前 Session 的历史:通常不是先做相关性搜索,而是从该 Session 的日志派生当前模型可见的消息视图,将未被替换遮蔽的历史带入请求。
-
其他 Session 的历史:当前 base/Web 默认不自动全文检索;全文搜索配置为
openAt: never。可通过显式会话引用、另行启用的历史工具或外接记忆 MCP 导入相关内容。 -
项目文件:适用指导与 Skill 目录可由插件注入;普通源文件不是全仓自动塞进请求,通常由模型提出 read/grep 等调用后才进入历史。
-
长历史:必要时裁剪或摘要,改变后续模型可见的 surface,而不是删除原始日志。
Plan 是怎么来的?
程序切换规划模式、注入指导、提供审阅工具;具体计划由 LLM 根据目标和工具证据生成。用户通过正式 Plan review 批准,后续 Step 才实施。
工具和子 Agent 谁决定用?
Harness 决定“有哪些工具、何时可用、能否执行”;LLM 决定“这一步请求调用什么、传什么参数”。子 Agent 是被调用后创建的独立执行者,不是每次 Prompt 自动必经的模块。
目录
1. 教学环境、原始 Prompt 与明确目标
1.1 我们假设的环境
应用:Web profile
Agent preset:standard,使用 Native 工具执行
主 Session:S-main,刚创建,此前从未开始 Turn,尚无模型消息 surface
主 Agent:lead
模型:demo-provider / demo-model
模型窗口:131072 tokens,支持工具调用与 medium effort
最大输出:8192 tokens
系统提示更新:本例假设 leading-only
工作区:/workspace/orders-demo
文件策略:workspace-write,根 Agent 审批可用
外部全文历史搜索:默认关闭
外部记忆 MCP:未接入
Team:本例不启用
这些不是你的当前个人模型配置。窗口、ID 和目录只用于展示机制。
假设项目中已有:
/workspace/orders-demo/
AGENTS.md
src/orders/routes.py
src/orders/service.py
src/orders/csv_export.py
tests/test_orders_export.py
.agents/skills/csv-export-guide/SKILL.md
这些项目文件仅作为教学背景,不表示本机真的存在。
1.2 项目指导输入
示意指导内容:
使用项目已有的权限装饰器,不得另造跳过权限的导出通道。
金额数据库单位为分,导出展示单位为元。
测试使用 pytest。
不要访问生产数据库;不得 commit 或 push。
1.3 一个教学 Skill
假设 Skill 目录中有 csv-export-guide,目录只展示:
名称:csv-export-guide
描述:复核 CSV 导出字段、转义、金额格式、权限与测试。
其正文提供这些方法:
先读既有导出与权限实现;规划阶段不实施。
用 CSV 库处理逗号、双引号和换行,不手写字符串拼接。
金额以元展示、两位小数,不使用千分位分隔。
导出检查应考虑电子表格公式解释风险。
核验未授权、边界条数和字段一致性。
该 Skill 是假定已安装的教学资源,不是 DSH 随附保证有的技能,也没有在本次工作中安装或创建它。
1.4 用户原始 Prompt:U0
/plan 为订单后台新增 CSV 导出。
导出当前筛选条件下的订单,最多 10000 条。
字段为订单号、客户名、金额、创建时间。
金额以元显示并保留两位小数,时间使用 Asia/Shanghai。
复用现有权限检查;不要访问生产环境,不要提交或推送。
先研究并给出方案,待我在计划审阅中批准后,再完成实现与测试。
必要时可用一个只读子 Agent 复核,并持续推进到验收完成。
后面把去掉 /plan 控制前缀的任务正文记为 U0-task。
1.5 什么是目标,什么不是目标
本任务的目标不是“调用 read”或“创建子 Agent”。工具是手段,最终验收是:
-
筛选条件正确传到导出逻辑;
-
导出权限复用已有机制;
-
超过 10000 条时按用户选择的策略处理;
-
字段、金额、时区和 CSV 转义正确;
-
约定的公式风险处理与测试到位;
-
测试和最终变更核验完成;
-
没有触碰生产环境、commit 或 push。
这是 LLM 对用户需求的具体化。Harness 不会从字符串里自动认证这些条件已经满足。
2. 总图:执行控制、数据和反馈
2.1 三种不同的箭头
-
控制流:谁触发谁执行。
-
数据流:哪些消息、参数、结果被传入下一组件。
-
反馈流:执行结果如何改变下一次模型决策。
sequenceDiagram
participant U as 用户
participant UI as Web / 命令入口
participant H as Harness 主循环
participant C as Context / Session
participant M as 主 LLM
participant T as 工具执行器
participant A as 子 Agent
participant E as 文件 / 进程环境
U->>UI: /plan + U0
UI->>H: 模式选择 + 正文进入 inbox
H->>C: 准入工具、组装提示、注入相关 context
C-->>H: 当前派生 messages 与工具定义
H->>M: R1:规划请求
M-->>H: Skill / 文件工具调用
H->>T: 校验、权限、执行
T->>E: 读取项目材料
E-->>T: 文件与搜索结果
T-->>C: 记录 tool/result
H->>M: 后续请求携带新证据
M-->>T: exit_plan_mode(plan)
T->>U: 正式计划审阅
U-->>T: Approve
T-->>H: approved=true,排队退出规划
H->>M: 后续 Step:实施模式
M-->>T: 修改文件、委派、测试
T->>A: 自包含只读审查任务
T->>E: 执行修改与测试
A-->>C: 完成通知与审查结论
E-->>C: 测试输出与退出码
H->>M: 失败与审查意见进入 context
M-->>T: 修复并再次验证
T-->>C: 通过结果与最终证据
H->>M: 汇总请求
M-->>U: 交付与未覆盖范围
图省略了实际事件与请求准备细节,下面逐步展开。
2.2 本例使用和没有使用的机制
|
机制 |
主线是否使用 |
原因 |
|---|---|---|
|
当前 Session 历史派生 |
是 |
每次请求要看到此前对话与工具结果 |
|
跨 Session 全文检索 |
否 |
默认关闭,也没有本任务需要的已知旧知识 |
|
自动全仓知识检索 |
否 |
普通文件通过工具按需读取 |
|
项目指导 |
是 |
已挂载插件按适用范围注入 |
|
Skill |
是 |
先目录,后调用加载正文 |
|
Plan review |
是 |
用户明确要求先规划、正式批准 |
|
Todo |
是 |
批准后跟踪执行步骤 |
|
Goal |
是 |
本例 LLM 选择建立长任务目标,作为继续策略 |
|
自动 Goal round |
否 |
主线在同一个 Turn 内完成并标记 Goal complete |
|
子 Agent |
是,1 个 spawn |
独立只读复核;减少主 context 中审查细节 |
|
Fork |
否 |
后文对比其种子规则 |
|
Jobs |
是 |
演示后台测试与显式收集结果 |
|
Context compaction |
未触发 |
本例历史压力保持在阈值下;另设分支讲解 |
|
Team / Workflow / Schedule |
否 |
这项任务不需要大规模编排或未来定时输入 |
|
Web 搜索 |
否 |
当前项目文件足够回答问题 |
这张表是本教学轨迹的选择,不是“DSH 每次都必定这样做”。
3. 最重要的三份数据:日志、Surface、请求
3.1 日志 L:发生过什么
Session 保存有类型的事件,例如:
plan/mode
agent/inbox/spliced
turn/start
step/start
system/message
user/message
request/header
request/context
assistant/message
tool/call
tool/result
step/end
turn/end
这只是事件类型示意,不是本文伪造的一份精确原始日志。每个事件有真实序号、来源、时间和相应数据结构。
3.2 Surface H:现在应让模型看到什么
当前实现从日志维护模型消息视图。可以进入模型消息历史的事件包括:
system/message
developer/message
user/message
assistant/message
tool/result
其中空内容和插件投影还会影响最终派生。其他 log-only 事件不直接变成一条模型消息。
例如 Goal、Todo 的状态有自己的记录;模型通常看到的是工具返回或插件生成的状态 context,而不是把这些内部事件 JSON 原封不动读一遍。
3.3 请求 Q:这一次传给模型的东西
当前循环的真实构造核心接近:
const request = {
...header.config,
messages: session.deriveMessages(),
toolHistory: session.toolHistory(),
tools: header.tools,
sessionId: session.id,
signal,
}
这里是为了易读省略条件分支后的源码说明,不是可以直接运行的外部 SDK 示例。实际请求随后由绑定 adapter 转成上游协议。
尤其注意:当前内部请求的系统提示在 messages 中,不是另一个隐藏 header.system。
3.4 最小 Context 公式
Q_i = 本 Step 的控制参数
+ deriveMessages(当前 Session surface 与内容投影)
+ 本 Step 准入的工具 Schema
+ 适配器所需的工具历史信息
不是:
Q_i = 搜一下所有历史 + 任意拼一些相关聊天 + 新问题
后者可以由可选检索插件实现,但不是默认主循环的历史构造算法。
3.5 本文的快照记号
|
记号 |
含义 |
|---|---|
|
|
含/不含规划指导的有效系统提示 |
|
|
用户原始任务正文 |
|
|
适用项目与用户指导 |
|
|
当前动态 runtime 事实快照 |
|
|
独立 time-context listener 条件注入的时间背景 |
|
|
Skill 名称与描述目录 |
|
|
第 i 次主模型输出 |
|
|
对应工具返回批次 |
|
|
子 Agent 完成通知和最终回答 |
|
|
当前准入工具集 |
后面的快照以逻辑组成展示,而不是宣称是某提供方逐字、逐 token 的 HTTP body。真实排序由已记录消息顺序、插件和 adapter 决定;正文会给出当前标准组合下可以核验的关键顺序。
依据:循环请求构造、Session 派生语义、日志与投影。
4. 第一次请求:Context 如何真正组装
以下准备阶段并不各自消耗一次模型调用。直到“发送 R1”才发生第一次主模型推理。
阶段 0:Host 启动,能力注册
输入:profile、bundle、用户 patch、启动 overlays、插件代码。
处理:
-
Cordis 加载服务与实现:Session、Agent loop、LLM adapter、工具 registry/executor、文件与进程后端、权限、持久化、Skill、Plan 等。
-
Web standard 为一个 Agent 提供适合它的 scoped 组合。
-
建立 Schema、事件监听器、作用域、生命周期清理函数。
输出:一个可创建 Agent 和 Session 的运行环境。
思想:能力组合与任务决策分离。加载了文件服务,不代表已经把文件内容给模型;有 Skill registry,不代表全文技能已经加载。
阶段 1:创建或定位 S-main
输入:用户选择的工作区、preset、模型选择。
处理:创建新 Session 与 Agent;持久化后端建立相应写所有权;初始化工具/提示作用域和 inbox。
输出:
S-main:无已完成 Turn
lead:当前空闲
cwd:/workspace/orders-demo
model:demo-provider/demo-model
思想:先确定“这个任务属于哪个执行者和哪段历史”,而不是每条消息都无身份地直接调用模型。
阶段 2:处理 /plan 和正文
输入:前端发送的原始 U0。
处理:
-
Slash command 走命令入口,不应一概描述为普通
session/prompt。 -
/plan选择规划模式。 -
/plan <task>的正文通过agent.steer()进入后续输入。 -
在没有 open Turn 时,模式可直接提交;有 open Turn 时按边界排队应用。
输出:规划模式选中,U0-task 已进入 inbox。
如果输入不是被命令接管的普通Prompt,则走客户端 session.prompt(content, mode, ...) → Host prompt admission:检查内容、身份、附件与requestId,创建带用户来源的消息。mode=steer进入next-step并唤醒,queue/followup进入next-turn并唤醒。没有命令插件接管时,未知斜杠文本也不应假定自动有控制语义。
思想:模式控制与用户自然语言内容是不同数据。/plan 不是让 LLM 自己猜一个开关。
阶段 3:打开 Turn、领取输入
输入:Agent 收件箱中的 U0-task,以及可能存在的 next-step 注入。
处理:
-
写入
turn/start。 -
首次先领取全部 pending next-step 输入,再至多领取一个 next-turn prompt;后续 Step 领取 next-step 输入。本例
/plan正文就在 next-step,next-turn 可以为空。 -
固定本 Step 的 provider、model、显式 reasoning effort。
输出:本 Step 的已领取消息与模型选择快照。
思想:input claim、排队和模型选择是运行时状态,不能靠对话文字自行同步。
阶段 4:准入工具,组装 sections/contexts/variables
输入:Agent scope、选中模型、当前模式、已注册贡献、取消信号。
处理:
-
tools.resolve(...):求值条件工具、作用域限制和注册状态,形成ResolvedToolSet。 -
systemPrompt.assemble(...):收集有效 prompt sections、动态 contexts、变量、工具声明。 -
按规定顺序组织 sections、变量和 Schema。
输出:类似这样的组装结果:
sections:Harness 身份 + persona + 工具指导 + plan policy + 其他有效指导
contexts:cwd、权限等 runtime 动态事实贡献
时间背景:由 time-context 独立 pre-step listener 注入,不属于上述 assembly contexts
variables:当前 model 等可插值变量
tools:read/grep/glob/skill/ask_user_question/exit_plan_mode 等准入定义
这是内容摘要,不是一个固定全产品模板。
思想:同时解决“告诉模型什么”和“允许它调用什么”,避免 prompt 承诺的能力与实际 executor 不一致。
阶段 5:agent/pre-step 注入与决策
输入:领取的消息、组装结果、本 Step 身份。
处理:协作 waterfall 中的插件可以:
-
加入适用指导 baseline;
-
加入 Skill 目录;
-
处理显式会话引用或
/Skill手势; -
检查 Goal round 是否过期;
-
压力满足条件时进行裁剪/压缩;
-
接受、改写或拒绝本批输入。
Fallback 将有需要的动态 runtime context 投影成 user-role 候选消息。
输出:enter(messages, ...) 或 reject。
本例第一轮需要注入 I、D_1、K,并由独立 time-context listener 提供时间/时区背景 W_1。该时间消息不是 D runtime 的 assembly context;当前默认至少间隔 10 分钟刷新,不是每 Step 必加。普通源文件内容此时尚未全部读取。
思想:context 是多个拥有明确责任的生产者贡献出来的,不是一个随意往字符串里追加内容的全局函数。
阶段 6:准备真实调用,再录入模型可见消息
输入:已接纳消息、固定模型选择、组装结果。
处理:
-
记录
step/start。 -
prepareCall()校验 adapter、参数与默认值,绑定本次实际调用和路由能力。 -
刷新动态 context,但不重跑本 Step 的整个组装。
-
协调系统提示节点;本例 first call 写入
S_plan。 -
将已接纳用户消息、指导和 context 写入模型消息 surface。
-
必要时记录
request/header、request/context。 -
deriveMessages()派生并冻结本次请求。
输出:R1 的不可变请求快照,以及仍然可取消的信号。
关键点:真实 adapter 准备成功之前,不把待接纳系统提示和用户输入提前当成已经发给模型的事实。取消与准备失败有清楚的录入边界。
阶段 7:第一份 Context 快照 C1
当前 standard 的首轮关键消息顺序可理解为:
system:有效 S_plan(首个系统节点)
user:U0-task
user:I,指导 baseline 在领取的用户消息后插入
user:D_1,runtime context
user:K,Skill catalog 后续加入
插件顺序、额外输入、显式 Skill 手势或用户覆盖可改变细节;上面只展示这个教学环境的核心消息。不是一个通用硬编码顺序。
此外还有独立 time-context 注入的 user-role 时间消息 W_1,不是把时间硬塞进 D_1;它相对其他插件的具体排列以 listener 为准。上面的列表展示核心相对顺序,不是完整 wire 数组。
工具 Schema T_1 作为另一组请求输入,不应伪装成普通用户聊天。fresh /plan 首请求没有旧 header 时,不会再凭空多出一条“模式已切换”通知。
C1 的可见事实:用户目标、项目规约、运行范围、可用方法目录。C1 没有:普通源文件全文、别的 Session 记忆、子 Agent 结果、未来测试结果。
依据:preStep 的真实调用顺序、录入与 stream、System Prompt 组装、指导与目录、Skill 目录与正文、baseline插入源码、目录尾附源码、独立时间消息源码、时间刷新默认、plan边界提交。
5. 主线逐阶段执行:R1—R14
5.1 数字先说明白
-
R1—R14:主 Agent 的 14 次示意模型请求,没有失败的 API attempt。 -
B1—B3:同一个子 Agent 的 3 次示意模型请求。 -
工具 batch 中可有多次调用,因此“14 次请求”不等于“14 个工具”。
-
用户回答问题与正式 Plan review 是工具交互,本例没有因此开启新的主 Turn。
-
所有主 R1—R14 都属于首次主 Turn
T-main-1;直到 R14 无后续欠账才结束。
这些计数只描述教学轨迹,不是运行日志统计。
R1:判断任务,加载方法,找项目入口
输入 C1:S_plan + U0-task + I + D_1 + W_1 + K + T_1。这是逻辑组成,不表示把全部贡献按这个字符串顺序发给模型。
LLM 的判断:这是受权限和格式约束的 CSV 任务,应先加载相关 Skill、找到现有实现;不能直接写新模块。
模型输出 A1:工具调用意图(示意):
[
{"name":"skill","arguments":{"name":"csv-export-guide"}},
{"name":"glob","arguments":{"pattern":"**/*export*","path":"/workspace/orders-demo"}}
]
这些 JSON 表示易读的调用意图,不是上游 wire 或 SessionEvent 的完整结构。
Harness 处理:解析参数、检查已准入定义、执行 Skill load 和路径搜索、记录 call/result。
输出 O1(教学):
skill:<skill_content> 完整方法正文与资源说明 </skill_content>
glob:src/orders/csv_export.py、tests/test_orders_export.py
下一轮为什么需要它:R2 现在知道 Skill 的真实操作要求和候选位置,不能只靠目录一句描述猜技能正文。
思想:渐进披露与先定位后读取。Skill 不执行实现,提供的是如何安全组合已有工具的知识。
R2:读取当前实现,形成项目事实
输入 C2:C1 的有效组成,加 A1 + O1;动态事实变化时有更新。
LLM 的判断:需要了解权限、筛选、CSV 和已有测试,而不是读整个仓库。
模型输出 A2(示意):
[
{"name":"grep","arguments":{"pattern":"permission|filter|export","path":"/workspace/orders-demo/src/orders","include":"*.py"}},
{"name":"read","arguments":{"file_path":"/workspace/orders-demo/src/orders/routes.py","offset":1,"limit":180}},
{"name":"read","arguments":{"file_path":"/workspace/orders-demo/src/orders/service.py","offset":1,"limit":180}},
{"name":"read","arguments":{"file_path":"/workspace/orders-demo/src/orders/csv_export.py","offset":1,"limit":160}},
{"name":"read","arguments":{"file_path":"/workspace/orders-demo/tests/test_orders_export.py","offset":1,"limit":180}}
]
输出 O2(教学摘要):
路由已有 require_permission("orders:export")。
service 支持当前 filter 参数。
数据库金额字段 total_cents,单位分。
已有 CSV writer 可复用。
已有 8 条相关测试,尚缺公式输入案例。
真实工具返回是文件片段/匹配;上面的摘要只是文档为读者压缩的可核验结论。模型不能仅拿一句“已有权限”代替读源代码。
下一轮影响:形成可依赖项目事实;避免另造权限层、误解货币单位。
思想:工具结果改变模型的知识状态。这里不是 embedding RAG,而是路径/文本搜索加显式文件读取。
R3:澄清人类拥有的取舍
输入 C3:已有需求和项目事实,但“最多 10000 条”尚有产品歧义。
LLM 的判断:超过上限时,是截取前 10000 条还是整体拒绝?源码不能替用户决定。
模型输出 A3(示意):
{
"name":"ask_user_question",
"arguments":{
"questions":[{
"id":"row-limit-policy",
"question":"筛选结果超过10000条时,希望如何处理?",
"options":[
{"label":"整体拒绝,提示缩小范围"},
{"label":"只导出前10000条,明确提示截断"}
]
}]
}
}
用户输入:选择“整体拒绝,提示缩小范围”。
输出 O3(逻辑值):该问题的已选择选项;工具结果进入当前历史。
状态变化:产品要求更明确,Plan mode 仍 active。这次回答不等于批准完整实施计划。
思想:可发现事实通过检查解决,用户取舍通过交互解决。避免把技术猜测伪装成用户要求。
R4:生成计划并进入正式审阅
输入 C4:项目证据 + 完整 Skill + 上限处理选择。
LLM 生成的计划内容(教学摘录):
# 订单 CSV 导出方案
目标:导出当前筛选结果,复用权限,最多10000条,超过整体拒绝。
实现:复用 routes/service/csv_export 三层;CSV writer负责转义。
格式:金额元、两位小数、无千分位;时间Asia/Shanghai。
测试:权限、筛选、上限、转义、金额与公式输入。
约束:不触碰生产,不提交、不推送。
验收:相关测试通过,最终diff可解释,无权限旁路。
模型输出 A4:仅调用 exit_plan_mode,把完整 Plan Markdown 放入 plan 参数。
Harness 处理:
-
校验当前 committed plan mode、计划标题与审阅通道。
-
展示完整方案给用户。
-
等待正式 Approve 或 Keep planning。
用户输入:点击 Approve。
输出 O4:approved: true,并有“从下一 Step 开始实施”的结果提示。
状态细节:
当前工具批次:仍保留 plan guidance
待应用模式:active=false
下一 accepted Step:使用退出规划后的组装,并提交 plan/mode(false)
主 Turn:仍 T-main-1,没有因为审批自动新建 Turn
下一 preStep 中先运行工具准入与 prompt assembly;它们读取所选的 pending 模式。随后 pre-step listener 提交模式事件,再进入本次 request。不能把代码中的“下一 request”误说为“必须下一整轮 Turn”。
思想:方案内容由 LLM 产生,实施批准由用户产生,状态应用边界由 Harness 管理。
R5:退出规划,建立执行状态与具体 Goal
输入 C5:历史中的计划与 O4,S_execute,当前 scope 和工具集。
本例假设 leading-only 路由,系统提示在头节点协调更新;不是把已生效 S_plan 与 S_execute 当两份同等现行指令随意拼接。
模型输出 A5(示意):
[
{
"name":"create_goal",
"arguments":{
"objective":"在订单工作区实现已批准CSV方案,修复相关失败并完成验收;不访问生产、不提交或推送",
"max_goal_rounds":3
}
},
{
"name":"todo_write",
"arguments":{"todos":[
{"content":"实现权限、筛选和CSV导出","status":"in_progress"},
{"content":"子Agent只读复核与运行测试","status":"pending"},
{"content":"修复问题并最终验收","status":"pending"}
]}
}
]
Harness 处理:
-
Goal 工具检查直接顶层人类授权与当前执行身份;原始 U0 就在本 Turn 中。
-
Goal 服务保存状态并武装继续资格。
-
Todo 保存完整列表;调度按工具安全分类处理,不因同批就一定并行。
输出 O5(教学):
Goal:G-main,revision=1,phase=active,roundsStarted=0,cap=3
Todo:1 in_progress,2 pending
ID 是教学符号;实际 ID 由运行时生成。
思想:把意图和进度显式化。Goal 是 LLM 在本例选择调用的工具,不是每次 /plan 自动生成的目标。roundsStarted=0 不妨碍在普通人类 Turn 中开展大量 Step。
R6:实施文件改动
输入 C6:批准的方案、项目原文、Skill、Goal/Todo 返回。
模型决策:对已读取文件作有限改动,复用既有结构。
动作(教学):
edit routes.py:增加导出入口,复用 require_permission。
edit service.py:传递筛选,检查结果上限。
edit csv_export.py:复用CSV writer,转换金额和时间。
为了教学反馈,我们假设第一版在金额格式中写了:
# 第一版示例缺陷:使用了千分位
amount = Decimal(order.total_cents) / Decimal(100)
amount_text = f"{amount:,.2f}"
它能生成 CSV,但不符合“金额无千分位”的 Skill/测试约定。
Harness 处理:检查参数、权限、读前编辑等策略,执行文件变更并返回结果。
输出 O6(教学):三个编辑操作成功;文件成为版本 V1。
思想:工具“编辑成功”只确认文件效果,不证明业务实现正确。下一步要用独立证据检验,而不是直接宣布完成。
R7:启动一个只读子 Agent,同时启动测试
输入 C7:V1 已写入;主模型知道任务与代码改动。
模型输出 A7:委派 prompt(教学完整示例):
你是订单CSV方案的只读复核者。
目标:检查刚完成的V1是否有权限旁路、字段/金额/时区问题、CSV转义和公式解释风险。
需求:当前筛选、最多10000条、超限整体拒绝、金额元两位无千分位、Asia/Shanghai。
位置:/workspace/orders-demo,重点看src/orders三文件和tests/test_orders_export.py。
约束:只读,不改文件、不访问生产、不提交推送、不调用需要人类审批的操作。
方案已由用户正式批准;你只审查,不替主Agent实施。
必要时加载csv-export-guide正文。
返回:发现、文件与位置、建议测试;没发现也说明检查范围,不能认证全部生产安全。
调用 subagent 时给出独立名称 csv-reviewer,采用 spawn,不传另一个模型路由,默认沿用父选择。
同一模型输出还提出:
{
"name":"bash",
"arguments":{
"description":"运行订单CSV导出测试",
"command":"pytest tests/test_orders_export.py -q",
"workdir":"/workspace/orders-demo",
"run_in_background":true
}
}
输出 O7(教学):
subagent:已启动 csv-reviewer,先返回名称,不返回完整审查结论
bash:已接纳后台 job J-test-1,尚未完成
调度假设:R6的文件改动全部 settled;R7到R9修复前,V1实现与测试输入保持不变。pytest仅使用合成订单和隔离fixture,不连接生产。测试可能写缓存,因此它不是OS级只读执行;子Agent只读复核与它并行,而后续代码改写必须等第一次测试与本次复核都结束。
思想:两个执行单元可以推进不同工作。主模型不应把“已启动”当“已通过”,也不必复制子 Agent 的全部阅读细节。
只读任务说明是行为约束;父为 workspace-write 时,不能仅靠该说明声称子运行时已切换为 OS 级 read-only。真正固定权限与任务约束要分别说明。
R8:收集第一次测试结果
输入 C8:当前代码 V1、审查者已启动、J-test-1 已知。
模型输出 A8:
{
"name":"job_output",
"arguments":{"job_id":"J-test-1","wait":true,"timeout_ms":30000}
}
教学调度假设:本次等待过程中测试结束;子 Agent 也即将结束。实际先后可不同。
输出 O8(教学):
FAILED test_money_format
Expected: 1234.50
Actual: 1,234.50
1 failed, 7 passed
[status: completed, exit code: 1]
这是教学结果,不是本次实际跑过的 pytest。
Harness 处理:返回 producer 输出与状态;被等待收集的结果避免再次重复通知。
思想:job_output 的等待结束不必总是成功;如果超时返回 running,则 job 仍在跑。本例进程已经结束,但测试失败。
特别区分:当前后台Shell的正常进程结算即使exit code非零,也映射为generic job completed;failed主要对应被抛出的启动/运行基础设施故障等路径。completed不是测试通过,exit code和断言才说明业务结果。
R8 与 R9 之间:子 Agent 结果进入主 inbox
我们选择一个清楚的教学时序:子 Agent 在 R9 准备前已经完成。
输入:子 Session 的最终回答。
Harness 处理:普通 parent-delivery 委派将完成通知送入在线父 Agent 的 next-step 输入。
输出 B_child(教学):
csv-reviewer 已完成只读复核。
发现:V1 的客户名等可控文本未处理 =、+、-、@ 开头的公式解释风险。
位置:src/orders/csv_export.py 的字段输出处。
建议:按项目约定处理危险前缀,并补四种前缀的参数化测试。
权限装饰器和超限拒绝路径已核对;未访问生产。
范围限制:这是代码复核,不保证所有电子表格软件或生产环境安全。
主模型会收到这段高价值结论,而不是自动收到 B1—B3 的每份文件和推理。
R9:根据测试与审查共同修复
输入 C9:
原需求与方案
+ 原Skill正文
+ 已写V1
+ O8:金额测试失败
+ B_child:公式解释风险及建议
模型决策:修复金额格式,并针对实际审查问题增加代码与测试,而不是原样重跑失败命令。
动作(教学摘录):
# 版本V2金额格式:保留两位小数,不带千分位
amount_text = f"{amount:.2f}"
# 教学约定的文本风险处理示例,非通用安全保证
if cell_text.startswith(("=", "+", "-", "@")):
cell_text = "'" + cell_text
新增四种前缀的参数化测试;仍使用 CSV writer 处理逗号、引号和换行。
输出 O9:编辑成功,测试从 8 个案例扩为 12 个案例,文件成为 V2。
思想:LLM 是可被反馈纠正的策略提出者;确定性测试和独立审查提供不同类型的证据。局部文本策略只是本例约定,不能泛化为全部 CSV 安全问题已解决。
R10:重新执行测试
输入 C10:V2 和新测试。
模型输出 A10:同一隔离工作区运行相关测试,后台返回 J-test-2。
输出 O10:测试 job 接纳。
思想:新代码需要新的结果;不能拿 V1 的“7 passed”替 V2 验收,也不能把 J-test-1 的结果误当 J-test-2。
R11:收集通过证据
输入 C11:明确的第二次 job ID。
模型动作:job_output(J-test-2, wait=true)。
输出 O11(教学):
12 passed
[status: completed, exit code: 0]
在这个教学环境中,对应进程成功退出。
思想:结果必须绑定到正确执行实例与代码版本。所谓验收不是“看见过一次绿色文字”,而是知道它验证了什么。
R12:主 Agent 最终检查
输入 C12:V2、通过测试、子 Agent 针对 V1 的发现与修复证据。
模型动作:
read:再次看最终实现和新增测试。
bash:git diff --check 与必要的最终diff核验。
get_goal:读取当前Goal的精确id与revision,供下一R13使用。
输出 O12(教学):
最终V2保留权限检查、筛选和拒绝超限路径。
金额格式与新测试一致。
CSV writer仍处理结构转义;约定公式前缀策略已覆盖。
diff --check通过;无commit/push动作。
get_goal:G-main,revision=1,phase=active,roundsStarted=0。
思想:子 Agent 审查的是 V1,不能把它的结论当 V2 的自动认证。父 Agent 必须对修复后的最终状态负责。
R13:关闭执行状态,不伪造完成
输入 C13:验收证据齐备,本例没有仍相关的运行中 job 或子任务。
模型动作:
update_goal:使用上一R12的get_goal返回,G-main 当前id、revision=1,action=complete。
todo_write:提交全部完成的完整列表。
输出 O13(教学):
Goal:revision=2,phase=complete,roundsStarted仍为0
Todo:全部completed
如果中途另有目标状态变更,就必须重新获取 revision,而不是死用 1。
思想:显式关闭继续意图。完成状态本身不是证明,前面的工具证据才支撑它。Goal cap 3 不是“最多执行 3 个 R”。
R14:最终回答,结束 T-main-1
输入 C14:当前有效 context 包括目标、计划、变更、失败、审查、修复、通过与完成状态。
模型输出 A14:
已实现CSV导出,复用原权限与筛选逻辑,超10000条整体拒绝。
金额元两位、Asia/Shanghai、CSV结构转义和约定前缀处理已落实。
第一轮发现金额失败,复核发现公式风险,均已修复。
相关测试12个通过,最终diff检查通过。
未访问生产,未提交推送;生产数据库和真实浏览器下载未实测。
这里的“未覆盖范围”是重要输出,不能删去以显得更完美。
Harness 处理:记录 assistant message,先写 step/end 并释放本Step的toolSet,再检查 agent/turn-stopping。该扩展点若新增next-step输入,Turn仍会继续;本例没有新增欠账,才写 turn/end 并变为空闲。Goal 已 complete,故无自动 Goal round。
思想:系统收敛于可说明的产物和证据,而不是无限继续。
依据:工具执行与后续请求分支、计划审批与 pending exit、Goal 工具权限、Jobs 等待和通知、后台Shell退出状态映射、Job status渲染。
6. 子 Agent 的独立轨迹与可见范围
6.1 Spawn 创建时传了什么
在 R7,父 Agent 传的是:
parent身份:S-main
子名称/描述:csv-reviewer / CSV只读复核
自包含任务prompt:R7中的完整委派说明
初始cwd:/workspace/orders-demo
默认模型选择:沿用父适合的选择
preset:沿用相应父preset revision,建立独立scope
这些创建参数不是全都作为聊天文字发送;身份、权限、cwd 等通过运行时与 context 贡献正确公开。
6.2 第一份子 Context
子system:它的persona、工具指导等有效sections
子user:自包含审查任务prompt
子user:由自身插件重新发现的适用指导I_child
子user:身份、目录、固定权限等D_child
子user:自身Skill目录K_child
子tools:该scope实际准入集合
不存在一项“把 C7 完整复制过去”。Spawn 不继承父聊天;子知道批准计划是因为委派 prompt 告知,而不是偷看了正式审阅结果。
它可以独立读取共享工作区的 V1,所以“没有父聊天”不等于“看不到父写的文件”。
6.3 B1:加载 Skill
输入:子任务、指导、目录。
LLM 判断:任务与 csv-export-guide 匹配,先加载完整正文。
输出:子 Session 自己的 Skill tool-call 和 tool-result。
父已加载正文,并不意味着 fresh 子 Agent 自动继承那份父工具结果。子必须自行加载,或者父在 prompt 中明确提供足够方法摘要;本例采用自行加载。
6.4 B2:读取 V1 并审查
输入:子 Skill 正文、任务和项目约束。
动作:读取三个实现文件和相关测试;不编辑、不提交、不过问生产。
输出:它自己 Session 中的阅读结果。
思想:局部材料留在局部上下文。共享文件是外部状态,同一时刻读到哪个版本需要明确。
6.5 B3:形成最后回答
输入:代码证据和 Skill 检查项。
输出:前述 B_child 文本,包含发现、位置、建议测试和范围限制。
它没有调用额外工具,自己的 Turn 收敛;subagent manager 收集本次结果,按 parent delivery 通知父 Agent。
不会自动返回的内容:
-
全部子工具调用与原始文件片段;
-
所有中间 assistant 输出;
-
模型内部未公开推理;
-
它执行环境中的任意变量和对象。
若父需要原始证据,可以要求结果包含必要片段、读取共享产物,或使用合适的历史查询接口;不能默认父已经看见全部。
6.6 父子可见内容对照
|
内容 |
父lead |
Spawn子csv-reviewer |
|---|---|---|
|
原始U0与澄清回答 |
有 |
只有委派prompt重述的部分 |
|
正式Approve工具结果 |
有 |
没有自动复制,prompt中说明已批准 |
|
父Skill正文结果 |
有 |
不自动继承;B1重新加载 |
|
共享文件V1 |
可读 |
可读,B2实际读取后进入context |
|
父Goal/Todo详细记录 |
有相应状态/结果 |
不自动复制,也不应替父控制根Goal |
|
子B2完整读取结果 |
不自动收到 |
有 |
|
子最后审查结论 |
收到完成通知 |
自己生成并记录 |
|
后续V2结果 |
R9后有 |
本次已结束,不自动获得 |
6.7 只读任务不等于自动只读权限
当前 in-process 子 Agent 权限在开始时固定,不能在子 Session 内部提权;需要审批的操作会自动拒绝,超范围应报告父 Agent。
父的 agent-local tool restriction 不会原样复制到新 scope,但这不等于权限逃逸。父来源和文件权限根、固定委派政策仍有专门管理。
本例“只读复核”还包含 prompt 行为要求。若部署需要强制仅能读取,应另设实际工具守卫、preset 或文件权限,而不是把一句只读当 OS 保证。
6.8 如果 R7 改用 Fork 会怎样
Fork 的种子截到父最后一个已完成 turn/end,不包含当前 in-flight Turn。
本例从新 Session 开始,R1—R14 都还在第一次 T-main-1。因此在 R7 调 fork,父没有已完成 Turn,种子是空。 它不会自动获得本轮 Plan、Approve、Skill 和文件工具历史。
若父此前完成过 T-main-0,Fork 会包含那个已完成前缀,再加本次子任务;当前 T-main-1 新事实仍必须写入 prompt。
普通 UI 的 Session fork 与子 Agent fork-provider 是不同操作,不能把它们的复制边界混用。
6.9 消息与等待的补充
-
send_message:确认 inbox 接受,不等回复;需要回复还要接收后续输入。 -
wait_agent:等调用者自己的 next-step inbox,不等任意 Agent 状态或任务板。 -
interrupt_agent:只确认停止请求接受;local 当前 Turn 停止不自动停止后代,external 不能续问。 -
inactive:只是现在不执行,不是“审查无错误”的证明。
主线只用普通完成通知,没有安排额外 follow-up 消息。完成通知不穿透正在运行的模型请求,也不因到达就让R8的job等待即时重新推理;本例它排在R9的边界消费。
若采用另一种调度,父已经空闲,通知可能唤醒一个只含通知来源的新Turn。那不等于自动Goal round,也没有本例T-main-1中的直接用户authority;要complete Goal,仍需当前直接用户或匹配的Goal round,不能只依赖旧聊天里曾有人类输入。
依据:Spawn seed 与默认选择、Spawn 可见内容与父结果、Fork 种子源码、固定委派权限、控制工具。
7. 每次模型请求的 Context 增量账本
7.1 一眼看到模型为什么改变行动
下表省略按条件出现的runtime/独立时间快照更新、内部控制日志和 provider 专有 framing,只记录教学主线的语义新材料。时间注入不是每Step必有。
|
请求 |
已有背景 |
本轮新增输入 |
模型输出 |
为什么转入下一步 |
|---|---|---|---|---|
|
R1 |
S_plan、U0、I、D、W、K、工具Schema |
首次任务与方法目录 |
Skill load + glob |
有正文与路径才能探索 |
|
R2 |
R1有效历史 |
A1/O1:Skill正文、候选路径 |
grep/read |
获取真实实现 |
|
R3 |
项目证据 |
A2/O2:权限、字段、现有测试 |
ask_user_question |
上限策略属于用户取舍 |
|
R4 |
需求+证据+方法 |
A3/O3:整体拒绝选择 |
exit_plan_mode |
正式批准才实施 |
|
R5 |
已批准计划 |
A4/O4 + S_execute |
create_goal + Todo |
建立可持续执行状态 |
|
R6 |
方案与状态 |
A5/O5:Goal与进度 |
edit实现 |
把意图变成代码V1 |
|
R7 |
V1 |
A6/O6:编辑成功 |
spawn审查 + 测试job |
编辑成功还不足验收 |
|
R8 |
执行实例 |
A7/O7:名称与job ID |
job_output |
收集真实测试结果 |
|
R9 |
V1与目标 |
A8/O8 + B_child:失败、审查 |
修金额、公式策略和测试 |
新证据要求修正 |
|
R10 |
V2 |
A9/O9:修复完成 |
第二次测试job |
V2需要新验证 |
|
R11 |
第二job |
A10/O10:J-test-2 |
job_output |
正确绑定执行结果 |
|
R12 |
通过证据 |
A11/O11:12 passed、exit0 |
最终read/diff + get_goal |
局部通过不替整体复核 |
|
R13 |
最终V2 |
A12/O12:验收证据、最新Goal |
complete Goal/Todo |
停止后续继续资格 |
|
R14 |
全过程有效历史 |
A13/O13:完成状态 |
最终交付文字 |
无剩余动作,Turn结束 |
7.2 C2 不是只发新工具结果
常见误解是第二次只发一句 Skill 或文件返回。一般不是。
在没有压缩的教学阶段:
C2.messages ≈ C1的有效消息
+ A1(含工具调用)
+ O1(配对工具结果)
+ 必要的动态context更新
工具Schema仍作为本 Step 输入。LLM 是在保留的需求和指导之下阅读新证据,不是每次失忆地只看最后一行。
7.3 C9 的逻辑快照
{
"format":"teaching-logical-snapshot-not-provider-wire",
"controls":{"provider":"demo-provider","model":"demo-model","reasoningEffort":"medium","maxTokens":8192},
"effective_system":"S_execute",
"retained_message_groups":[
"U0-task、I、Skill目录与已加载正文",
"项目read/grep证据",
"上限澄清选择",
"正式计划与批准结果",
"Goal/Todo工具返回",
"V1编辑调用和结果",
"子Agent启动名称与J-test-1",
"测试失败:金额格式不符",
"csv-reviewer完成通知:公式风险"
],
"tool_schemas":"T_9,本Step实际准入集合"
}
这个 JSON 刻意不是实际 API request。要重建真实请求,应按 Session 顺序调用派生器,再交给 adapter,不是把这些字符串分组直接当 wire messages。
7.4 哪些状态不会自动全文拼接
|
运行时保存的东西 |
模型何时看见 |
|---|---|
|
原始日志中所有事件 |
不是全都看见;由surface及投影筛选 |
|
Goal/Todo内部事件 |
通常通过工具返回或状态context,不自动全文事件JSON |
|
工具函数实现 |
不作为Schema发给模型,模型只见声明 |
|
上游认证key |
用于认证,不应变成prompt文本 |
|
未读取源文件 |
不因存在于磁盘而自动看见 |
|
子完整Session |
spawn父不自动收全文,只收结果/消息 |
|
文件中的新版本 |
实际读取/注入后才能成为模型观察 |
|
已遮蔽历史 |
不再整体进入当前请求,原日志可另读 |
7.5 每个 Step 都准备,但不是所有内容都反复新增
-
工具准入和prompt assembly是 Step 准备动作。
-
稳定系统提示无须每次新增一条。
-
不变指导/Skill目录有可见状态和digest检查,不必重复注入同一基线。
-
动态runtime context在变化或缺失时协调;旧snapshot可仍保留在历史,由新快照的supersedes语义说明当前事实,不是每Step都删除旧runtime。
-
独立时间消息按refreshIntervalMs条件更新,默认10分钟;它与runtime snapshot不是一个生产者。
-
request/header在初始、恢复、配置/工具变化或新系列等条件下记录,不是每次重复同一JSON。 -
当前消息在每次 API 输入中仍可能重复计费,日志没有重复append不等于API没有重复输入。
8. 工具执行到底发生了什么
8.1 用 R7 的测试命令拆开
模型输出:Bash 工具调用意图,不是进程已经运行。
输入检查:工具名字在 T_7 中吗?参数符合 Schema 吗?caller 是哪个 Agent?当前信号是否取消?
执行管线:
记录 tool/call
→ pre-execute:允许/拒绝/请求审批
→ monotonic guards:不能被后续重新放宽的否决
→ execute wrappers:期限、观测等
→ 工具body
→ 内容准备和post策略
→ finalizeContent
→ frozen final result
→ 记录tool/result
真实参数校验和批次调度在相应入口发生;上图展示主要策略与结果流程,不冒充完整函数调用栈。
8.2 Shell、实现、模型入口在这里怎样接起来
LLM 的 bash tool-call
→ tool-bash 消费者
→ ctx.shell 统一执行接口
→ 当前配置的 shell backend
→ subprocess / OS 执行
→ stdout、stderr、退出状态
配置本地实现时会使用本地 Bash 能力;配置沙箱后端时还会准备 confinement。不能根据工具名为 bash 就认定所有部署都直接、不受限地调用同一个 bash-local。
普通 Bash 每次新 shell;workdir 决定这一次的执行目录,之前的一次 cd 不自然延续。
8.3 工具结果如何回到下一次模型调用
执行端返回canonical value
→ 生成模型可见内容与展示元数据
→ 日志记录tool/result
→ 下一Step的deriveMessages包含对应结果
→ LLM根据结果继续
渲染文本、UI card 和程序调用者的 canonical value 是相关但不同的层。打码UI文本不是从程序内彻底抹掉值。
8.4 为什么必须配对
模型提出的 tool-call 有关联ID,后续 result必须正确对应。一次调用没有结果,会影响继续、恢复和协议合法性。
若操作开始但结果未可靠提交,运行时不能自信地说“没发生”,会保留 unknown outcome 等恢复信息。
8.5 Skill 与 Bash 的不同
-
Skill load 返回程序性方法知识,本身不完成 CSV 实现。
-
Bash body运行真实进程,可以改文件或产生外部副作用。
-
二者都可以表现为工具调用与工具结果,但安全风险和职责不同。
所有工具都叫tool,不意味着它们都是纯函数。
依据:工具管线、工具 canonical 与rendered区别、Bash输入输出与后台。
9. 同会话继续、恢复和跨会话检索
9.1 变体 A:不是新Session,而是已有当前历史
假设 S-main 此前有两轮已完成消息:
用户:订单金额字段单位是分。
助手:后续导出应除以100显示元。
用户:导出时间使用上海时区。
助手:采用Asia/Shanghai。
下一 Prompt 的请求通常会带这些仍在当前surface中的消息,再加新输入和必要context。
它不是先问向量库“最相关的两句话是什么”。这是会话历史的确定性派生与保留。 当然,长历史压缩后可能只带摘要,而非所有原文。
9.2 变体 B:重启后恢复这个Session
session persistence读取合法日志
→ 构造当前逻辑Session并修复需要的中断配对
→ 恢复surface和模型选择等状态
→ 挂当前preset及服务
→ 后续请求重新派生messages
读取旧 Session 数据不是重载 LLM 隐含脑状态。恢复也不保证文件、外部进程、提供方cache都还在。
9.3 变体 C:明确引用另一个Session
用户从UI显式引用旧会话,运行时捕获一个有预算、不可变、不受信任的文本快照,跟随引用消息进入目标历史。
这是“指定来源后制作快照”,不是默认全库搜索;源以后变化也不会改写已捕获内容。
9.4 变体 D:开启历史搜索工具
当前默认 base/Web:
session-query-sqlite:
path: ':memory:'
openAt: never
这是易读字段示意,不是可直接粘贴的完整Cordis patch。
若部署另行启用全文搜索和实验性模型历史工具,可出现:
LLM:调用session_search寻找过去CSV方案
工具:返回候选会话、片段
LLM:继续session_event_read读取精确证据
工具:返回具体事件
下一请求:把检索结果作为工具历史纳入
这里才是真正的“检索历史再加入当前context”。也要区分:当前SQLite方案是全文token/phrase搜索,不是自动embedding语义召回。
9.5 变体 E:外接记忆MCP
LLM:查询某项目长期约定
MCP:返回已保存事实和来源
LLM:核验范围与时效
下一请求:工具结果进入context并影响方案
是否写入、如何召回、embedding、冲突和遗忘由所接服务与指导决定;DSH随附记忆参考配置默认关闭。
9.6 一句话辨别这些“历史”
|
操作 |
本质 |
|---|---|
|
当前聊天继续 |
继续同一surface |
|
旧聊天resume |
从持久化恢复同一逻辑历史 |
|
子Agent fork |
捕获父已完成Turn前缀作为seed |
|
明确会话引用 |
指定旧内容的有界快照 |
|
session_search |
按需全文检索,需启用 |
|
记忆MCP query |
外部知识服务召回,能力由提供方决定 |
依据:默认历史搜索配置、Web覆盖、模型历史工具、会话引用、第三方记忆。
10. 历史过长时的裁剪与摘要分支
本主线没有触发压缩。下面是一条条件分支,不计入前面的R1—R14。
10.1 输入
某后续Step准备时,累计文件结果、工具Schema、消息与输出预留使压力达到策略阈值。
本例教学容量如果采用默认政策:
W = 131072
O = 8192
B = 65536
阈值T = floor(min(W×0.8, W−O−B)) = 57344
这是基于假设容量的计算,不是测得你这次会话占用了57344 tokens。
10.2 第一步:计量与确定性裁剪
处理:token meter根据当前surface、路由与usage估算压力;有pruner时先裁剪较早过大工具结果。
输出:保留头尾等受控内容,记录替换,重新计量。
思想:先移除高成本低必要性内容,可能无需额外模型摘要。Pruner不做语义理解,也不保证中间段没有重要信息。
10.3 第二步:辅助摘要调用
仍然过大时:
输入:选择的较早安全范围、系统头、相应工具历史与摘要要求。
处理:compaction-basic直接调用摘要模型,不是新建另一个完整通用子Agent;保留文本摘要,检查确实缩减、配对边界合法等。
输出:记录摘要与一个user-role checkpoint替换,近期尾部保留。
压缩前:S + I + U0 + 很多A/O + 最近失败与修复
压缩后:S + checkpoint(目标、事实、进度、约束) + 最近失败与修复
10.4 后续请求输入
原始日志L:仍保存那些旧A/O及替换证据
当前surfaceH:摘要遮蔽旧范围
下一请求Q:deriveMessages看到摘要+近期,不再整体发送被遮蔽原文
摘要有损,不等于可靠事实认证;需要精确参数时仍可以重读证据。
10.5 费用与缓存
-
Token计量、确定性裁剪:不调用模型。
-
默认摘要:额外模型调用,可能多次;不保证复用cache。
-
JSONL的Zstd:磁盘编码,与语义摘要不同。
-
KV cache:前缀计算复用,不负责跨会话知识存储。
切换provider/model也会检查目标压力;只有压力检查或压缩失败等对应 MODEL_SWITCH_COMPACTION_FAILED,不是所有模型准备错误都用这个码。
依据:压力公式与流程、辅助摘要机制、Token meter。
11. Goal、结束 Turn 与自动继续
11.1 本例为什么没有Goal round
R5创建Goal,R13完成Goal,R14结束Turn。中间所有工作仍是直接用户请求的T-main-1。
Goal记录:roundsStarted=0
主模型调用:R1—R14共14次
矛盾吗?不矛盾。
Goal轮数只计被接纳的自动目标轮次,不计所有Step。
11.2 如果模型阶段性停下但Goal尚未完成
假设另一个合法轨迹中,主Agent已经阶段性收敛,Goal还active:
输入:active+armed目标、剩余round预算、Agent及相关progressing后代空闲。
处理:driver排入一个goal-sourced用户输入,包含目标和轮次;经过revision校验与pre-step准入。
输出:新Turn的新请求系列,继续同一个Session。
T-main-1结束
→ 自动Goal round 1输入
→ T-main-2继续
不是fresh子Agent重跑,也不是Loop无条件递归调用模型。
11.3 Jobs也是另一条继续来源
后台完成可在Agent忙时进入后续Step,空闲时默认唤醒新Turn。因此不能把每个新Turn都归因于Goal,也不能保证先等Goal再等job。
11.4 恢复后不会自行重新武装
Session resume/fork后已有active Goal是disarmed,需要人类授权恢复继续。当前模型工具对phase=paused还有额外拒绝,需要用户操作;active-but-disarmed重新武装是另一情况。
11.5 完成判断是什么性质
目标完成由模型在允许的身份下报告,当前driver没有独立evaluator证明全部需求满足。本例把测试、diff、约束检查放在前面,才让complete有根据。
Goal cap不是token、金额或时间预算;难度高或还有有用工作不能随意写blocked。
依据:Goal driver条件、Goal限制、Goal工具paused限制。
12. 失败、取消与不能保证的事情
12.1 模型API失败:Attempt,不是重复用户Prompt
如果R2第一次API attempt失败,可进入有限retry策略。当前Step保留已组装内容与准入工具,不重跑整个pre-step,不把同一U0再录入一次。
失败partial stream通常记录为assistant/attempt用于诊断,不当作成功assistant消息进入后续模型历史。重试可受已落地surface变化影响。
12.2 工具失败:反馈,不一定结束Turn
合法工具调用发生业务错误或命令非零退出,通常返回结构化/文本错误给下一请求;模型可以根据结果修改策略。
这与插件抛出未经处理异常、模型认证失败等不同。不要把所有错误都当成一次工具可自行修复的问题。
12.3 Unknown outcome:不能盲重试
TOOL_NOT_STARTED:没有开始记录
TOOL_OUTCOME_UNKNOWN:有开始记录,无可靠结果
ABORTED_BEFORE_DISPATCH:尚未分发即取消
“删除文件”“发邮件”“支付”结果未知时,应先核验外部状态或用幂等机制,不能像只读grep那样原样重试。
12.4 权限拒绝不是换个写法继续
模型指导、计划批准、子任务描述都不扩大实际文件权限。真实沙箱拒绝应由授权渠道解决,不能换路径、shell或插件偷偷绕开。
子Agent固定scope不能自己提权,必须把限制报告父Agent。
12.5 取消不是回滚
当前合作式工具、进程cleanup和权限层管理停止;已写文件、已发送网络请求和已提交外部动作不会自动撤销。
停止请求接受、工作quiet、持久化完成是不同时间点。
12.6 上下文与记忆也有边界
-
Summary可能遗漏或失真。
-
旧文件观察可能过期。
-
Skill正文不是模型新增权重。
-
删除一份笔记不等于清除日志、fork、spill、引用快照和MCP副本。
-
运行时有日志不意味着断电或存储故障下绝对无损。
-
Sandbox和审批降低风险,不构成完整安全审计证明。
13. 谁负责决策,谁负责确定性处理
|
问题 |
主要负责者 |
背后的思想 |
|---|---|---|
|
Prompt归哪个Session |
UI/Host/Session controller |
身份与执行生命周期明确 |
|
/plan是否选中 |
命令与Plan服务 |
控制信息不靠LLM猜 |
|
当前模型参数是否可用 |
adapter prepareCall |
协议与默认值由所有者校验 |
|
哪些工具可调用 |
registry、scope、条件准入 |
能力与实际执行一致 |
|
读哪些文件 |
LLM提出工具参数 |
灵活探索,但结果要核验 |
|
指导与目录如何加入 |
指令/Skill/context插件 |
来源、预算、刷新责任分离 |
|
是否自动搜其他会话 |
配置及检索消费者 |
不是主循环隐含动作 |
|
具体Plan是什么 |
LLM |
基于证据生成方案 |
|
是否批准实施 |
用户审阅 |
人类取舍与授权 |
|
工具动作可否执行 |
policy、approval、guard、sandbox |
不让语言承诺替代强制约束 |
|
编辑后的正确性 |
测试、复核、验收与模型判断 |
真实证据纠正推断 |
|
是否委派 |
LLM或编排消费者 |
可分任务减少单context负担 |
|
子看到什么 |
seed规则、子scope、prompt、独立注入 |
共享文件不共享脑 |
|
长历史如何处理 |
meter、pruner、compaction |
有界context而非无限记忆 |
|
是否继续新Turn |
inbox、Jobs、Goal等driver |
继续需要具体输入与策略 |
|
最终任务完成 |
模型报告,证据支撑,用户可验收 |
当前无自动全能认证器 |
四个设计思想贯穿全部流程
-
决策和执行分离:LLM生成意图,工具及环境产生真实结果。
-
状态和视图分离:日志保存事实,surface决定当前可见内容。
-
方法和事实分离:Skill教怎么做,read/test告诉现在是什么。
-
局部推进和整体收敛分离:子Agent负责局部,Lead负责最终一致性与验收。
可靠Agent不是“总能第一次猜对”,而是知道当前依据是什么、能安全行动、能听取失败、能恢复推进,并能说明完成的证据与限制。
14. 实现入口与核验来源
下面源码路径均指向本机当前检出,不指向教学项目。日期固定在本文基线;代码升级后应重新核验。
|
要追踪的动作 |
推荐源码/文档 |
|---|---|
|
整体Turn flow |
|
|
Web普通Session命令 |
|
|
普通Prompt Host入口 |
|
|
UI命令接管与Remote执行 |
|
|
/plan与模式服务 |
|
|
claim与工具/prompt准备 |
|
|
Turn循环与结束 |
|
|
prepare、录入、stream |
|
|
工具批次与继续分支 |
|
|
请求快照构造 |
|
|
prompt assembly |
|
|
runtime context协调 |
|
|
指导baseline与预算 |
|
|
Skill目录与正文 |
|
|
工具准入 |
|
|
工具policy顺序 |
|
|
持久与surface差别 |
|
|
正式批准后续Step |
|
|
Spawn初始数据 |
|
|
Fork已完成Turn边界 |
|
|
子权限 |
|
|
消息、停止、wait |
|
|
Bash进程与后台 |
|
|
Job结果与唤醒 |
|
|
Goal自动继续 |
|
|
Goal工具权限 |
|
|
默认FTS关闭 |
|
|
历史查询工具opt-in |
|
|
指定会话快照 |
|
|
可选外接记忆 |
|
|
压缩阈值 |
|
|
默认摘要器 |
|
|
安全与支持边界 |
15. 用 12 句话复述整条流程
-
用户Prompt先进入某个Session与模式控制入口,不是立刻裸调用模型。
-
Harness领取输入,确定模型与当前工具权限。
-
prompt插件组装系统指导,context插件注入环境、适用规则和Skill目录。
-
当前Session通过surface派生历史,默认不是从所有旧聊天做相关性检索。
-
adapter准备成功后,记录模型可见输入并冻结本次请求。
-
LLM根据目标和材料选择读文件、加载方法或问用户,而不是运行时替它写好计划。
-
正式计划审阅由用户批准,下一Step实施;普通问答同意不等于Plan批准。
-
工具执行真实读改测,返回结果进入下一次context。
-
子Agent收到自包含任务和自己的context,不自动共享父全部聊天;结果通过通知回到父。
-
失败和审查意见改变LLM下一步行动,促使修复再验证。
-
长历史可以被裁剪或摘要,原始日志与当前输入仍然是不同层。
-
最终由Lead收集正确版本的证据,关闭目标和进度,解释完成与未覆盖范围,然后收敛。
一个Prompt并没有变成一个神奇的“大Prompt”。它被运行时组织成多次有上下文、有动作、有反馈、有状态和有验收的模型决策。

浙公网安备 33010602011771号