Mastra 中的 Ralph Wiggum 自主循环:从 Ralph Plan 交互规划到 Completion Scorer 落地实践
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
本文以 Mastra 仓库中的
ralph-plan技能为骨架,系统讲解如何通过协作式对话把模糊目标打磨成结构化的 ralph-loop 命令(background / setup / tasks / testing 四段式),并结合仓库中的探索文档与核心源码,深入解析该模式在 Mastra Agent Network 中的工程化落地:即把"完成度判定"统一建模为返回 0/1 的 Completion Scorer,让 Agent 在外部校验(测试通过、构建成功)的驱动下自主迭代直到任务真正完成。读完本文,你将掌握 ralph 命令的构建方法、交互式规划的提问策略,以及如何用createScorer+completion配置写出可运行的自主循环代码。
Ralph Loop:让 Agent 反复失败直到成功
Ralph Wiggum Loop 是仓库 explorations/ralph-wiggum-loop-integration.md 中定义的自主 Agent 执行模式:AI Agent 持续、迭代地工作,直到满足明确的完成标准才停止。其核心哲学是"让 Agent 反复失败,直到它成功"(Let the agent fail repeatedly until it succeeds)。
这一模式的关键特征包括:
- 持久迭代(Persistent Iteration):Agent 持续循环执行,而不是一次生成就结束;
- 上下文保留(Context Preservation):每一轮迭代都能看到之前运行的结果,失败信息成为下一轮的输入;
- 明确的完成标准(Completion Criteria):有清晰的成败度量,例如"测试通过""构建成功";
- 安全控制(Safety Controls):最大迭代次数、超时限制,防止无限循环;
- 失败即数据(Failure as Data):每一次失败尝试都为下一次迭代提供修正依据。
在 Mastra 中,ralph-plan技能(.claude/skills/ralph-plan/SKILL.md)正是为这个循环生成"命令"的规划助手:它不直接执行任务,而是通过与用户的多轮协作对话,产出一个聚焦、可执行、分节清晰的 ralph-loop 命令。
Ralph 命令的四段式结构
一份完整的 ralph 命令由四个明确的 XML 小节构成,外加一个完成承诺标记:
<background> Context about the task, the user's expertise level, and overall goal. </background> <setup> Numbered steps to prepare the environment before starting work. Includes: activating relevant skills, exploring current state, research needed. </setup> <tasks> Numbered list of specific, actionable tasks to complete. Tasks should be concrete and verifiable. </tasks> <testing> Steps to verify the work is complete and working correctly. Includes: build commands, how to run/test, validation steps. </testing> Output <promise>COMPLETE</promise> when all tasks are done.四个小节的分工如下:
| 小节 | 作用 | 关键内容 |
|---|---|---|
<background> | 提供任务上下文 | 任务背景、用户的专业水平、总体目标 |
<setup> | 准备工作清单 | 编号步骤:激活相关技能、探索当前状态、需要做的研究 |
<tasks> | 具体可执行任务 | 编号列表,具体且可验证 |
<testing> | 完成验证步骤 | 构建命令、运行/测试方式、校验步骤 |
<promise>COMPLETE</promise> | 完成承诺 | 所有任务完成后输出 |
交互式规划五步流程
ralph-plan的核心不是一次性生成命令,而是通过协作式对话逐节打磨。规划过程分为五个步骤:
Step 1:理解目标(Understand the Goal)
首先向用户确认:
- 高层目标是什么?(What is the high-level goal?)
- 这涉及代码库的哪个区域?(What area of the codebase does this involve?)
- 有没有约束或要求?(Are there any constraints or requirements?)
Step 2:定义背景(Define Background)
帮助用户建立<background>小节:
- Agent 应该扮演什么专家/角色(persona)?
- 用一句话概括核心目标是什么?
Step 3:规划 Setup 步骤(Plan Setup Steps)
确定<setup>小节的内容:
- 需要哪些技能或工具?
- 需要先做什么探索/研究?
- 需要什么环境准备?
Step 4:拆解任务(Break Down Tasks)
与用户一起:
- 把目标拆成具体的、编号的任务;
- 确保任务具体且可验证(verifiable);
- 按依赖关系排序(先做依赖项);
- 在合适的地方补充实现细节。
Step 5:定义测试(Define Testing)
确立<testing>小节:
- 如何构建/编译改动?
- 如何运行并验证成果?
- 成功是什么样子?
九条规划准则:从"模糊描述"到"可执行命令"
ralph-plan技能明确给出了九条准则,它们是保证命令质量的关键:
- 保持追问(Be Inquisitive):主动深挖细节,就实现细节、边界情况、假设连续追问。不要接受模糊描述——深入下去直到清晰为止。
- 识别缺口(Identify Gaps):主动指出任何缺失、含糊、或日后可能引发问题的点。技能文档给出了三个典型追问范例:
- "你提到了创建 endpoint,但还没有指定请求/响应格式——它应该长什么样?"
- "这个任务依赖对 X 工作原理的理解,但目前没有对应的研究步骤——我们要加一个吗?"
- "如果处理器抛出错误会怎样?UI 应该处理这种情况吗?"
- 研究代码库(Research the Codebase):不要只问用户——主动探索代码库填补知识空白。例如用户说"添加一个像 tools tab 那样的 tab",就去搜索并阅读 tools 的实现,理解其中的模式、文件结构和约定。研究结果用于:在任务中给出具体的文件路径和函数名、找出可遵循的既有模式、发现需要一并修改的依赖代码、给出具体的实现细节而非模糊指令。
- 保持迭代(Be Iterative):不要立即产出完整命令。多提问、讨论选项、逐步细化。
- 具体化(Be Specific):模糊的任务会造成混乱。技能文档给出了正反示例:
- 差:"Improve the UI"(改进 UI)
- 好:"Create a '/processors' endpoint that lists processors, mimicking the '/tools' endpoint"(创建一个列出处理器的
/processorsendpoint,仿照/toolsendpoint)
- 包含上下文(Include Context):Setup 步骤应包含研究/探索环节,以理解既有代码。
- 参考既有模式(Reference Existing Patterns):尽量指向已有的相似实现,供 Agent 遵循。
- 考虑依赖(Consider Dependencies):任务排序要让依赖项先完成。
- 控制范围(Keep Scope Focused):一份 ralph 命令应有清晰、可达成的范围。若范围过大,建议拆成多条 ralph 命令。
示例对话流:把想法收敛成计划
技能文档给出了一个典型的多轮对话节奏,规划助手始终遵循"先问清、再起草、后确认"的循环:
用户:I want to add a new feature to the playground(我想给 playground 加一个新功能)
助手:让我们来规划一下。你能告诉我更多信息吗:
- 你要加什么功能?
- 它影响 playground 的哪一部分?
- 有没有类似的功能可以参考其实现模式?
用户:[提供细节]
助手:明白了。让我先起草 background 小节:
<background> [Draft background based on discussion] </background>这个描述准确抓住了目标吗?需要调整哪里吗?
[然后逐个小节继续迭代……]
注意对话的节奏:先问开放性问题收集信息,每起草一个小节就立即请求用户确认,得到反馈后再进入下一个小节,直到四个小节全部收敛。
输出格式与关键注意事项
当计划最终确定后,把完整的 ralph 命令放进一个代码块,方便用户直接复制:
<background> ... </background> <setup> ... </setup> <tasks> ... </tasks> <testing> ... </testing> Output <promise>COMPLETE</promise> when all tasks are done.重要:输出 ralph 命令时要避免使用双引号(")和反引号(`)字符,因为它们会在命令被复制执行时干扰格式化。改用单引号('),或者改写措辞彻底避免引号。
落地一:Completion 就是返回 0/1 的 Scorer
ralph-plan负责规划,而规划出的"测试通过才算完成"这一逻辑在 Mastra 中由完成度评分器(Completion Scorer)落地。仓库探索文档 explorations/ralph-wiggum-loop-integration.md 给出了一个关键洞察:
Completion checks are justMastraScorersthat return 0 (not complete) or 1 (complete).
这统一了两件事:Evals(离线评测)与Completion(运行时循环控制)。同一个原语,不同上下文。
这个设计在核心源码中已经实现。查看 packages/core/src/loop/network/validation.ts 顶部的注释:
/** * Network Completion Scorers * * Completion checks are just MastraScorers that return 0 (failed) or 1 (passed). * This unifies completion checking with the evaluation system. */CompletionConfig 完整配置
packages/core/src/loop/network/validation.ts 中定义了CompletionConfig,各字段与默认值如下:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
scorers | MastraScorer[] | 无(使用默认 LLM 检查) | 用于判定任务是否完成的评分器,每个返回 0(未完成)或 1(完成) |
strategy | 'all' \| 'any' | 'all' | 'all'表示所有评分器都必须通过,'any'表示至少一个通过即可 |
timeout | number | 600000(10 分钟) | 所有评分器的总超时时间(毫秒) |
parallel | boolean | true | 是否并行运行评分器 |
onComplete | (result: CompletionRunResult) => void | — | 评分完成后回调 |
suppressFeedback | boolean | false | 为true时,#### Completion Check Results反馈消息不会写入 memory,避免污染后续迭代的对话历史 |
从源码结构看,runCompletionScorers(validation.ts#L238-L306)是执行核心:并行模式下用Promise.race对全部评分器与超时做竞争,超时后通过Promise.allSettled收集已完成的评分结果并置timedOut: true;串行模式下则在'all'策略遇到失败、'any'策略遇到成功时提前短路。最终complete的判定为:'all'时要求结果数与评分器数相等且全部通过,'any'时只要有一个通过。
CompletionContext:评分器能看到的网络状态
评分器运行时通过run.input拿到完整的CompletionContext(validation.ts#L46-L74),包含:
iteration:当前迭代号(从 1 开始)maxIterations:允许的最大迭代数messages:对话线程中的全部消息originalTask:发起本次网络运行的原始任务selectedPrimitive:本轮选中的原语({ id, type: 'agent' | 'workflow' | 'tool' | 'none' })primitivePrompt/primitiveResult:发给原语的输入及其执行结果networkName、runId、threadId、resourceIdcustomContext:来自请求的自定义上下文
落地二:在 Agent 上配置完成度评分器
规划阶段的"测试通过才算完成"可以在 Agent 定义时直接声明为默认网络配置。以下代码继承自探索文档 ralph-wiggum-loop-integration.md 的 Quick Start,并结合源码类型补全了上下文:
import { Agent } from '@mastra/core/agent'; import { Memory } from '@mastra/memory'; import { createScorer } from '@mastra/core/evals'; import { execSync } from 'child_process'; // 创建完成度评分器:跑测试,全过返回 1,否则返回 0 const testsScorer = createScorer({ id: 'tests', description: 'Run unit tests to verify code works', }).generateScore(async ({ run }) => { try { execSync('npm test', { stdio: 'pipe' }); return 1; // Tests passed } catch { return 0; // Tests failed } }); const agent = new Agent({ id: 'code-migrator', instructions: 'You help migrate code between frameworks.', model: openai('gpt-4o'), memory: new Memory(), agents: { coder: codingAgent }, // 默认网络选项:循环最多 20 轮,全部评分器通过才算完成 defaultNetworkOptions: { maxSteps: 20, completion: { scorers: [testsScorer], strategy: 'all', }, }, }); // 运行网络 —— 使用默认完成度评分器 const result = await agent.network('Migrate all tests from Jest to Vitest'); for await (const chunk of result.fullStream) { if (chunk.type === 'network-validation-end') { console.log(`Completion: ${chunk.payload.passed ? '✅' : '❌'}`); } }completion配置也可以按次调用时传入,覆盖 Agent 默认值(packages/core/src/agent/agent.types.ts#L449-L473 中NetworkOptions.completion的文档即注明:"Uses MastraScorers that return 0 (not complete) or 1 (complete). By default, the LLM evaluates completion.")。
落地三:三种完成度评分器写法
1. 代码型评分器:访问完整网络状态
评分器通过run对象拿到run.input(即CompletionContext)、run.output(被评估的原语结果)、run.runId与run.requestContext。下面示例来自探索文档,展示如何读取迭代号、原始任务、选中原语和消息历史:
import { createScorer } from '@mastra/core/evals'; // 简单评分器:跑测试 const testsScorer = createScorer({ id: 'tests', description: 'Run unit tests', }).generateScore(async () => { try { execSync('npm test', { stdio: 'pipe' }); return 1; } catch { return 0; } }); // 上下文感知评分器:读取完整网络状态 const progressScorer = createScorer({ id: 'progress', description: 'Check progress', }).generateScore(async ({ run }) => { // run.input 即 CompletionContext const ctx = run.input; console.log(`Iteration: ${ctx.iteration}`); console.log(`Task: ${ctx.originalTask}`); console.log(`Primitive: ${ctx.selectedPrimitive.id}`); console.log(`Result: ${run.output}`); // 等价于 ctx.primitiveResult // 检查消息历史中是否已出现代码块 const hasCodeOutput = ctx.messages.some(m => m.content?.includes?.('```')); return hasCodeOutput ? 1 : 0; }); // 使用自定义请求上下文(requestContext) const envScorer = createScorer({ id: 'env-check', description: 'Environment-aware check', }).generateScore(async ({ run }) => { const isProd = run.requestContext?.env === 'production'; // 生产环境执行更严格的检查 return isProd ? runStrictChecks() : 1; });2. LLM 型评分器:LLM 当裁判
当需要在代码校验之外叠加 LLM 评估时,可以在createScorer配置中传入judge(LLM-as-judge)。这在源码 packages/core/src/evals/base.ts 的ScorerJudgeConfig中有完整类型定义:judge可配置model、instructions,还可选配tools(让内部 judge Agent 在打分前调用只读工具独立核验)、memory、maxSteps、inputProcessors/outputProcessors等:
const taskCompleteScorer = createScorer({ id: 'task-complete', description: 'LLM evaluates if task is complete', judge: { model: openai('gpt-4o-mini'), instructions: 'You evaluate task completion.', }, }).generateScore({ description: 'Evaluate if the task is complete', createPrompt: ({ run }) => { const ctx = run.input; // CompletionContext return ` Original task: ${ctx.originalTask} Latest result: ${ctx.primitiveResult} Is this task complete? Return 1 if yes, 0 if no. `; }, }); // LLM 评分器与代码评分器组合使用 completion: { scorers: [taskCompleteScorer, testsScorer], }3. 默认 LLM 完成度检查(未配置 scorers 时)
当不配置任何scorers时,网络循环会使用内置的默认 LLM 检查。源码 validation.ts#L395-L529 中的runDefaultCompletionCheck用结构化输出约束模型返回{ isComplete, completionReason, finalResult },并通过scorerId: 'default-completion'标记。它只回答"是否完成",不负责生成最终结果——最终结果来自原语的输出。
落地四:完成度检查的运行机制
完成度检查只回答一个问题:"这算完成了吗?(Is this done?)",它不生成最终结果。最终结果始终是原语的输出。
探索文档中的对照表:
| 配置 | 实际运行内容 |
|---|---|
无completion.scorers | 默认 LLM 检查 |
配置了completion.scorers: [...] | 使用你自己的评分器 |
所有检查(默认 LLM 或自定义评分器)都返回统一结构:
{ complete: boolean, // 任务完成了吗? completionReason: string, // 为什么? }从源码看,CompletionRunResult(validation.ts#L161-L172)还额外携带scorers(每个评分器的得分、reason、耗时)、totalDuration与timedOut标记。评分器自身抛异常时会被捕获并记为score: 0、errored: true(validation.ts#L96-L102),这与"合法地评 0 分"可区分,便于消费方(如 goal step 暂停目标)据此做出不同反应。
整个网络循环的流程如下:
┌─────────────────────────────────────────────────────────────────┐ │ Agent Network Loop │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. Routing Agent 选择原语(agent/workflow/tool) │ │ ↓ │ │ 2. 执行选中的原语 │ │ ↓ │ │ 3. 运行完成度评分器 │ │ (未配置时使用默认 LLM 评分器) │ │ ↓ │ │ ┌───────┴───────┐ │ │ Score=0 Score=1 │ │ (未完成) (完成) │ │ │ │ │ │ ▼ ▼ │ │ 4. 注入反馈 5. 完成 ✅ │ │ │ │ │ └──────────► 回到第 1 步 │ │ │ └─────────────────────────────────────────────────────────────────┘从 packages/core/src/loop/network/index.ts 的实现看,循环由 routing step 与各原语执行 step 组成:routing step 让路由 Agent 输出{ primitiveId, primitiveType, prompt, selectionReason },当primitiveId === 'none'时视为完成;完成度判定则统一交由 validation 模块的评分器处理。评分结果会通过formatCompletionFeedback(validation.ts#L335-L365)格式化为#### Completion Check Results反馈消息注入下一轮迭代。
落地五:流式事件观测循环进度
网络循环把每个阶段以流式 chunk 暴露给调用方。NetworkChunkType(packages/core/src/stream/types.ts#L831-L858)中与循环控制相关的事件包括routing-agent-start、routing-agent-text-delta、agent-execution-start/end、workflow-execution-start/end、tool-execution-start/end,以及专门用于完成度评分的network-validation-start/network-validation-end和结构化输出的network-object/network-object-result。
探索文档给出了消费这些事件、实时观测循环的示例:
for await (const chunk of result.fullStream) { switch (chunk.type) { case 'network-validation-start': console.log(`Running ${chunk.payload.checksCount} scorers...`); break; case 'network-validation-end': console.log(`Complete: ${chunk.payload.passed ? '✅' : '❌'}`); for (const scorer of chunk.payload.results) { console.log(` ${scorer.scorerName}: ${scorer.score}`); } break; case 'routing-agent-start': console.log(`Iteration ${chunk.payload.inputData.iteration}`); break; } }落地六:从规划到代码 —— 自主循环的参考实现
仓库 explorations/ralph-wiggum-loop-prototype.ts 提供了一份更底层的参考实现,展示了不用 Agent Network、直接用单 Agent + 循环实现 Ralph Wiggum 模式的思路(注意:该文件为仓库中的探索原型,标注了设计意图,非核心包正式 API)。其核心executeAutonomousLoop(agent, config)的流程是:
- 按
maxIterations循环调用agent.generate(contextualPrompt); - 每轮把前
contextWindow(默认 5 次)迭代的结果(成功/失败、输出摘要、错误信息)拼进上下文,让 Agent 从历史失败中学习; - 通过
completion.check()(如testsPassing('npm test')、buildSucceeds('npm run build')、lintClean('npm run lint'))做外部校验; - 支持
maxTokens预算、iterationDelay间隔、onIterationStart/onIteration回调; - 成功或到达
maxIterations时返回AutonomousLoopResult(含迭代明细、总 token、总耗时)。
探索文档 agent-network-vs-ralph-wiggum.md 对两种模式做了对照分析:Agent Network 的优势是"路由 + 多原语编排",弱点是完成判定依赖 LLM 自评(可能幻觉);Ralph Wiggum 的优势是"程序化外部验证",弱点是单 Agent、没有路由智能。文档的结论是二者互补,并给出了统一自主循环的桥接设想(verify/override/llm-only三种校验模式)。而在 explorations/network-validation-bridge.ts 中,这一设想被实现为可运行的桥接参考:networkWithValidation每轮执行网络后运行校验检查,按mode决定是否采纳 LLM 的完成判断,失败时把校验错误作为[VALIDATION FEEDBACK FROM PREVIOUS ITERATION]消息注入下一轮;同时提供createValidationTools(),把runTests/runBuild/runLint/checkTypes/runCommand封装成路由 Agent 可直接调用的工具。
完整用法模式汇总
探索文档归纳了四种典型用法:
1. 默认:仅 LLM 完成度检查
// 不配 scorers —— 使用内置 LLM 评估 await agent.network('Build a landing page');2. 仅代码评分器(完全替代 LLM)
// 评分器完全替代默认 LLM 检查 await agent.network('Migrate to Vitest', { completion: { scorers: [testsScorer, buildScorer], }, });3. 混合评分器
await agent.network('Build API', { completion: { scorers: [ testsScorer, // 代码型 qualityScorer, // LLM 型 apiScorer, // 代码型 ], strategy: 'all', }, });4. 任一通过策略
await agent.network('Fix the bug', { completion: { scorers: [testsScorer, manualApprovalScorer], strategy: 'any', // 测试通过 或 人工审批 任一即可 }, });使用 Scorer 的收益
综合探索文档与源码实现,把完成度判定统一为 Scorer 带来五点收益:
- 统一原语(Unified Primitives):Evals 与 completion 使用同一个
createScorerAPI(见 packages/core/src/evals/base.ts#L1869-L1897 的createScorer定义与MastraScorer类); - 可复用(Reusable):评测用的 scorer 可以直接当完成度检查用;
- 可组合(Composable):代码型与 LLM 型 scorer 可自由混合;
- 可观测(Observable):
ScorerResult携带score、passed、reason、duration,配合network-validation-start/end流式事件可全程观测; - 可测试(Testable):每个 scorer 都可以独立测试。
结语
从.claude/skills/ralph-plan/SKILL.md的交互式规划,到explorations目录中的模式设计文档,再到packages/core/src/loop/network/validation.ts中的正式实现,Mastra 中形成了一条完整的"规划 → 编码 → 验证"链路:先用ralph-plan的追问式对话把模糊目标收敛成带background / setup / tasks / testing的结构化命令,再把命令中的"完成标准"翻译为返回 0/1 的 Completion Scorer,最后让 Agent Network 在外部校验的驱动下自主迭代。这种"让 Agent 反复失败直到成功"的模式,特别适合测试可自动验证的机械性改造任务——而"完成与否"的判定,始终掌握在确定性的代码手里,而不是 LLM 的自我感觉里。
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考