Agent-as-Tool:从硬编码工作流到受控并行多Agent的一次实践

0 阅读23分钟

本文基于 AI Mind 项目的真实实现整理。
GitHub:github.com/HWYD/ai-min…
对应代码版本:v0.0.5 - v0.4.1
线上链接:ai.hwyblog.cloud/instant-min…

AI Mind 是一个持续迭代中的 Next.js AI Chat 项目。从最基础的本地聊天开始,逐步加入流式协议、工具调用、MCP、Skill 和 Agent 能力。

如果你对这个项目感兴趣,或者这篇文章对你有一点帮助,也欢迎顺手到 GitHub 帮 AI Mind 点个 Star⭐,这会是对我继续更新很大的鼓励。


多agent面板.png

起点:从固定 Pipeline 到多 Agent 的演进动机

先交代一下背景。AI Mind 是我在做的一个 AI 助手项目,其中有一条链路叫 /delivery-chain——用户输入需求,系统输出一份从需求理解到实现方案到任务拆解到交付评审的完整报告。这条链路后来演变成了一套多 Agent 协作架构,也是本文要讲的核心。

在做 /delivery-chain 之前,项目里已经有一条更成熟的链路 /tasklist——具备持久化、人工审批(Human-in-the-Loop,简称 HITL)、断点续跑,还接了可观测性,出了问题能定位 trace。这条线是稳的,但它是为"版本规划任务拆解"这个特定场景设计的,有它自己的状态机和交互模型。

/delivery-chain 的定位不一样。它是一个公开 demo 入口,要展示"从需求走到交付评审"的端到端能力,但又不能真的改代码、不能读真实项目文件、不能有太重的持久化负担。

第一个版本我用 LangGraph StateGraph 写了一个固定顺序的 workflow:loadContext → Plan → Task → Review → BuildReport。四个节点硬编码串起来,跑完就完了,report 是 run 内的非持久化输出。

这个版本能用,但我越用越觉得它有架构上的局限:

  • 流程是死的,加阶段、调顺序都要改图结构
  • Review 只有一个通用评审视角,无法从风险、边界等多个角度同时审查
  • LLM 在每个 stage 内部有发挥空间,但"做什么、按什么顺序做"完全是代码写死的

我想让 LLM 参与流程编排——决定什么时候调哪个阶段、让多个视角的评审同时进行——但又不想做成那种"Agent 之间无限聊天"的混乱局面。这是一个典型的灵活性 vs 可控性的矛盾。

从 v0.3.5 到 v0.4.1,我用了几个版本逐步回答这个问题。整个演进路线是:固定Pipeline(v0.3.6)→ 进度可视化(v0.3.7)→ 串行Agent-as-Tool(v0.4.0)→ 并行评审+synthesis(v0.4.1)。最终(v0.4.1)的架构如下——先看全貌,后面逐层展开:

/delivery-chain 请求
  -> 加载 demo 上下文
  -> ControlledDeliveryManager
       -> 阶段: plan(串行)
            -> 调用 plan-subagent
            -> 接收 plan artifact
       -> 阶段: task(串行,依赖 plan)
            -> 调用 task-subagent(传入 plan artifact)
            -> 接收 tasks artifact
       -> 阶段: review-group(受控并行,依赖 plan + tasks)
            -> 调用 review-subagent  (通用评审)
            -> 调用 risk-subagent    (风险评审)
            -> 调用 boundary-subagent (边界检查)
            -> 接收 3 份评审结果
       -> 阶段: synthesis
            -> 构建 ReviewBundle
            -> 代码规则优先处理:blocked 判断 / 冲突合并 / 风险排序
            -> LLM 润色最终报告(LLM 失败降级为纯规则输出)
  -> 输出交付链路报告

五个子Agent,两个阶段串行,一个阶段受控并行,最后Manager做综合判断——这就是最终形态。这篇文章讲的就是这个架构是怎么设计出来的,以及为什么这么设计。

现有多Agent模式:为什么我没直接用

在动手之前,我先梳理了一下当时主流的几种多Agent编排模式,想清楚它们各自的问题在哪里。

模式一:固定顺序 Pipeline

