多 Agent 系统 (Multi-Agent System)
多 Agent 系统是 trpc-agent-go 框架的核心功能之一,允许您创建由多个专门化 Agent 组成的复杂系统。这些 Agent 可以以不同的方式协作,实现从简单到复杂的各种应用场景。
概述
多 Agent 系统基于 SubAgent 概念构建,通过 WithSubAgents option 实现各种协作模式:
基础概念
SubAgent - 通过 WithSubAgents option 配置的专门化 Agent,是构建复杂协作模式的基础
核心协作模式
链式 Agent (ChainAgent) - 使用 SubAgent 按顺序执行,形成处理流水线
并行 Agent (ParallelAgent) - 使用 SubAgent 同时处理同一输入的不同方面
循环 Agent (CycleAgent) - 使用 SubAgent 在循环中迭代,直到满足特定条件
辅助功能
Agent 工具 (AgentTool) - 将 Agent 包装成工具,供其他 Agent 调用
Agent 委托 (Agent Transfer) - 通过 transfer_to_agent 工具实现 Agent 间的任务委托
等待用户回复路由 - 让 Agent 在需要补充信息时显式接管下一条用户消息
Team - 更高层的团队编排封装,支持协调者团队与 Swarm(见 team 包)
SubAgent 基础
SubAgent 是多 Agent 系统的核心概念,通过 WithSubAgents option 实现。它允许您将多个专门化的 Agent 组合在一起,构建复杂的协作模式。
SubAgent 的作用
专业化分工 :每个 SubAgent 专注于特定领域或任务类型
模块化设计 :将复杂系统分解为可管理的组件
灵活组合 :可以根据需要组合不同的 SubAgent
统一接口 :所有协作模式都基于相同的 WithSubAgents 机制
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
)
// 创建 SubAgent
mathAgent := llmagent . New (
"math-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "处理数学计算和数值问题" ),
llmagent . WithInstruction ( "你是数学专家,专注于数学运算和数值推理..." ),
)
weatherAgent := llmagent . New (
"weather-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "提供天气信息和建议" ),
llmagent . WithInstruction ( "你是天气专家,提供天气分析和活动建议..." ),
)
// 使用 WithSubAgents option 配置 SubAgent
mainAgent := llmagent . New (
"coordinator-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "协调者 Agent,负责任务委托" ),
llmagent . WithInstruction ( "你是协调者,分析用户请求并委托给合适的专家..." ),
llmagent . WithSubAgents ([] agent . Agent { mathAgent , weatherAgent }),
)
动态 SubAgent 与 Agent Factory
在 trpc-agent-go 中,SubAgent 并不是一种新的运行时类型。它仍然只是普通的
agent.Agent。所谓 SubAgent,只是描述它和父 Agent 的关系:它通过
WithSubAgents 挂在某个父 Agent 下面,因此这个父 Agent 可以把任务委托给它。
理解这个边界之后,动态创建 Agent 时就不容易选错 API。
Root Agent 与 SubAgent 的区别
优先选择能解决问题的最小 API:
需求
推荐 API
原因
本次请求要从某个命名 Agent 开始执行
runner.WithAgentFactory + agent.WithAgentByName
Runner 在 run 开始前选择 root Agent。
父 Agent 要委托给一批已经构建好的专家 Agent
llmagent.WithSubAgents
父 Agent 可以直接把所有专家的名称和描述暴露给模型。
父 Agent 要委托给某个专家,但这个专家构建成本较高,或需要请求级参数
agent.NewLazyAgent + WithSubAgents
父 Agent 先通过 agent.Info 暴露专家信息,真正调用时才构建具体 Agent。
租户、项目或数据库决定有哪些专家 Agent
在业务代码中构建 []agent.Agent,再传给 WithSubAgents
配置发现、鉴权、覆盖规则通常应由业务应用负责。
runner.WithAgentFactory 面向的是 root Agent 查找 。它回答的问题是:
“这次 run 已经知道要用哪个 root Agent 名字,请帮我创建它。”
WithSubAgents 面向的是 父 Agent 作用域内的可见性 。它回答的问题是:
“当前这个父 Agent 正在运行时,它可以委托给哪些专家?”
因此,把一个 Agent factory 注册到 Runner 上,并不意味着所有父 Agent 都能
自动委托给这个 Agent。这是有意设计的:不同父 Agent 往往需要不同的委托边界。
按请求构建 root Agent 和 SubAgents
如果整棵 Agent 树都依赖本次请求,推荐在 Runner factory 中构建父 Agent,
再把请求级 SubAgents 显式传给 WithSubAgents。
r := runner . NewRunnerWithAgentFactory (
"support-app" ,
"support-coordinator" ,
func ( ctx context . Context , ro agent . RunOptions ) ( agent . Agent , error ) {
tenantID , _ := agent . GetRuntimeStateValue [ string ]( & ro , "tenant_id" )
billingAgent := llmagent . New (
"billing-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "Handles billing and invoice questions" ),
llmagent . WithInstruction (
"Answer billing questions for tenant " + tenantID + "." ,
),
)
coordinator := llmagent . New (
"support-coordinator" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "Routes support requests to specialists" ),
llmagent . WithInstruction (
"Decide whether to answer directly or transfer to a specialist." ,
),
llmagent . WithSubAgents ([] agent . Agent { billingAgent }),
)
return coordinator , nil
},
)
events , err := r . Run (
ctx ,
userID ,
sessionID ,
model . NewUserMessage ( "Why was my invoice higher this month?" ),
agent . MergeRuntimeState ( map [ string ] any {
"tenant_id" : "tenant-001" ,
}),
)
_ = events
_ = err
这个模式把职责拆得很清楚:
业务应用决定加载哪个租户或项目配置。
Runner 负责创建本次请求使用的 root Agent。
父 Agent 显式声明本次允许委托的 SubAgents。
懒加载 SubAgent
有些专家 Agent 只有少数请求会用到。如果它会打开网络连接、初始化大型工具集、
准备沙箱环境,那么每次都提前构建会造成浪费。
agent.NewLazyAgent 解决的是这个很小的框架边界:父 Agent 可以先拿到
agent.Info,因此 transfer_to_agent 工具能展示专家名称和描述;只有当这个
懒加载 Agent 的 Run 方法真正被调用时,才会创建具体 Agent。
lazyBillingAgent := agent . NewLazyAgent (
agent . Info {
Name : "billing-agent" ,
Description : "Handles billing and invoice questions" ,
},
func ( ctx context . Context , ro agent . RunOptions ) ( agent . Agent , error ) {
tenantID , _ := agent . GetRuntimeStateValue [ string ]( & ro , "tenant_id" )
return llmagent . New (
"billing-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "Handles billing and invoice questions" ),
llmagent . WithInstruction (
"Answer billing questions for tenant " + tenantID + "." ,
),
), nil
},
)
coordinator := llmagent . New (
"support-coordinator" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "Routes support requests to specialists" ),
llmagent . WithInstruction (
"Use transfer_to_agent when a specialist should handle the request." ,
),
llmagent . WithSubAgents ([] agent . Agent { lazyBillingAgent }),
)
懒加载 Agent 仍然是普通的 agent.Agent。父 Agent 仍然通过标准
WithSubAgents 机制看到它,transfer_to_agent 也仍然通过标准
FindSubAgent 路径找到它。
使用时注意:
agent.Info.Name 是稳定的委托目标名。factory 返回的具体 Agent 应使用相同
名字,便于事件、trace 和 session 分支保持一致。
NewLazyAgent 最适合叶子专家 Agent。如果这个专家内部还需要自己的
SubAgents,请在 factory 内部继续构建和配置。
框架不会接管 factory 内部创建的资源。如果 factory 创建了请求级连接、
tool set 或沙箱,请在业务代码或 callback 中自行清理。
业务侧负责发现配置
框架不规定动态 SubAgent 的定义必须来自哪里。它们可以来自代码、数据库、
租户配置服务、插件,或业务应用自己管理的文件。
推荐边界是:
业务应用负责发现、校验和合并配置。
业务应用把配置转换成 agent.Agent,或者转换成 agent.NewLazyAgent(...)
描述。
框架通过 WithSubAgents、transfer_to_agent、AgentTool、ChainAgent、
ParallelAgent、CycleAgent 或 GraphAgent 运行这些 Agent。
这样配置来源、鉴权、灰度策略仍然由业务掌控,同时框架继续保持一个统一的
运行时抽象:agent.Agent。
核心协作模式
所有协作模式都基于 SubAgent 概念,通过不同的执行策略实现:
链式 Agent (ChainAgent)
链式 Agent 使用 SubAgent 按顺序连接,形成处理流水线。每个 SubAgent 专注于特定任务,并将结果传递给下一个 SubAgent。
使用场景
内容创作流程 :规划 → 研究 → 写作
问题解决流程 :分析 → 设计 → 实现
数据处理流程 :收集 → 清洗 → 分析
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/chainagent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
)
// 创建 SubAgent
planningAgent := llmagent . New ( "planning-agent" , ... )
researchAgent := llmagent . New ( "research-agent" , ... )
writingAgent := llmagent . New ( "writing-agent" , ... )
// 创建链式 Agent,使用 WithSubAgents 配置 SubAgent
chainAgent := chainagent . New (
"multi-agent-chain" ,
chainagent . WithSubAgents ([] agent . Agent {
planningAgent ,
researchAgent ,
writingAgent ,
}),
)
示例会话
🔗 多 Agent 链式演示
链式流程:规划 → 研究 → 写作
==================================================
👤 用户:解释可再生能源的好处
📋 规划 Agent:我将创建一个结构化的分析计划...
🔍 研究 Agent:
🔧 使用工具:
• web_search (ID: call_123)
🔄 执行中...
✅ 工具结果:最新的可再生能源数据...
✍️ 写作 Agent:基于规划和研究:
[结构化的综合回答]
并行 Agent (ParallelAgent)
并行 Agent 使用 SubAgent 同时处理同一输入的不同方面,提供多角度的分析。
使用场景
商业决策分析 :市场分析、技术评估、风险评估、机会分析
多维度评估 :不同专家同时评估同一问题
快速并行处理 :需要同时获得多个视角的场景
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent/parallelagent"
)
// 创建 SubAgent
marketAgent := llmagent . New ( "market-analysis" , ... )
technicalAgent := llmagent . New ( "technical-assessment" , ... )
riskAgent := llmagent . New ( "risk-evaluation" , ... )
opportunityAgent := llmagent . New ( "opportunity-analysis" , ... )
// 创建并行 Agent,使用 WithSubAgents 配置 SubAgent
parallelAgent := parallelagent . New (
"parallel-demo" ,
parallelagent . WithSubAgents ([] agent . Agent {
marketAgent ,
technicalAgent ,
riskAgent ,
opportunityAgent ,
}),
)
示例会话
⚡ 并行多 Agent 演示
Agent:市场 📊 | 技术 ⚙️ | 风险 ⚠️ | 机会 🚀
==================================================
💬 用户:我们应该为供应链跟踪实施区块链吗?
🚀 开始并行分析:"我们应该为供应链跟踪实施区块链吗?"
📊 Agent 正在分析不同角度...
────────────────────────────────────────────────────────────────────────────────
📊 [market-analysis] 开始分析...
⚙️ [technical-assessment] 开始分析...
⚠️ [risk-evaluation] 开始分析...
🚀 [opportunity-analysis] 开始分析...
📊 [market-analysis]: 区块链供应链市场正在经历强劲增长,年复合增长率为67%...
⚙️ [technical-assessment]: 实施需要分布式账本基础设施和共识机制...
⚠️ [risk-evaluation]: 主要风险包括40%目标市场的监管不确定性...
🚀 [opportunity-analysis]: 战略优势包括增强透明度,可带来15-20%的成本降低...
🎯 所有并行分析成功完成!
────────────────────────────────────────────────────────────────────────────────
✅ 多角度分析在4.1秒内完成
循环 Agent (CycleAgent)
循环 Agent 使用 SubAgent 在迭代循环中运行,直到满足特定条件(如质量阈值或最大迭代次数)。
使用场景
内容优化 :生成 → 评估 → 改进 → 重复
问题解决 :提出 → 评估 → 增强 → 重复
质量保证 :草稿 → 审查 → 修订 → 重复
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent/cycleagent"
)
// 创建 SubAgent
generateAgent := llmagent . New ( "generate-agent" , ... )
criticAgent := llmagent . New ( "critic-agent" , ... )
// 创建循环 Agent,使用 WithSubAgents 配置 SubAgent
cycleAgent := cycleagent . New (
"cycle-demo" ,
cycleagent . WithSubAgents ([] agent . Agent {
generateAgent ,
criticAgent ,
}),
cycleagent . WithMaxIterations ( 3 ),
cycleagent . WithEscalationFunc ( qualityEscalationFunc ),
)
退出函数(WithEscalationFunc)
你可以把 WithEscalationFunc 理解为“退出函数”:当回调返回 true,
CycleAgent 就会退出循环。代码里叫 EscalationFunc,这里的
Escalation 只是指控制流上抛 / 中止循环,并不是“版本升级”。
CycleAgent 会按顺序运行 SubAgent,然后重复整套流程。它会在以下任一
情况发生时停止:
你的 EscalationFunc 对某个事件返回 true
达到 WithMaxIterations(n) 设定的上限
context.Context 被取消(超时 / 手动取消)
EscalationFunc 会收到什么?
回调签名如下:
type EscalationFunc func ( * event . Event ) bool
它会在子 Agent 产生并被转发出来的事件上执行。为了避免在流式输出的
“半截 token”上误判,CycleAgent 只会在更“关键”的事件上做检查,
例如:
错误事件(evt.Error != nil)
工具结果事件(evt.Object == model.ObjectTypeToolResponse)
最终完成事件(evt.Done == true,非 streaming chunk)
默认行为
如果你不设置 WithEscalationFunc,CycleAgent 只会在遇到错误时停止。
示例:基于质量阈值停止
一个常见的循环是:生成 → 评审 → 达标就停 。
让评审 Agent 通过工具返回一个“机器可读”的信号(例如 record_score
工具返回 JSON,其中包含 needs_improvement)。当
needs_improvement 变为 false 时立即停止(需要引入
encoding/json):
type scoreResult struct {
NeedsImprovement bool `json:"needs_improvement"`
}
func qualityEscalationFunc ( evt * event . Event ) bool {
if evt == nil || evt . Response == nil {
return false
}
if evt . Error != nil {
return true
}
if evt . Object != model . ObjectTypeToolResponse {
return false
}
for _ , choice := range evt . Response . Choices {
msg := choice . Message
if msg . Role != model . RoleTool {
continue
}
var res scoreResult
if err := json . Unmarshal ([] byte ( msg . Content ), & res ); err != nil {
continue
}
return ! res . NeedsImprovement
}
return false
}
提示:这个函数运行在事件循环里,建议保持轻量、无副作用,并做好
nil / 解析失败的防御处理。
示例会话
🔄 多 Agent 循环演示
最大迭代次数:3
循环:生成 → 评估 → 改进 → 重复
==================================================
👤 用户:写一个短笑话
🤖 循环响应:
🤖 生成 Agent:为什么骷髅不互相打架?
因为它们没有胆量!
👀 评估 Agent:
🔧 使用工具:
• record_score (ID: call_123)
🔄 执行中...
✅ 质量评分:75/100
⚠️ 需要改进 - 继续迭代
🔄 **第2次迭代**
🤖 生成 Agent:这是一个改进版本,有新的转折:
**为什么骷髅从不赢得争论?**
因为它们总是在中途失去脊梁!
👀 评估 Agent:
🔧 使用工具:
• record_score (ID: call_456)
🔄 执行中...
✅ 质量评分:85/100
🎉 质量阈值达到 - 循环完成
🏁 循环在2次迭代后完成
辅助功能
Agent 工具是构建复杂多 Agent 系统的重要基础功能,它允许您将任何 Agent 包装成可调用的工具,供其他 Agent 或应用程序使用。
使用场景
专业化委托 :不同 Agent 处理特定类型的任务
工具集成 :Agent 可以作为工具集成到更大的系统中
模块化设计 :可重用的 Agent 组件可以组合在一起
复杂工作流 :涉及多个专门化 Agent 的复杂工作流
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
agenttool "trpc.group/trpc-go/trpc-agent-go/tool/agent"
"trpc.group/trpc-go/trpc-agent-go/tool/function"
)
// 创建专门的 Agent
mathAgent := llmagent . New (
"math-specialist" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "专门处理数学运算的 Agent" ),
llmagent . WithInstruction ( "你是一个数学专家,专注于数学运算、计算和数值推理..." ),
llmagent . WithTools ([] tool . Tool { calculatorTool }),
)
// 将 Agent 包装成工具
agentTool := agenttool . NewTool (
mathAgent ,
// 默认 skip summarization=false,当设置为 true 时会在 tool.response 后直接结束本轮
agenttool . WithSkipSummarization ( false ),
// 开启转发:把子 Agent 的流式事件内联到父流程
agenttool . WithStreamInner ( true ),
// 工具结果只返回子 Agent 最后一条完整 assistant 消息
agenttool . WithResponseMode ( agenttool . ResponseModeFinalOnly ),
)
// 在主 Agent 中使用 Agent 工具
mainAgent := llmagent . New (
"chat-assistant" ,
llmagent . WithTools ([] tool . Tool { timeTool , agentTool }),
)
Agent工具架构
聊天助手 (主 Agent)
├── 时间工具 (函数)
└── 数学专家 Agent 工具 (Agent)
└── 数学专家 Agent (专门化 Agent)
└── 计算器工具 (函数)
示例会话
🚀 Agent 工具示例
模型:deepseek-v4-flash
可用工具:current_time, math-specialist
==================================================
👤 用户:计算 923476 * 273472354
🤖 助手:我将使用数学专家 Agent 来计算这个结果。
🔧 工具调用已启动:
• math-specialist (ID: call_0_e53a77e9-c994-4421-bfc3-f63fe85678a1)
参数:{"request":"计算 923476 乘以 273472354"}
🔄 执行工具中...
✅ 工具响应 (ID: call_0_e53a77e9-c994-4421-bfc3-f63fe85678a1):
"计算 923,476 乘以 273,472,354 的结果是:
\[
923,\!476 \times 273,\!472,\!354 = 252,\!545,\!155,\!582,\!504
\]"
✅ 工具执行完成。
流式内部转发(StreamInner)
当为 Agent 工具启用 WithStreamInner(true) 时:
子 Agent 的事件会以流式形式转发到父流程(event.Event),可直接显示 choice.Delta.Content
为避免重复打印,子 Agent 最终的整段文本默认不会作为转发事件再次输出;但会被聚合写入最终的 tool.response,用于满足模型“工具消息跟随”的要求
如果你想保留内部进度、但隐藏子 Agent 的 assistant 正文,可以再加上 WithInnerTextMode(agenttool.InnerTextModeExclude)
建议在 UI 层:
展示子 Agent 转发的增量内容
如非调试,不再额外打印最终聚合的工具响应内容
示例:在事件循环中区分外层助手/子 Agent/工具响应
// 子 Agent 转发的增量(作者不是父 Agent)
if ev . Author != parentName && ev . Response != nil && len ( ev . Response . Choices ) > 0 {
if delta := ev . Response . Choices [ 0 ]. Delta . Content ; delta != "" {
fmt . Print ( delta )
}
return
}
// 工具响应(包含聚合内容),默认不打印,避免重复
if ev . Response != nil && ev . Object == model . ObjectTypeToolResponse {
// ...按需展示或忽略
return
}
选项对照
WithSkipSummarization(false):默认,工具返回后允许外层模型再总结一次
WithSkipSummarization(true):工具返回后跳过外层模型的总结调用
WithStreamInner(true):启用子 Agent 事件转发(父/子 Agent 建议都 Stream: true)
WithStreamInner(false):按普通可调用工具处理,不转发内部流
WithInnerTextMode(agenttool.InnerTextModeInclude):启用内部转发时,继续展示子 Agent 的 assistant 文本
WithInnerTextMode(agenttool.InnerTextModeExclude):保留内部进度事件,但不再转发子 Agent 的 assistant 正文
WithResponseMode(agenttool.ResponseModeDefault):默认兼容模式,把子 Agent 的 assistant 消息拼接成工具结果
WithResponseMode(agenttool.ResponseModeFinalOnly):只把子 Agent 最后一条完整 assistant 消息作为工具结果返回
当子 Agent 是一个上下文隔离的独立工作者,而父 Agent 只应该消费它的最终答案时,
使用 ResponseModeFinalOnly。如果还希望在流式 UI 中隐藏子 Agent 的正文,
再单独组合 InnerTextModeExclude。
模型钉住 (Pin Model)
默认情况下,子 Agent 会继承调用方的 RunOptions.ModelName、
RunOptions.Model 和 RunOptions.ModelSelector。这仅在调用方通过
agent.WithModelName(...)、agent.WithModel(...) 或
agent.WithModelSelector(...) 在 runner.Run 时指定了模型才会生效(例如 AGUI
服务端转发终端用户的模型选择)。如果 RunOptions 中未设置模型,子 Agent 自然使用
自己通过 llmagent.WithModel 配置的模型。
当子 Agent 需要始终使用自己的模型,不受 RunOptions 传入的运行时模型选择的影响:
agenttool . NewTool ( subAgent ,
agenttool . WithPinModel ( true ),
)
Agent 委托 (Agent Transfer)
Agent 委托通过 transfer_to_agent 工具实现 Agent 间的任务委托,允许主 Agent 根据任务类型自动选择合适的 SubAgent。
使用场景
任务分类 :根据用户请求自动选择合适的 SubAgent
智能路由 :将复杂任务路由到最合适的处理者
专业化处理 :每个 SubAgent 专注于特定领域
无缝切换 :在 SubAgent 之间无缝切换,保持对话连续性
基本用法
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/function"
)
// 创建 SubAgent
mathAgent := llmagent . New (
"math-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "处理数学计算和数值问题" ),
llmagent . WithInstruction ( "你是数学专家,专注于数学运算和数值推理..." ),
llmagent . WithTools ([] tool . Tool { calculatorTool }),
)
weatherAgent := llmagent . New (
"weather-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "提供天气信息和建议" ),
llmagent . WithInstruction ( "你是天气专家,提供天气分析和活动建议..." ),
llmagent . WithTools ([] tool . Tool { weatherTool }),
)
// 创建协调者 Agent,使用 WithSubAgents 配置 SubAgent
coordinatorAgent := llmagent . New (
"coordinator-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "协调者 Agent,负责任务委托" ),
llmagent . WithInstruction ( "你是协调者,分析用户请求并委托给合适的专家..." ),
llmagent . WithSubAgents ([] agent . Agent { mathAgent , weatherAgent }),
)
动态 SubAgent 发现(结合 A2A)
在真实系统中,SubAgent 往往是通过 A2A 协议暴露的远程 Agent,
它们的列表会随着时间变化(例如从注册中心动态上下线)。
为支持这一场景,LLMAgent 实现了 agent.SubAgentSetter
接口,可以在运行时刷新 SubAgent 列表,而无需重建协调者:
import (
"fmt"
"context"
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/a2aagent"
)
func refreshSubAgents ( ctx context . Context , ag agent . Agent ) error {
cfg , ok := ag .( agent . SubAgentSetter )
if ! ok {
return fmt . Errorf ( "agent does not support dynamic SubAgents" )
}
// 1. 从注册中心或配置源发现远程 Agent。
urls := [] string {
"http://localhost:8087/" ,
"http://localhost:8088/" ,
}
// 2. 为每个远程 Agent 构建 A2AAgent 代理。
subAgents := make ([] agent . Agent , 0 , len ( urls ))
for _ , url := range urls {
a2 , err := a2aagent . New ( a2aagent . WithAgentCardURL ( url ))
if err != nil {
// 生产环境中建议记录日志并跳过失败项。
continue
}
subAgents = append ( subAgents , a2 )
}
// 3. 原子性地替换协调者上的 SubAgents。
cfg . SetSubAgents ( subAgents )
return nil
}
这种模式可以让你:
与任意注册中心集成(服务发现、数据库、配置文件等)
动态增加或移除远程 SubAgent
保持 Runner 和会话管理逻辑不变,因为协调者始终是同一
个 Agent 实例
Agent委托架构
协调者 Agent (主入口)
├── 分析用户请求
├── 选择合适的 SubAgent
└── 使用 transfer_to_agent 工具委托任务
├── 数学 SubAgent (数学计算)
├── 天气 SubAgent (天气信息)
└── 研究 SubAgent (信息搜索)
示例会话
🔄 Agent 委托演示
可用 SubAgent:math-agent, weather-agent, research-agent
==================================================
👤 用户:计算复利,本金5000美元,年利率6%,期限8年
🎯 协调者:我将把这个任务委托给我们的数学专家进行准确计算。
🔄 启动委托...
🔄 委托事件:将控制权转移给 Agent:math-agent
🧮 数学专家:我将帮助您逐步计算复利。
🔧 🧮 执行工具:
• calculate ({"operation":"power","a":1.06,"b":8})
✅ 工具完成
🔧 🧮 执行工具:
• calculate ({"operation":"multiply","a":5000,"b":1.593})
✅ 工具完成
复利计算结果:
- 本金:$5,000
- 年利率:6%
- 期限:8年
- 结果:$7,969.24(利息约$2,969.24)
跨轮追问用户
transfer_to_agent 解决的是 当前这一轮由谁处理 。它本身并不会告诉
Runner:下一条用户消息 应该继续交给谁。
当 SubAgent 发起追问时,这个问题就会暴露出来:
coordinator transfer 给某个 SubAgent
SubAgent 向用户追问缺失信息
用户在下一次请求里补充信息
默认情况下,Runner 还是会从常规入口 Agent 开始
更干净的做法是把这件事做成 显式 、一次性 的路由:
在 Runner 上开启 runner.WithAwaitUserReplyRouting(true)
对可能追问用户的 Agent,开启
llmagent.WithAwaitUserReplyTool(true)
在这些 Agent 的 instruction 里明确要求模型:如果要向用户补字段,
先调用 await_user_reply,再发出追问
import (
"context"
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/model"
"trpc.group/trpc-go/trpc-agent-go/runner"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
profileAgent := llmagent . New (
"profile-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "负责采集并更新客户资料。" ),
llmagent . WithInstruction ( `
你负责更新客户资料。
如果缺少必要字段:
1. 先调用 await_user_reply
2. 再向用户提出一个明确的问题
3. 然后等待下一条用户消息
字段齐全后,再调用 update_profile。
` ),
llmagent . WithAwaitUserReplyTool ( true ),
llmagent . WithTools ([] tool . Tool { updateProfileTool }),
)
coordinatorAgent := llmagent . New (
"coordinator-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithInstruction ( `
用户要更新资料时,直接 transfer 给 profile-agent。
不要自己收集资料字段。
` ),
llmagent . WithSubAgents ([] agent . Agent { profileAgent }),
)
r := runner . NewRunner (
"crm-app" ,
coordinatorAgent ,
runner . WithAwaitUserReplyRouting ( true ),
)
// 第 1 轮:
// user: "帮我更新资料"
// coordinator-agent -> transfer_to_agent(profile-agent)
// profile-agent -> await_user_reply + "请问要保存的手机号是多少?"
// 第 2 轮(同一个 session):
// user: "+1-555-0101"
// Runner 这一轮会直接从 profile-agent 开始。
events , err := r . Run (
context . Background (),
"user-1" ,
"session-1" ,
model . NewUserMessage ( "+1-555-0101" ),
)
if err != nil {
// handle error
}
for evt := range events {
if evt . IsRunnerCompletion () {
break
}
}
这套能力的行为边界:
这条路由只消费一次;下一条用户消息开始时就会被清掉
显式指定的 agent.WithAgent(...) 或 agent.WithAgentByName(...)
仍然优先
如果目标 Agent 路径已经失效,Runner 会回退到默认入口 Agent,
并清理掉脏路由
对最常见的 “coordinator + WithSubAgents” 结构,Runner
会按完整的 Agent 链路径恢复,不需要你把每个 SubAgent 再额外注册一遍
如果你不是用 LLMAgent,而是自己实现 agent.Agent,请看 runner
文档里的底层 API:agent.MarkAwaitingUserReply(...)。
内置 Explorer(只读探索 Agent)
agent/llmagent/builtin 提供一个开箱即用的只读“探索 / 检索 / 分析 / 查看”型
Agent 预设,省去每次手写名字、description、只读 prompt、继承父能力面的样板代码。
builtin.NewExplorer() 返回一个普通的 agent.Agent,因此两种接入方式都支持:
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent/builtin"
agenttool "trpc.group/trpc-go/trpc-agent-go/tool/agent"
)
// 方式一:作为 SubAgent,模型通过 transfer_to_agent 把控制权交给 explorer
root := llmagent . New ( "assistant" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithTools ( tools ),
llmagent . WithSubAgents ([] agent . Agent { builtin . NewExplorer ()}),
)
// 方式二:包成 AgentTool,模型同步调用 explorer,拿结果后主 Agent 继续
root := llmagent . New ( "assistant" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithTools ( append ( tools , agenttool . NewTool ( builtin . NewExplorer ()))),
)
两种方式的能力继承一致:transfer 和 AgentTool 都在父 invocation 的克隆上运行子
Agent,Explorer 在 Run 时通过直接父 invocation 推导默认能力面。
默认继承行为
不传任何选项时,Explorer 默认从直接父 invocation 继承:
用户工具 :父 Agent 通过 WithTools / WithToolSets 注册的工具。框架自动注入的
工具(transfer_to_agent、await_user_reply 等)不会被继承。
knowledge :父的知识检索能力(knowledge_search 等)会按父配置重新生成。
skills / code executor :按父配置重新生成,绑定到子调用而非硬搬父运行时状态。
model :继承父 invocation 当前解析出的 model。
只读语义是软约束
Explorer 的 read-only 是 system prompt 软约束,不是权限隔离 。它的定位是一个便捷的
内置只读角色:模型会被提示去检索、查看和总结,但框架不会自动把工具分类成只读工具或写
工具。
自定义能力面
builtin . NewExplorer (
builtin . WithName ( "explorer" ),
builtin . WithDescription ( "Reads and investigates available context without modifying anything." ),
builtin . WithInstruction ( customReadOnlyPrompt ),
builtin . WithSkills ( readOnlySkillsRepo ), // 显式替换
builtin . WithModel ( modelInstance ), // 显式指定,否则继承父 model
)
可用选项:WithName、WithDescription、WithInstruction、WithTools、
WithSkills、WithModel、WithCodeExecutor,以及高级逃生口
WithLLMAgentOptions(直接透传内部 llmagent.Option,谨慎使用)。
行为说明:
不传 WithTools:运行时继承父用户工具;传了则使用显式工具集,且不再继承父用户工具
与 knowledge。
不传 WithSkills / WithCodeExecutor:运行时按父能力重新生成。
不传 WithModel:继承父 invocation 当前 model;若父也没有 model,Run 返回清晰错误。
没有父 invocation(例如作为 root 运行):不继承,只使用显式配置。
环境变量配置
所有多 Agent 示例都需要以下环境变量:
变量名
必需
默认值
说明
OPENAI_API_KEY
是
-
OpenAI API 密钥
OPENAI_BASE_URL
否
https://api.openai.com/v1
OpenAI API 基础URL
运行示例
所有示例代码位于 examples
核心协作模式示例
链式 Agent 示例
cd examples/multiagent/chain
export OPENAI_API_KEY = "your-api-key"
go run main.go -model deepseek-v4-flash
并行 Agent 示例
cd examples/multiagent/parallel
export OPENAI_API_KEY = "your-api-key"
go run main.go -model deepseek-v4-flash
循环 Agent 示例
cd examples/multiagent/cycle
export OPENAI_API_KEY = "your-api-key"
go run main.go -model deepseek-v4-flash -max-iterations 5
辅助功能示例
Agent 工具示例
cd examples/agenttool
export OPENAI_API_KEY = "your-api-key"
go run main.go -model deepseek-v4-flash
Agent 委托示例
cd examples/transfer
export OPENAI_API_KEY = "your-api-key"
go run main.go -model deepseek-v4-flash
自定义和扩展
添加新的 Agent
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/agent/chainagent"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
// 创建自定义 Agent
customAgent := llmagent . New (
"custom-agent" ,
llmagent . WithModel ( modelInstance ),
llmagent . WithDescription ( "自定义 Agent 描述" ),
llmagent . WithInstruction ( "自定义指令" ),
llmagent . WithTools ([] tool . Tool { customTool }),
)
// 集成到多 Agent 系统中
chainAgent := chainagent . New (
"custom-chain" ,
chainagent . WithSubAgents ([] agent . Agent {
existingAgent ,
customAgent , // 添加自定义 Agent
}),
)
配置工具
import (
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/function"
)
// 创建自定义工具
customTool := function . NewFunctionTool (
customFunction ,
function . WithName ( "custom_tool" ),
function . WithDescription ( "自定义工具描述" ),
)
// 为 Agent 分配工具
agent := llmagent . New (
"tool-agent" ,
llmagent . WithTools ([] tool . Tool { customTool }),
)
调整参数
import (
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/model"
)
// 配置生成参数
genConfig := model . GenerationConfig {
MaxTokens : intPtr ( 500 ),
Temperature : floatPtr ( 0.7 ),
Stream : true ,
}
// 应用到 Agent
agent := llmagent . New (
"configured-agent" ,
llmagent . WithGenerationConfig ( genConfig ),
)
2026-06-09 10:59:38
2025-08-25 07:06:16