第14章 Agent-as-Tool(智能体即工具)
本章目标
- 理解 Agent-as-Tool 模式的设计理念
- 掌握 SubAgentTool 的使用方法
- 学会将智能体封装为可调用的工具
- 了解多轮对话会话管理机制
14.1 Agent-as-Tool 概述
14.1.1 什么是 Agent-as-Tool?
Agent-as-Tool(智能体即工具) 是一种将智能体封装为工具的模式,使得一个智能体可以通过工具调用来使用另一个智能体的能力。这种模式实现了智能体之间的层级协作。
┌─────────────────────────────────────────────────────────────┐
│ Agent-as-Tool 模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 主智能体 (Supervisor) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Toolkit │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────┐ │ │ │
│ │ │ │ 普通工具 │ │ SubAgentTool │ │ │ │
│ │ │ │ (搜索/计算) │ │ (子智能体工具) │ │ │ │
│ │ │ └─────────────┘ └─────────┬───────────┘ │ │ │
│ │ └────────────────────────────┬─────────────────┘ │ │
│ └────────────────────────────────┼─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 子智能体 (Sub-Agent) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 专业能力(研究、分析、翻译等) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 调用流程: │
│ 1. 用户请求 → 主智能体 │
│ 2. 主智能体决定调用 SubAgentTool │
│ 3. SubAgentTool 执行子智能体 │
│ 4. 子智能体返回结果 → 主智能体 │
│ 5. 主智能体整合结果 → 用户 │
│ │
└─────────────────────────────────────────────────────────────┘
14.1.2 核心优势
| 优势 | 说明 |
|---|---|
| 能力组合 | 主智能体可以调用多个专业子智能体 |
| 会话管理 | 支持多轮对话,保持上下文 |
| 解耦设计 | 子智能体独立开发、测试、部署 |
| 动态选择 | 主智能体可以根据任务选择合适的子智能体 |
| 事件转发 | 支持流式输出和事件转发 |
14.2 SubAgentTool 核心组件
14.2.1 SubAgentProvider 接口
SubAgentProvider 用于创建子智能体实例:
import io.agentscope.core.agent.Agent;
/**
* 子智能体提供者
* 每次调用 provide() 返回一个新的智能体实例
*/
@FunctionalInterface
public interface SubAgentProvider<T extends Agent> {
/**
* 提供一个新的智能体实例
*/
T provide();
}
14.2.2 SubAgentConfig 配置
import io.agentscope.core.tool.subagent.SubAgentConfig;
import io.agentscope.core.session.Session;
import io.agentscope.core.agent.StreamOptions;
// 创建子智能体配置
SubAgentConfig config = SubAgentConfig.builder()
// 工具名称(可选,默认从智能体名称生成)
.toolName("call_researcher")
// 工具描述(可选,默认从智能体描述获取)
.description("调用研究员智能体进行深度研究")
// 是否转发事件到父智能体(支持流式输出)
.forwardEvents(true)
// 流式输出选项
.streamOptions(StreamOptions.defaults())
// 会话存储(用于多轮对话状态保持)
.session(session)
.build();
14.2.3 SubAgentTool 类
import io.agentscope.core.tool.subagent.SubAgentTool;
import io.agentscope.core.tool.subagent.SubAgentProvider;
import io.agentscope.core.tool.subagent.SubAgentConfig;
// 创建子智能体工具
SubAgentTool researcherTool = new SubAgentTool(
// 智能体提供者(lambda 表达式)
() -> createResearcherAgent(),
// 配置
SubAgentConfig.builder()
.toolName("call_researcher")
.description("调用研究员进行信息研究和分析")
.forwardEvents(true)
.build()
);
// 工具暴露的参数:
// - session_id: 可选,用于多轮对话
// - message: 必需,发送给子智能体的消息
14.3 基本使用
14.3.1 创建子智能体工具
import io.agentscope.core.ReActAgent;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.core.tool.subagent.SubAgentTool;
import io.agentscope.core.tool.subagent.SubAgentConfig;
public class AgentAsToolExample {
public static void main(String[] args) {
String apiKey = System.getenv("DASHSCOPE_API_KEY");
// ========================================
// 1. 创建子智能体工具
// ========================================
// 研究员智能体工具
SubAgentTool researcherTool = new SubAgentTool(
() -> createResearcherAgent(apiKey),
SubAgentConfig.builder()
.toolName("call_researcher")
.description("调用研究员智能体进行深度信息研究和分析")
.forwardEvents(true)
.build()
);
// 翻译员智能体工具
SubAgentTool translatorTool = new SubAgentTool(
() -> createTranslatorAgent(apiKey),
SubAgentConfig.builder()
.toolName("call_translator")
.description("调用翻译员智能体进行多语言翻译")
.forwardEvents(true)
.build()
);
// ========================================
// 2. 创建主智能体(使用子智能体工具)
// ========================================
Toolkit toolkit = new Toolkit();
toolkit.registerAgentTool(researcherTool);
toolkit.registerAgentTool(translatorTool);
ReActAgent supervisor = ReActAgent.builder()
.name("Supervisor")
.sysPrompt("""
你是一个任务协调者。
你有以下专业助手可以调用:
- call_researcher: 用于深度研究和信息收集
- call_translator: 用于翻译任务
根据用户的需求,选择合适的助手来完成任务。
你可以:
1. 直接回答简单问题
2. 调用专业助手处理复杂任务
3. 整合多个助手的结果给出最终回答
""")
.model(createModel(apiKey))
.toolkit(toolkit)
.memory(new InMemoryMemory())
.build();
// ========================================
// 3. 使用主智能体
// ========================================
System.out.println("用户: 请研究一下人工智能在医疗领域的最新应用\n");
Msg response = supervisor.call(
Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder()
.text("请研究一下人工智能在医疗领域的最新应用")
.build())
.build()
).block();
System.out.println("助手: " + response.getTextContent());
}
private static ReActAgent createResearcherAgent(String apiKey) {
return ReActAgent.builder()
.name("Researcher")
.description("专业研究员,擅长深度信息收集和分析")
.sysPrompt("""
你是一位专业研究员。
你的能力:
1. 收集和整理信息
2. 分析数据和趋势
3. 提供详细的研究报告
请根据用户的研究需求,提供详细、准确、有洞察力的分析。
""")
.model(createModel(apiKey))
.memory(new InMemoryMemory())
.toolkit(new Toolkit())
.build();
}
private static ReActAgent createTranslatorAgent(String apiKey) {
return ReActAgent.builder()
.name("Translator")
.description("专业翻译员,支持多语言翻译")
.sysPrompt("""
你是一位专业翻译员。
你的能力:
1. 中英文互译
2. 保持原文风格和语气
3. 处理专业术语
请提供准确、流畅的翻译结果。
""")
.model(createModel(apiKey))
.memory(new InMemoryMemory())
.toolkit(new Toolkit())
.build();
}
}
14.3.2 多轮对话支持
SubAgentTool 支持多轮对话,通过 session_id 参数保持上下文:
// 子智能体工具调用时的参数
// 第一次调用(新会话):
{
"message": "请分析这篇文章的主题"
}
// 后续调用(继续会话):
{
"session_id": "abc-123-def", // 从前一次响应中获取
"message": "请进一步解释第二点"
}
会话管理示例:
import io.agentscope.core.session.InMemorySession;
import io.agentscope.core.session.Session;
// 创建会话存储
Session session = new InMemorySession();
// 配置带会话的子智能体工具
SubAgentConfig config = SubAgentConfig.builder()
.toolName("call_analyst")
.description("数据分析师,支持多轮分析对话")
.session(session) // 启用会话持久化
.build();
SubAgentTool analystTool = new SubAgentTool(
() -> createAnalystAgent(apiKey),
config
);
// 主智能体可以通过 session_id 进行多轮对话
// 第一次调用返回:
// session_id: abc-123
// 分析结果: ...
// 主智能体可以使用同一个 session_id 继续对话
14.4 事件转发与流式输出
14.4.1 启用事件转发
SubAgentConfig config = SubAgentConfig.builder()
.toolName("call_writer")
.forwardEvents(true) // 启用事件转发
.streamOptions(StreamOptions.builder()
.streamThinking(true) // 转发思考过程
.streamToolCall(true) // 转发工具调用
.build())
.build();
14.4.2 流式输出处理
// 主智能体使用流式模式
supervisor.stream("请帮我写一篇关于 AI 的文章")
.doOnNext(event -> {
// 可以接收到子智能体的流式事件
if (event.getMessage() != null) {
System.out.print(event.getMessage().getTextContent());
}
})
.blockLast();
14.5 完整示例:研究助理系统
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.InMemoryMemory;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.model.DashScopeChatModel;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.core.tool.subagent.SubAgentConfig;
import io.agentscope.core.tool.subagent.SubAgentTool;
import java.util.Scanner;
/**
* 研究助理系统
* 主智能体协调多个专业子智能体完成复杂研究任务
*/
public class ResearchAssistantSystem {
public static void main(String[] args) {
String apiKey = System.getenv("DASHSCOPE_API_KEY");
// ========================================
// 创建专业子智能体工具
// ========================================
// 文献研究员
SubAgentTool literatureReviewer = new SubAgentTool(
() -> createSpecialist(apiKey, "LiteratureReviewer", """
你是一位文献研究专家。
你的专长:
1. 查找和分析学术文献
2. 总结研究现状和趋势
3. 识别研究空白和机会
请提供详细的文献综述和分析。
"""),
SubAgentConfig.builder()
.toolName("call_literature_reviewer")
.description("调用文献研究员进行学术文献检索和综述")
.forwardEvents(true)
.build()
);
// 数据分析师
SubAgentTool dataAnalyst = new SubAgentTool(
() -> createSpecialist(apiKey, "DataAnalyst", """
你是一位数据分析专家。
你的专长:
1. 分析数据模式和趋势
2. 进行统计推断
3. 可视化数据洞察
请提供数据驱动的分析和建议。
"""),
SubAgentConfig.builder()
.toolName("call_data_analyst")
.description("调用数据分析师进行数据分析和解读")
.forwardEvents(true)
.build()
);
// 报告撰写员
SubAgentTool reportWriter = new SubAgentTool(
() -> createSpecialist(apiKey, "ReportWriter", """
你是一位专业报告撰写员。
你的专长:
1. 组织和结构化信息
2. 撰写清晰专业的报告
3. 提炼关键发现和建议
请将研究结果整理成结构化的报告。
"""),
SubAgentConfig.builder()
.toolName("call_report_writer")
.description("调用报告撰写员整理和撰写研究报告")
.forwardEvents(true)
.build()
);
// ========================================
// 创建主研究协调员
// ========================================
Toolkit toolkit = new Toolkit();
toolkit.registerAgentTool(literatureReviewer);
toolkit.registerAgentTool(dataAnalyst);
toolkit.registerAgentTool(reportWriter);
ReActAgent coordinator = ReActAgent.builder()
.name("ResearchCoordinator")
.sysPrompt("""
你是一位研究项目协调员。
你可以调用以下专业助手:
- call_literature_reviewer: 进行文献研究
- call_data_analyst: 进行数据分析
- call_report_writer: 撰写研究报告
工作流程:
1. 理解用户的研究需求
2. 分解任务并分配给合适的专家
3. 整合各专家的结果
4. 提供最终的研究成果
请高效协调团队完成研究任务。
""")
.model(DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-max")
.stream(true)
.build())
.toolkit(toolkit)
.memory(new InMemoryMemory())
.maxIters(15) // 增加迭代次数以支持多工具调用
.build();
// ========================================
// 交互式对话
// ========================================
System.out.println("=== 研究助理系统 ===");
System.out.println("可用专家: 文献研究员、数据分析师、报告撰写员");
System.out.println("输入 'exit' 退出\n");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("你: ");
String input = scanner.nextLine().trim();
if ("exit".equalsIgnoreCase(input)) {
break;
}
if (input.isEmpty()) continue;
System.out.print("协调员: ");
coordinator.stream(input)
.doOnNext(event -> {
if (event.getMessage() != null) {
String text = event.getMessage().getTextContent();
if (text != null && !text.isEmpty()) {
System.out.print(text);
}
}
})
.blockLast();
System.out.println("\n");
}
scanner.close();
}
private static ReActAgent createSpecialist(
String apiKey, String name, String prompt) {
return ReActAgent.builder()
.name(name)
.sysPrompt(prompt)
.model(DashScopeChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-plus")
.stream(true)
.build())
.memory(new InMemoryMemory())
.toolkit(new Toolkit())
.build();
}
}
14.6 最佳实践
14.6.1 设计建议
| 建议 | 说明 |
|---|---|
| 明确分工 | 每个子智能体应该有明确的专业领域 |
| 清晰描述 | 工具描述应该让主智能体知道何时调用 |
| 适度层级 | 避免过深的嵌套调用 |
| 会话管理 | 复杂任务使用会话保持上下文 |
| 错误处理 | 处理子智能体可能的失败情况 |
14.6.2 工具描述示例
// 好的工具描述
SubAgentConfig.builder()
.toolName("call_code_reviewer")
.description("""
调用代码审查专家。使用场景:
- 需要审查代码质量和安全性
- 需要代码优化建议
- 需要检查最佳实践合规性
输入: 要审查的代码或代码描述
输出: 详细的审查报告和建议
""")
.build();
// 避免的描述
SubAgentConfig.builder()
.toolName("call_agent")
.description("调用一个智能体") // 太模糊
.build();
14.6.3 与其他模式的对比
| 模式 | 适用场景 | 通信方式 |
|---|---|---|
| Agent-as-Tool | 层级协作,主从关系 | 工具调用 |
| MsgHub | 平等对话,多方讨论 | 消息广播 |
| Pipeline | 流水线处理,顺序执行 | 输出传递 |
| A2A | 分布式部署,跨服务调用 | 网络协议 |
14.7 本章小结
本章介绍了 Agent-as-Tool 模式:
- 核心概念:将智能体封装为工具,实现层级协作
- SubAgentTool:子智能体工具的核心实现
- SubAgentConfig:配置选项(名称、描述、会话、流式输出)
- 多轮对话:通过 session_id 保持上下文
- 事件转发:支持流式输出到主智能体
Agent-as-Tool 模式非常适合构建"主管-专家"类型的多智能体系统。
练习
- 创建一个包含 3 个专业子智能体的客服系统
- 实现带会话管理的多轮对话子智能体
- 比较 Agent-as-Tool 和 MsgHub 在同一任务上的使用差异
- 尝试嵌套调用:主智能体 → 子智能体 → 孙智能体
上一章:第13章-多智能体辩论 | 下一章:第15章-A2A协议