这是我最初用的方案。LangGraph StateGraph 串固定节点,边写死,没有分支。

  • 优点:绝对可控,流程100%确定,极易调试,安全边界清晰。
  • 缺点:完全不灵活,流程写死改不了;LLM 没有任何跨阶段决策权;加阶段、调顺序都要改图结构。

这是最"笨"但最可靠的方案,适合流程完全固定的场景,但不是我想要的终点。

模式二:Supervisor + Worker

一个 Supervisor Agent 作为主控,由它根据任务进展动态决定调用哪个 Worker Agent。Worker 之间不直接通信,一切委派通过 Supervisor。LangGraph 的 Supervisor 模式、很多框架的"Manager Agent"都是这个思路。

  • 优点:灵活,LLM 有较大的决策空间,能动态调整流程。
  • 缺点:可控性差。Supervisor 是 LLM,它可能做出错误路由(没plan就直接写代码)、陷入循环(A→B→A→B...)、重复调用。没有代码层硬约束,所有规则都靠 prompt 说,但 prompt 是建议不是护栏。出了问题很难调试——你不知道 Supervisor 为什么做出那个决策。

模式三:自由对话 / Group Chat

AutoGen、CrewAI 支持的模式:多个Agent在群聊里自由发消息,通过 Speaker Selection 决定下谁说话。Agent 之间可以直接对话、辩论、互相批评。

  • 优点:最灵活,最像人类协作,适合头脑风暴和创意生成。
  • 缺点:最不可控。Agent 之间容易陷入无意义的冗长对话循环(行话叫"聊嗨了")——可能为一个细节来回讨论十几轮;几乎没有安全边界;调试极其困难,几十上百轮对话根本不知道问题出在哪。这种模式适合Demo和创意场景,不适合生产环境里需要可靠完成任务的场景。

模式四:Map-Reduce / 并行分发

一个任务分发给多个Worker并行处理,最后汇总。适合子任务完全独立的场景,效率高,但处理不了有依赖关系的顺序流程,也没有编排决策权。

我需要的位置

把这四种模式放在"可控性-灵活性"光谱上看:

完全可控 ──── Pipeline ──── Agent-as-Tool ──── Supervisor ──── Free Chat ──── 完全自由
                                   ▲
                              本文的位置

我需要的是Pipeline和Supervisor之间的位置:

  • 比Pipeline灵活:LLM有一定编排决策权,可以支持多视角并行评审
  • 比Supervisor可控得多:代码层硬护栏兜底,不是靠prompt"说服"模型
  • 支持有控制的并行:不是任意并行,而是在明确的阶段边界内、白名单工具之间的并行

这就是我做Agent-as-Tool的出发点。


Agent-as-Tool:核心架构设计

核心思路一句话:把各个阶段从"固定图节点"变成"注册给Manager的Tool",Manager通过模型原生tool-calling调用它们,但每次调用前必须经过代码层Phase-aware DelegationPolicy校验,并行只在明确允许的Review阶段内发生。

这个设计有五个关键组成部分。

一、子Agent = 结构化Tool,不是自主Agent

我没有造新的Agent runtime,也没引入多Agent框架。plan-subagent、task-subagent、review-subagent、risk-subagent、boundary-subagent就是五个普通的工具定义(项目中所有工具的注册接口,定义名称、输入输出 Schema 和执行函数),用Zod schema定义输入输出,和系统里的"搜索工具"、"文件读取工具"地位完全平等——同一个注册机制,没有特殊通道。模型用原生tool-calling调用它们,不需要新的通信协议。

每个subagent tool有明确的边界定义:

type SubagentToolDefinition = {
    id: 'plan-subagent' | 'task-subagent' | 'review-subagent' | 'risk-subagent' | 'boundary-subagent'
    roleInstruction: string
    inputArtifactKinds: RuntimeArtifactKind[]  // 必须的输入artifact(可选值见下文 RuntimeArtifact)
    outputArtifactKinds: RuntimeArtifactKind[] // 产出的artifact
    allowedTools: string[]                      // 子Agent自己能调的工具
    nonGoals: string[]                          // 明确不能做什么
    runtimeScopes: ['delivery-chain-manager']   // 作用域
}

