第 3 卷
13子 agent #2(会话 …)
14第 1 步:等模型回话
| 15 | 调工具bash({"command":"find \"…/docs\" -type f | wc -l", …}) |
|---|---|
| 16 | 工具返回:3 |
| 17 | 第2步:等模型回话 |
| 18 | 调工具structured_output({"directory":"docs","exists":true,"fileCount":3,"samp |
| 19 | 工具返回:Structured output recorded. |
20 21--- 编排结果(脚本 return 的值)---
| 22 | workflow"count-src-docs-files"completed (2 agents). |
|---|---|
| 23 | Return value: |
| 24 | { |
| 25 | "perDirectory": [ |
| 26 | {"directory":"src","fileCount": 5 }, |
| 27 | {"directory":"docs","fileCount": 3 } |
| 28 | ], |
| 29 | "totalFiles": 8, |
| 30 | "summary":"src/ has 5 files, docs/ has 3 files, total 8 files." |
| 31 | } |
为省篇幅这里只留关键字段。return 的具体字段由模型写的脚本决定、每次不同,上面是某次示例,常见字段如 fileCount / perDirectory / totalFiles / summary 等。
Workflow 的核心:编排是模型现写的一段脚本,脚本里用 agent() 派活、 parallel()/pipeline() 编排。比如“并发审查这 10 个文件”,它会写 parallel() 一
次派十个;“先审计再修复”,它会写 pipeline() 串起两个阶段。
Ralph 怎么编排:工具写死脚本
Ralph 是另一条路:编排脚本写死在 ralph 工具的源码里(@deepseek-ai/dsh-toolralph 包顶部的 RALPH_SCRIPT 常量),模型改不了它,只能传一个不可变目标
(objective)和轮次上限(maxRounds)。这个脚本每轮把目标交给一个全新子 agent。
注意,Ralph 不是 15 讲的 Goal:Goal 是单个 agent 围绕目标续跑,Ralph 是每轮换一个全新子 agent、跨轮接力。
一家》里那个笨拙却执着的 Ralph Wiggum),核心是“每轮重置上下文、锲而不舍地重跑”。
ralph-demo.ts 给模型的提示词就一句:
复制代码 1请用 ralph 工具:目标是把本次示例整理成一份要点笔记写进工作区;请显式传 maxRounds=2。
模型据此调了 ralph 工具,传的参数长这样,本次传 objective 和 maxRounds:
复制代码 1{
| 2 | "objective":"在工作区写出一份中文要点笔记NOTES.md……", |
|---|---|
| 3 | "maxRounds":2 |
| 4 | } |
工具内部调 workflowEngine.start() 时,引擎实际收到的 request 长这样,其中 script 写死、args 装模型传的东西:
复制代码 1{
| 2 | "script":"RALPH_SCRIPT(写死的循环脚本,见下面⻣架)", |
|---|---|
| 3 | "meta": {"name":"ralph-loop", … }, |
| 4 | "parent":"(当前调用ralph工具的agent)", |
| 5 | "args": {"objective":"在工作区写出一份中文要点笔记NOTES.md……","maxRounds":2,"m |
| 6 | "subagentProvider":"spawn", |
| 7 | "maxTotalAgents":2, |
| 8 | "signal":"(取消信号)" |
| 9 | } |
下面是 RALPH_SCRIPT 的关键⻣架:
复制代码 1*// 写死在 ralph 工具里的循环脚本(RALPH_SCRIPT 关键⻣架)
2const reportSchema = { /* … status: continue|complete|blocked 三选一 */ } 3
| 4 | letprevious | //上一份交接(第一轮是undefined) |
|---|---|---|
| 5 | for(letround =1; round <= args.maxRounds; round++) { | |
| 6 | constprompt = [ | |
| 7 | 'You are one fresh worker … no parent conversation and no prior child session | |
| 8 | 'Immutable objective:\n'+ args.objective, | //模型传的不可变目标 |
| 9 | 'The shared workspace … is the long-term memory and source of truth', | //共享 |
| 10 | 'Previous structured handoff:\n'+ (previous ===undefined?'(none — this is | |
| 11 | // …(其余次要文案省略) | |
| 12 | ].join('\n\n') | |
| 13 | constreport =awaitagent(prompt, {schema: reportSchema }) | // agent()原语:派 |
| 14 | if(report.status==='complete')return{status:'complete',roundsStarted: r | |
| 15 | if(report.status==='blocked')return{status:'blocked',roundsStarted: rou | |
| 16 | // …子agent失败(agent()返回null)→转成round-failed返回(此处省略) | |
| 17 | previous = report | // continue:这份报告作为下一轮的交接 |
| 18 | } | |
| 19 | **return{status:'budget-limited',roundsStarted: args.maxRounds,report: previ |
运行后(示例输出):
复制代码 1--- 模型传了什么(ralph 工具收到的参数)---
2{"objective":"在工作区写出一份中文要点笔记 NOTES.md……","maxRounds":2} 3
| 4 | ---每一轮:开一个全新子agent,它内部发生了什么--- |
|---|---|
| 5 | 第1轮的子agent(会话…) |
| 6 | 第1步:等模型回话 |
| 7 | 调工具bash({"command":"pwd; ls -la; …","description":"Inspect workspace…"}) |
| 8 | 工具返回:…(工作区里还没有NOTES.md) |
| 9 | 第2步:等模型回话 |
| 10 | 调工具bash({"command":"cat > NOTES.md <<'EOF' … EOF", …}) |