- 后端
- 微服务
- AI Agent
- RPC框架
【免费下载链接】go-micro
A Go agent harness and service framework
Go Micro 将"循环"(loop)实现为一个普通 flow 步骤:micro.FlowLoop在有序、可断点续跑(checkpointed)的步骤列表中反复运行一个 body 步骤,直到停止条件触发或达到迭代上限。本文围绕 agent-loops.md 指南,结合 flow/loop.go、flow/loop_test.go 与完整示例 examples/flow-loop/main.go,讲清 Loop 的停止条件、硬性上限、进度观测与持久化语义,并给出可复制运行的代码与改造成真实 Agent 循环的路径。
为什么需要 Loop:从"一问一答"到"持续工作"
大多数 Agent 调用是一次性的:输入一个 prompt,得到一个答案。而智能体(agentic)系统的下一步是循环——让 Agent 反复执行某个步骤,持续工作直到目标达成,而不是一轮就停。典型场景包括:
- 一个 Agent 持续改进架构、另一个删除重复抽象,两者不断提交 PR;
- 一篇文章反复打磨直到"足够好";
- 一个构建反复修复、重跑直到全绿。
循环的代价是成本与失控风险:循环"烧 token 的速度远快于简单的问答式聊天机器人",而"继续干直到完成"这种非确定性的停止条件没有任何天然上限。因此一个可用的循环必须同时具备两样东西:
- 停止条件(stop condition)——决定循环何时算"做完";
- 硬性上限(hard cap)——一个保证循环必然终止的护栏。
Go Micro 把这两者打包成一个 flow 步骤:micro.FlowLoop。
Loop 的形状:一个普通 flow 步骤
micro.FlowLoop是StepFunc,所以它可以像其他任何步骤一样嵌入 flow 的有序步骤列表。它反复运行一个body步骤,把 flow 的State从一轮传递给下一轮,直到停止条件触发或迭代上限被命中——以先到者为准。
f := micro.NewFlow("refactor", micro.FlowProvider("anthropic"), micro.FlowSteps( micro.FlowStep{Name: "improve", Run: micro.FlowLoop( micro.FlowDispatch("coder"), // the body: an agent does one pass micro.FlowUntilLLM("Is the refactor complete with no duplicated abstractions left?"), micro.FlowLoopMax(5), // the ceiling: never more than 5 passes )}, ), )从源码看,micro.FlowLoop只是 micro.go 中对flow.Loop的薄封装,FlowLoopMax/FlowUntil/FlowUntilLLM/FlowOnIteration则分别透传到flow.LoopMax/flow.Until/flow.UntilLLM/flow.OnIteration。真正的引擎在 flow/loop.go:
func Loop(body StepFunc, opts ...LoopOption) StepFunc { o := LoopOptions{Max: 10} // ...选项解析:Max<=0 时重置为 10 return func(ctx context.Context, in State) (State, error) { if body == nil { return in, fmt.Errorf("flow: Loop requires a body step") } cur := in for iter := 1; iter <= o.Max; iter++ { out, err := body(ctx, cur) if err != nil { return cur, fmt.Errorf("loop iteration %d: %w", iter, err) } cur = out if o.OnIter != nil { o.OnIter(iter, cur) } done, err := loopDone(ctx, o, cur, iter) if err != nil { return cur, err } if done { return cur, nil } } return cur, nil } }注意几个实现细节:
- 状态是流动的:
cur = out让每次迭代的输入都是上一轮的输出,因此 body 每一步都能看到前一轮的结果; - body 为 nil 会报错:
flow: Loop requires a body step; - body 出错立即中止:错误会被包装为
loop iteration %d: ...并返回,同时返回当前状态; - 达到上限时返回最新状态而非报错:护栏完成任务后循环"正常退出",调用方拿到的是最近一轮的
State。
停止条件一:代码定义(FlowUntil)
当"完成"可以用代码衡量时(测试通过、分数超过阈值、队列为空),用FlowUntil传入一个谓词。每轮迭代结束后引擎都会调用它,返回true即终止循环。谓词签名是LoopCondition(flow/loop.go):接收context.Context、最新State和刚完成的迭代序号(从 1 开始):
micro.FlowUntil(func(_ context.Context, s micro.FlowState, iter int) (bool, error) { var d Draft _ = s.Scan(&d) return d.Quality >= 90, nil })这里的s.Scan(&d)把 flow 状态解码进一个结构化结构体(状态由State.Set/State.Scan承载)。谓词返回(bool, error),若返回错误则循环以该错误结束。
停止条件二:模型判断(FlowUntilLLM,受监督的 "Ralph" 循环)
当"完成"不好用代码量化时,FlowUntilLLM让 flow 的模型在每轮迭代后判断目标是否达成,得到肯定回答就停止。这正是受监督的 "Ralph" 循环:由 Agent 决定何时完成,而上限仍然保证它必然停止。
micro.FlowUntilLLM("Have all the failing tests been fixed?")该选项要求 flow 配置了模型(FlowProvider/FlowAPIKey)。底层实现见 flow/loop.go 的askDone:它把问题与最新状态拼成 prompt,要求模型"只回答 yes 或 no",然后用isAffirmative判断回答是否为肯定(flow/loop.go)。肯定词前缀匹配yes、done、true、complete、finished(大小写不敏感)。如果 flow 没有模型,会返回错误:flow: UntilLLM requires a flow model (set Provider/APIKey)。
两种停止条件可以组合:loopDone(flow/loop.go)先评估代码谓词,再评估 LLM 判断,任一触发即停止循环。
护栏:FlowLoopMax—— 永远设置的硬上限
FlowLoopMax(n)是循环的天花板。body 最多运行n次,因此即使停止条件永不触发,循环也必然终止。上限被命中时,循环返回最新状态而不是报错——护栏完成了它的职责。文档明确建议:总是设置它。
- 默认值:即使不传
FlowLoopMax,源码中LoopOptions{Max: 10}也会给出默认上限 10;若显式传入n <= 0,会被重置为 10(见 flow/loop.go)。 - 验证:flow/loop_test.go 的
TestLoopMaxCapStops构造了一个永不触发的谓词,配LoopMax(5),断言 body 恰好执行 5 次——这正是"护栏保证终止"的直接测试证据。
预算更紧时,把上限调低,并搭配两类配套机制防止后台循环烧出无上限的账单:
- Agent 防护栏(guardrails):Agent Guardrails 指南 中的
MaxSteps(单次Ask的总工具调用次数上限)、LoopLimit(同一工具同一参数重复调用的上限,默认开启)、ApproveTool(执行前门禁,可用于额度/审批策略)以及WrapTool(执行全生命周期钩子,可做计量与审计); - 付费工具(x402):Payments (x402) 指南 让每个端点成为"付费工具",按调用计费(per-call metering),进一步约束无限扩大的开销。
观察进度:FlowOnIteration
FlowOnIteration在每轮迭代后执行,用于日志记录或持久化摘要,方便观察长时间运行的循环进展:
micro.FlowOnIteration(func(iter int, s micro.FlowState) { log.Printf("pass %d: %s", iter, s.String()) })回调在每轮 body 执行完毕、停止条件评估之前被调用(flow/loop.go),收到从 1 开始的迭代序号与最新状态。测试 flow/loop_test.go 的TestLoopOnIteration验证了回调按[1 2 3]顺序收到每次迭代。
持久化语义:循环是一个单一 flow 步骤
循环作为单个 flow 步骤运行。flow 通过其 Checkpoint 机制 在步骤前后保存循环的结果,恢复(resume)时会重新进入该步骤——所以循环 body 必须是可安全重复执行的(幂等)。
有两个关键边界(在 durability.md 中明确说明):
- 循环内的迭代不会被引擎单独 checkpoint。引擎只保存整个循环步骤的开始/结束状态;
FlowOnIteration只是应用自有的进度记录回调,它不会改变引擎的恢复边界,也不会自动跳过已完成迭代。恢复后,整个循环步骤会从头(或从保存的状态)重新执行。
因此对于长循环,用FlowOnIteration记录进度;对于不可重复的外部副作用(支付、资源开通等),按 durability.md 的提示自行实现幂等或对账,因为这里"没有跨外部服务的精确一次(exactly-once)保证"。
完整可运行示例:离线跑通一个 Loop
仓库自带一个无需 API Key的离线示例 examples/flow-loop/main.go:body 和停止条件都是纯 Go 代码,每次迭代让草稿质量 +30,直到质量达到 90 停止,同时用FlowLoopMax(10)兜底:
func main() { const goodEnough = 90 f := micro.NewFlow("refine", micro.FlowSteps( micro.FlowStep{Name: "improve", Run: micro.FlowLoop( improve, // Stop early once the draft is good enough... micro.FlowUntil(func(_ context.Context, s micro.FlowState, iter int) (bool, error) { var d Draft _ = s.Scan(&d) fmt.Printf(" pass %d → quality %d\n", iter, d.Quality) return d.Quality >= goodEnough, nil }), // ...but never run the body more than 10 times (the ceiling). micro.FlowLoopMax(10), )}, ), micro.FlowDeleteOnSuccess(), ) fmt.Println("refining until quality >=", goodEnough) if err := f.Execute(context.Background(), `{"text":"initial draft","quality":0}`); err != nil { fmt.Println("flow error:", err) return } for _, r := range f.Results() { fmt.Printf("\ndone: %s\n", r.Answer) } }运行方式(从仓库根目录):
go run ./examples/flow-loop/ # refining until quality >= 90 # pass 1 → quality 30 # pass 2 → quality 60 # pass 3 → quality 90 # done: {"text":"draft refined (quality 90)","quality":90}示例中的improve每轮把Draft.Quality增加 30 并更新文本,然后通过in.Set(d)写回状态;第三轮质量达到 90,FlowUntil触发停止。FlowDeleteOnSuccess()表示成功后删除运行 checkpoint(失败运行始终保留)。输出中的done: ...来自f.Results()的Answer字段。
把它改造成真实 Agent 循环只需两步:
- 把 body 从纯函数换成
micro.FlowDispatch("agent")(把状态交给已注册的 Agent 通过 RPC 处理)或micro.FlowLLM(...)(一次增强式 LLM 轮次,服务即工具); - 把停止判断换成
micro.FlowUntilLLM("Is the work complete?")——即文首的受监督 "Ralph" 循环形态。
用测试验证行为
flow/loop_test.go 提供了五个覆盖核心行为的确定性测试,可作为理解语义的参考:
| 测试 | 验证点 |
|---|---|
TestLoopUntil | 代码谓词在n >= 3时停止,输出状态为"3" |
TestLoopMaxCapStops | 谓词永不触发时,上限 5 强制恰好执行 5 次 |
TestLoopOnIteration | 回调依次收到迭代[1 2 3] |
TestLoopBodyError | body 返回的错误(如context.Canceled)向上传播 |
TestIsAffirmative | 模型回答的肯定/否定判定,覆盖yes/DONE/complete/no/空串等 |
可运行:
go test ./flow -run 'TestLoop' -v相关阅读
- Agents and Workflows — flow(预定义路径)与 agent(自主决策)的边界,理解 Loop 所处的编排层;
- Agent Guardrails — 用
MaxSteps/LoopLimit/ApproveTool/WrapTool约束循环能做什么、烧多少; - Plan & Delegate — 把工作拆分给多个 Agent,是 Loop body 内常见的协作模式;
- Durability and Recovery — 断点续跑与恢复边界,理解循环步骤的持久化语义。
核心思路一句话:让 Agent 一直干到完成,但永远给它一个天花板。FlowUntil/FlowUntilLLM决定"何时算完",FlowLoopMax保证"必然终止",FlowOnIteration让长循环可观测,而单步骤 checkpoint 语义提醒你保持 body 可重复执行。
- 后端
- 微服务
- AI Agent
- RPC框架
【免费下载链接】go-micro
A Go agent harness and service framework
相关推荐
ToolJet 接入 Cohere 大模型实战指南:文本生成与 Chat 对话插件的完整配置
ToolJet 接入 Cohere 大模型实战指南:文本生成与 Chat 对话插件的完整配置 本文围绕 ToolJet 官方 Marketplace 中的 Co
后端微服务AI AgentRPC框架RedisDesktopManager Windows版:终极Redis可视化管理的完整指南
RedisDesktopManager Windows版:终极Redis可视化管理的完整指南 你是否还在为Redis命令行操作的繁琐而烦恼?想象一下,当你需要快
后端微服务AI AgentRPC框架Agent Governance Toolkit 实战:用 Agent Control Specification 为 Python 客户支持 Agent 构建全链路 Rego 治理护栏
Agent Governance Toolkit 实战:用 Agent Control Specification 为 Python 客户支持 Agent 构建
人工智能AI AgentAI 安全治理策略引擎Agent 沙箱认证鉴权
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考