五个子Agent各自的职责:

  • plan-subagent:理解需求,生成实现方案
  • task-subagent:基于plan拆解具体任务
  • review-subagent:通用评审——检查plan/tasks对齐、任务拆解合理性、漏项、优先级、验收清晰度
  • risk-subagent:风险评审——检查实现复杂度、测试覆盖、tool-calling稳定性、维护成本等,输出high/medium/blocker/low风险等级和缓解建议
  • boundary-subagent:边界检查——检查是否越界触碰DB/HITL/checkpoint/@artifact:///Tasklist Agent等非目标范围,输出passed/needs_review/blocked

这里有几个重要设计:一是nonGoals,不只是告诉它"要做什么",还明确告诉它"不要做什么";二是inputArtifactKinds是代码层硬约束,不是prompt建议——task-subagent必须收到plan artifact才能被调用;三是所有subagent tools通过runtimeScopes做作用域过滤,只在delivery-chain-manager scope内可用,普通聊天场景看不到它们。

二、Phase-aware DelegationPolicy:代码层硬护栏

这是整个架构最关键的部分。先说一下 v0.4.0 的基线状态:五个子 Agent 已按固定顺序串行调用(plan → task → review),不允许任何并行。v0.4.1 在此基础上做了两件事——把并行能力按阶段放开,并加入 Manager synthesis。并行不是全局开关,而是按阶段控制:

const deliveryChainDelegationPolicy = {
    allowedSubagentTools: ['plan-subagent', 'task-subagent', 'review-subagent', 'risk-subagent', 'boundary-subagent'],
    maxToolCalls: 5,                                    // plan+task+3review=5
    allowParallel: false,                               // 全局不允许并行
    allowNestedDelegation: false,                       // 禁止子Agent嵌套调用
    requirePlanBeforeTask: true,
    requireTasksBeforeReview: true,
    rejectUnregisteredTools: true,
    rejectOutOfOrderToolCalls: true,
    // 新增:Review Group 独立控制并行
    reviewGroupPolicy: {
        allowedReviewTools: ['review-subagent', 'risk-subagent', 'boundary-subagent'],
        allowParallelInReview: true,
        maxReviewToolCalls: 3,
    }
}

Manager主循环按phase执行:plan(串行) → task(串行) → review-group(受控并行) → synthesis。

Policy校验规则:

  • Plan/Task阶段:任何parallel tool calls直接fail closed,与v0.4.0一致
  • Review阶段:只允许白名单内三个review-class tools并行,混入plan/task或未注册工具直接fail closed
  • 任何阶段:超过maxToolCalls、嵌套委派、未注册工具、乱序调用——一律fail closed

为什么不直接把全局allowParallel改成true?因为那样会破坏Plan/Task阶段的fail-closed保障。并行能力必须是phase-scoped的,而不是全局放开。这是我刻意的设计选择——宁可加一层ReviewGroupPolicy,也不在全局开关上妥协。

关于fail closed策略: 任何一项不通过,直接fail closed——不执行tool,不把错误返回给LLM让它"纠错",直接返回安全失败信息终止流程。为什么不给LLM改错机会?因为我不想模糊"代码层约束"和"LLM决策"的边界。如果Policy拦住了非法调用却又把错误扔回给LLM让它"纠正",那护栏就变成了建议。MVP阶段,护栏就是护栏,碰了就停。

三、RuntimeArtifact:单次run内的受控流转

多Agent协作的核心问题是信息交接——plan的输出要交给task,plan+tasks要交给三个review并行消费。我设计了RuntimeArtifact作为内部交接载体:

type RuntimeArtifact = {
    id: string
    kind: 'plan' | 'tasks' | 'review' | 'delivery_report'
    title: string
    markdown: string
    source: { subagentId?: string; stage?: string }
    metadata?: Record<string, unknown>  // 用metadata扩展,不新增kind
}

v0.4.1我刻意不新增RuntimeArtifact kind。三个review subagent产出的artifact都用kind: 'review',通过metadata.reviewType区分'general' | 'risk' | 'boundary'。这样不修改schema定义,不引入breaking change。risk-subagent还会在metadata里带severity: 'blocker' | 'high' | 'medium' | 'low' | 'info',boundary-subagent带boundaryStatus: 'passed' | 'needs_review' | 'blocked'blocked: true/false

RuntimeArtifact的设计围绕"三不原则":

不持久化。 Artifact只在单次run的内存中存在,不写数据库,不做跨会话交接。这不是偷懒,是刻意选择。持久化是最昂贵的架构决策——一旦写入数据库,schema迁移、权限控制、数据清理、向后兼容全来了。等真的需要@artifact:// scheme和artifact持久化时,再在RuntimeArtifact基础上演进,而不是一开始就做一个"通用artifact system"。

不暴露给前端。 Artifact不进入message.artifacts,不进入artifact-* stream chunks。它是Agent之间的内部交接语言,不是给用户看的。用户看到的最终Delivery Chain Report是Manager综合后生成的用户友好版本。

不泄露敏感信息。 Artifact里不能有raw prompt、raw response、stack trace、API key、cookie、真实文件路径。子Agent返回的summaryForManager必须是经过脱敏的安全摘要。

Artifact的流转依赖链:

plan-subagent完成 → RuntimeArtifact(kind='plan')
    ↓
task-subagent消费plan → RuntimeArtifact(kind='tasks')
    ↓
review-group并行消费plan+tasks:
    ├── review-subagent → RuntimeArtifact(kind='review', reviewType='general')
    ├── risk-subagent   → RuntimeArtifact(kind='review', reviewType='risk')
    └── boundary-subagent → RuntimeArtifact(kind='review', reviewType='boundary')
    ↓
Manager综合三个review → RuntimeArtifact(kind='delivery_report') → 用户看到报告

四、Tool Result强Schema契约

子Agent不能返回自由格式的Markdown文本,必须返回符合Zod JSON Schema的结构化结果:

type SubagentToolJsonResult = {
    status: 'completed' | 'blocked' | 'failed'
    markdown: string             // 给用户看的内容
    artifactTitle?: string
    warnings: string[]
    summaryForManager: string    // 给Manager的安全摘要(不给用户看)
    metadata?: Record<string, unknown>
}

为什么要强Schema?因为如果返回自由文本,Manager(LLM)需要"理解"文本内容来决定下一步,这又引入了不确定性。强Schema让Manager runtime可以确定性地处理结果——completed就生成artifact,failed就记录,blocked就标记——不需要LLM去"猜"。校验不通过一律按failed处理,不生成artifact。

五、ReviewBundle + Manager Synthesis:规则优先,LLM润色

v0.4.1最有价值的升级是Manager synthesis。三个并行review返回结果后,不是简单把三段markdown拼在一起,而是:

  1. 构造ReviewBundle(run-local内部结构,包含generalReview、riskReview、boundaryReview和failedReviews数组)
  2. 先用代码硬规则处理关键判断,再交给LLM润色
  3. LLM润色失败时,降级为纯规则生成的报告,不fail closed

硬规则包括:

  • boundary-subagent返回blocked → final conclusion = blocked(最高优先级)
  • risk-subagent severity=blocker → 等同于boundary blocked,final conclusion = blocked
  • boundary-subagent failed → final conclusion至少needs_review
  • review-subagent failed → final conclusion = needs_review
  • risk-subagent failed → final report标注"risk review missing"
  • 多个子Agent提到相同问题 → 合并去重
  • 子Agent意见冲突 → 标注冲突并给出综合判断
  • Partial failure:1-2个review failed时synthesis继续,report标注哪些检查缺失;3个全部failed才fail closed

为什么用"规则优先+LLM润色"而不是让LLM自己综合?因为:

  • 纯规则保证安全性和可测试性——blocked判断、风险优先级这些不能靠LLM"发挥"
  • LLM润色提升报告的可读性和自然度,避免报告像机器拼接的
  • 降级机制保证LLM失败时核心流程不受影响——最差情况是输出纯规则生成的朴素报告,但不会崩溃

最终报告结构包含:综合结论、本轮评审覆盖情况、Review总评、风险评估、边界检查、合并后的关键问题、阻塞项/高风险项、建议下一步。

关于并行的设计取舍

v0.4.0是完全串行,v0.4.1只在Review阶段放开并行。为什么不做更激进的并行?

  • Plan和Task有严格依赖链(Task必须消费Plan产物),并行没有意义还增加复杂度
  • 三个评审天然独立(通用质量、技术风险、边界合规),并行能同时从三个视角审查,且有实际价值
  • 限制并行范围可以保持DelegationPolicy的可控性——全局allowParallel保持false,只在ReviewGroupPolicy里放开
  • 不做任意tool并行,不做plan/task阶段并行,不做嵌套并行

这是一个刻意保守的选择。并行是能力,但也是复杂度来源。先在最有价值、依赖最清晰的地方放开,验证稳定后再考虑扩展。


执行流程与安全防御

受控工具调用循环(v0.4.1 最终版)

实际的运行时循环按phase执行:

  1. 初始化:加载 demo 上下文,初始化空artifact集合和ReviewBundle,绑定五个子Agent tools到Manager,初始化Policy状态
  2. Plan phase:Manager决定调用plan-subagent,过Policy校验(必须串行、不能是其他tool),执行,结果过Schema校验,生成plan artifact
  3. Task phase:plan artifact存在后才能进入,Manager调用task-subagent(传入plan artifact),同上校验,生成tasks artifact
  4. Review phase:plan+tasks都存在后才能进入,Manager在一次tool call中发起三个review-class tools的并行调用(如果模型只返回1-2个,缺失的标记为failed),过ReviewGroupPolicy校验(只能是这三个、不能混入其他tool、不能嵌套),并行执行三个tool,结果分别过Schema校验,生成三个review artifact(completed的),构造ReviewBundle
  5. Synthesis phase:基于ReviewBundle执行硬规则处理,再LLM润色,LLM失败则降级纯规则输出,生成delivery_report
  6. 输出最终报告

模型有决策权,但决策权被Policy严格限制在边界内。模型可以决定"我现在要进入review phase并调用三个并行review",但它不能决定"跳过plan直接调review"——Policy会拦住。

部分失败处理

并行场景下partial failure是常态,我做了分级处理:

  • 单个review返回非法JSON:该review标记为failed,其余继续
  • 1个review failed:synthesis继续,report标注对应review missing
  • 2个review failed:synthesis继续,report标注2个review missing
  • 3个review全部failed:fail closed,输出安全失败报告
  • 模型在review phase返回4个以上tool call:fail closed(超过允许的3个)
  • 模型在plan/task phase返回parallel calls:fail closed

这种"容忍部分失败但不容忍全部失败"的策略,比v0.4.0的"任何失败直接fail closed"更务实,也更符合并行执行的实际场景。

七层安全防线

从v0.3.5到v0.4.1,我叠了七层防御:

机制防什么
1. Tool白名单Manager只绑五个subagent tools看不到无关/危险工具
2. Scope过滤runtimeScope限定delivery-chain-manager普通聊天场景不暴露子Agent
3. Phase-aware PolicyDelegationPolicy分阶段控制顺序/依赖/并行范围乱序/重复/非授权并行直接被拦
4. Schema校验Zod强JSON Schema校验tool result格式错误、夹带敏感信息都被拦
5. 规则优先Synthesis硬规则处理blocked/风险/冲突,不依赖LLM判断关键安全判断确定性执行
6. Partial failure降级1-2个失败继续synthesis,LLM润色失败降级纯规则避免单点失败导致整体崩溃
7. 信息脱敏Progress只发安全摘要,不含raw数据用户看不到prompt/response/stack/artifact

我不相信单一防线。七层叠在一起,即使某一层被突破,还有其他层兜着。这是传统安全工程 Defense in Depth 的思路,用在Agent系统里一样有效。

用户体验:进度展示

工作流进度用通用的workflow-progress-* chunk协议,v0.4.1的step序列是:

load → delegate-plan → delegate-task → delegate-review-group → synthesize-report

注意review阶段不是三个step,而是一个汇总step delegate-review-group——并行评审的进度用单step汇总展示,summary是安全描述("已启动并行评审"),不暴露raw invocation、raw result、RuntimeArtifact。不新增stream chunk类型,不修改frontend reducer public shape。完成后所有step自动折叠成一行"已处理XmXXs"。


Agent-as-Tool的优缺点与适用场景

对比其他模式的优缺点

经过v0.3.5到v0.4.1的实际实现和使用,我对Agent-as-Tool的优缺点有比较清晰的认识:

相比固定Pipeline:

  • 优点:LLM有编排决策权(在Policy边界内);支持多视角并行评审;新增子Agent不需要改图结构,只需注册tool并更新Policy;synthesis规则可以独立演进
  • 缺点:比Pipeline复杂,需要维护Policy、Schema、ReviewBundle;理论上有LLM决策在边界内出错的可能(但Policy兜底)

相比Supervisor模式:

  • 优点:代码层Policy强制约束,不依赖prompt"说服"模型;phase-aware并行控制,不会全局放开;乱序/非授权并行/超调用直接被拦,不会无限循环;子Agent是强Schema Tool,边界清晰;关键判断用硬规则不依赖LLM;调试更简单——tool call边界明确
  • 缺点:灵活性不如纯Supervisor,Policy限制了模型的决策空间;需要upfront定义Schema和Policy,前期设计成本稍高;并行是phase-scoped的,不是任意并行

相比自由对话/Group Chat:

  • 优点:可控性高几个数量级;安全边界清晰;不会无限聊天;支持受控并行而不混乱;调试可追踪;partial failure有降级策略
  • 缺点:不适合创意生成和开放式探索;不支持Agent之间对等协商辩论;流程相对刚性

什么时候应该用Agent-as-Tool?

  1. 企业级/生产级AI应用:有严格安全要求、不能出错、需要可审计的场景。出错代价高,需要系统行为在可预测范围内。
  2. 流程有大致阶段但允许细节决策:有明确阶段划分,阶段间有依赖关系,但允许模型在阶段内自主决策,未来可以增减阶段或调整评审视角。
  3. 需要多视角并行审查:同一组产物需要从不同独立视角同时评审(比如质量、风险、合规),并行能提升效率和覆盖度。
  4. 步骤间有明确依赖关系:DAG式的依赖(B必须等A的输出,C必须等A+B的输出),用RuntimeArtifact和Policy可以很好表达。
  5. 需要可观测可调试:出了问题能快速定位——是Policy拦了?是哪个subagent失败了?是Schema校验没过?边界清晰。

什么时候不应该用?

  1. 开放式创意生成:头脑风暴、写小说、辩论——需要Agent自由发挥、碰撞想法,护栏太多会扼杀创意。用自由对话模式更好。
  2. 流程完全不确定:你甚至不知道需要哪些步骤,需要Agent自己探索"该做什么",Agent-as-Tool的固定tool集合和Policy太死板。
  3. Hackathon/快速原型:想快速验证想法,固定Pipeline或CrewAI更快。Agent-as-Tool需要定义Schema、写Policy、做边界,这些工程化工作在原型阶段是负担。
  4. Agent对等协商:多Agent站在不同立场辩论、讨价还价,星型Manager结构不支持对等交互。

刻意不做的事

从v0.4.0到v0.4.1,我始终坚持一个很长的Non-goals清单:

  • 不做全局Agent Catalog:不需要一个中心化的 Agent 注册中心让用户自由注册、发现和组合 Agent,子 Agent 始终由 Manager 内部控制
  • 不做subagent-to-subagent直接通信:所有交接通过Manager和RuntimeArtifact
  • 不做nested subagent:子Agent不能调子Agent,避免委派链条失控
  • 不做任意并行:只有review阶段三个白名单tool允许并行
  • 不做HITL/checkpoint/resume:不接PostgresSaver,保持/delivery-chain轻量(HITL是后续版本的事,但复杂度太高,v0.4.1不碰)
  • 不做@artifact://持久化:不提前做通用artifact系统
  • 不做用户可选subagent picker:用户不需要也不应该选择调用哪些子Agent
  • 不保留旧graph fallback:不做双主控
  • 不新增RuntimeArtifact kind:用metadata扩展而非新kind
  • 不修改stream-core/frontend reducer:Public surface完全不变
  • 模型不支持tool-calling/parallel tool-calling直接fail closed:不降级回旧runner

**知道不做什么比知道做什么更难。**每加一个功能,架构复杂度就上一个台阶。我只做最小必要集合。


一些工程复盘

做完这轮从v0.3.5到v0.4.1的重构,有一些体会。

护栏比引导可靠。 不要试图用几百字prompt"引导"模型做正确的事。模型是概率性的,总有概率忽略指令。用代码层白名单、Schema校验、Policy约束确保它不能做错事,比希望它"会"做对事靠谱得多。Fail Closed不是保守,是负责任。

并行要phase-scoped,不要全局放开。 一开始我想过直接把allowParallel改成true,后来意识到这会破坏Plan/Task阶段的安全性。最终的方案是全局保持串行,只在ReviewGroupPolicy里独立控制并行——这个设计虽然多了一层抽象,但安全性有保障。

规则优先,LLM润色。 涉及blocked判断、风险等级、冲突合并这些关键决策,用代码硬规则保证确定性;自然语言生成、报告可读性这些交给LLM。LLM润色失败有降级,不影响核心流程。这比"让LLM做所有事"靠谱得多。

Partial failure是并行的常态。 串行场景下一个环节失败整个流程失败是合理的,但并行场景下不能因为一个review失败就丢掉另外两个有效的结果。分级处理:部分失败标注缺失继续,全部失败才终止。

公开接口是负债。 从v0.3.5到v0.4.1,我始终只暴露/delivery-chain一个命令。五个子Agent是内部概念,用户看不到。v0.4.1做了并行评审和synthesis升级,stream protocol和frontend reducer完全没变,用户几乎感知不到内部重构。

非持久化原则。 RuntimeArtifact只存在于内存,不写数据库。这不是偷工减料,是刻意选择。持久化是最昂贵的架构决策,等真的需要时再加,不提前做"通用系统"。

用metadata扩展,不要新增kind。 risk和boundary review没有新增RuntimeArtifact kind,而是复用kind:'review'+metadata区分。这避免了schema膨胀,也为未来扩展留了空间。

渐进式演进优于大爆炸重写。 固定Pipeline(v0.3.6)→进度可视化(v0.3.7)→串行Agent-as-Tool(v0.4.0)→并行评审+synthesis(v0.4.1),四个版本每一步用户都几乎无感知。不要等"完美架构"想清楚再动手,先做能跑的版本,再在稳定基础上逐步重构。好的架构是演进出的,不是设计出来的。

测试要用fake model。 从v0.4.0到v0.4.1,Manager测试始终用fake tool-call model,不依赖真实provider。真实模型的tool-calling输出不稳定,contract test必须确定性。partial failure组合我只测关键路径(全成功、1个失败、boundary blocked、全失败),不测全部27种组合——避免测试爆炸。

克制是工程能力。 Non-goals清单上的每一条都是我主动放弃的。AI领域每天有新框架、新demo刷屏,忍住不加功能比加功能更难。


最后

这篇文章没有提出什么革命性的新概念。Agent-as-Tool本质上是把传统软件工程里"接口定义"、"契约编程"、"权限控制"、"分层防御"、"降级策略"这些老思路,用到了多Agent系统设计上。子Agent就是有强Schema的工具,DelegationPolicy就是代码层权限检查,RuntimeArtifact就是类型安全的内部数据传递,Synthesis硬规则就是业务逻辑不能交给LLM的体现。

AI Agent领域很热闹,每天都有"自主Agent完成复杂任务"的demo。但Demo和生产系统是两回事。真要让用户用,需要它可靠、可控、出了问题能定位、部分失败能降级。

给Agent套上护栏,不是限制它的能力,而是让它的能力真正可用。并行能力不是放开就好,要在明确的边界内、在最有价值的地方放开。关键判断不要交给LLM,代码硬规则兜底,LLM负责它擅长的事——自然语言生成和润色。

这就是我从v0.3.5到v0.4.1这次重构的全部思考。

项目地址

👉 GitHub:github.com/HWYD/ai-min…

👉 线上体验:ai.hwyblog.cloud/instant-min…

如果这篇文章或者 AI Mind 项目对你有所帮助,也欢迎顺手帮项目点个 Star⭐。这个支持对我来说很重要,也会让我更有动力继续整理后续版本的实现过程、设计取舍和踩坑复盘。