AgentScope 2.0 工具加载机制:Skills、MCP 与自定义 Tools 的一次讲清
前言
在构建 AI Agent 时,「让大模型用上工具」几乎是最关键的环节。AgentScope 2.0 把这一环节拆成了三个互补的能力:
- Tool(自定义工具):开发者用 Java 写的本地方法,最贴近业务
- MCP(Model Context Protocol):跨进程、跨语言的标准工具协议,复用生态
- Skill(技能):基于 Markdown 的指令包,不动代码就能扩展 Agent 能力
这三类能力既独立又统一——最终都汇入 Toolkit,并以 JSON Schema 的形式暴露给 LLM。本文以 [agentscope2.0demo](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo) 项目为示例,结合源码讲清楚它们各自的加载流程与协作方式。
一、项目概览
在深入加载机制之前,先认识一下示例项目本身。这个 demo 是一个基于 Spring Boot 的 Chat Service,用 AgentScope 2.0 把一个 ReActAgent 包装成 HTTP 接口,演示了 Tool/MCP/Skill 三种能力的完整集成。

1.1 技术栈与版本
项目依赖在 [pom.xml](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/pom.xml) 中声明,核心版本如下:
| 框架 / 库 | 版本 | 用途 |
|---|---|---|
| JDK | 17 | 运行时基线 |
| Spring Boot | 3.5.0 | Web 框架与 IoC 容器 |
| AgentScope | 2.0.0 | Agent 核心框架(agentscope-core + agentscope-extensions-model-openai) |
| MCP SDK | 0.17.0 | Model Context Protocol 客户端实现(版本由 AgentScope 2.0.0 决定) |
| OpenTelemetry | 1.61.0 | 可观测性 API/SDK + OTLP Exporter |
| OpenTelemetry SemConv | 1.27.0-alpha | GenAI 语义约定 |
| Lombok | 随 Spring Boot 父 POM | 减少样板代码 |
其中 OpenTelemetry 相关依赖主要为可观测性预留,本文不展开,后续会单独成文。
1.2 代码结构
项目采用典型的 Spring Boot 分层结构:
agentscope2.0demo/
├── pom.xml # Maven 依赖与构建配置
├── src/main/
│ ├── java/com/conca/chat/
│ │ ├── ChatApplication.java # Spring Boot 启动入口
│ │ ├── config/ # 配置层(装配核心 Bean)
│ │ │ ├── AgentConfig.java # ★ ReActAgent + Toolkit 装配
│ │ │ ├── McpToolkitConfig.java # ★ MCP 客户端装配
│ │ │ ├── ModelConfig.java # OpenAIChatModel 装配
│ │ │ ├── WorkspaceLoader.java # 工作区扫描(subagents 等)
│ │ │ ├── TracingConfig.java # 可观测性配置
│ │ │ └── LangfuseEnrichmentMiddleware.java
│ │ ├── controller/ # HTTP 接口层
│ │ │ ├── ChatController.java # /chat 对话接口
│ │ │ └── DebugController.java # 调试接口
│ │ ├── dto/ # 数据传输对象
│ │ │ ├── ChatRequest.java
│ │ │ ├── ChatResponse.java
│ │ │ └── StreamEvent.java
│ │ ├── session/
│ │ │ └── SessionManager.java # 会话管理
│ │ └── tool/
│ │ └── CustomTools.java # ★ @Tool 自定义工具
│ └── resources/
│ └── application.yaml # ★ 配置入口(workspace、mcp.clients)
├── workspace/ # 运行时工作区
│ ├── AGENTS.md # 系统提示词补充
│ ├── skills/ # ★ Skill 仓库
│ │ └── demo-skill/SKILL.md
│ ├── subagents/ # 子智能体定义
│ │ └── helper.md
│ └── sessions/ # 会话状态持久化目录
│ └── __anon__/<sessionId>/agent_state.json
└── io/agentscope/core/ # AgentScope 核心源码(参考阅读用)
├── ReActAgent.java
├── skill/ # Skill 子系统
│ ├── AgentSkill.java
│ └── repository/FileSystemSkillRepository.java
└── tool/
├── Toolkit.java # ★ 工具容器核心
└── mcp/ # MCP 客户端
1.3 关键约定
从结构上能看出几个项目约定:
- 配置集中在
config/包:所有 Spring@Configuration/@Bean都在这里,业务代码不感知装配细节; - 工具与 Agent 解耦:
CustomTools是纯业务工具,AgentConfig负责把它和 MCP、Skill 装到一起; - workspace 是运行时数据根:Skill、subagent、session 状态都在这个目录下,便于热更新与备份;
io/agentscope/core/是 AgentScope 框架源码:放在项目里只是为了方便阅读源码,不是业务代码。
1.4 装配链路一览
后面几章会展开细节,这里先给一张装配链路速查:
ChatApplication (启动)
│
▼
Spring 自动扫描 @Configuration
│
├─ ModelConfig → OpenAIChatModel
├─ McpToolkitConfig → List<McpClientWrapper> ← application.yaml:mcp.clients
├─ AgentConfig → Toolkit (含 CustomTools + MCP)
│ → FileSystemSkillRepository ← workspace/skills/
│ → ReActAgent
├─ WorkspaceLoader → 扫描 workspace/subagents/
└─ TracingConfig → 可观测性
│
▼
ChatController 注入 ReActAgent,对外暴露 /chat
带着这张地图,下面进入正式的加载机制讲解。
二、整体架构
AgentScope 把 tool 相关的构件组织成三个概念:
| 概念 | 角色 | 注册入口 |
|---|---|---|
| Tool | 任意实现 AgentTool 接口或标注 @Tool 注解的对象 | Toolkit.registerTool() |
| MCP Client | 通过 MCP 协议连接的外部工具服务器 | Toolkit.registerMcpClient() |
| Skill | Markdown 写成的指令包,附带可选资源文件 | ReActAgent.builder().skillRepository() |
注意:Skill 是「指令 + 资源」,不会直接出现在
Toolkit的工具表里;Agent 在推理过程中通过load_skill_through_path这个内置工具按需读取 Skill 内容。这是 Skill 与 Tool/MCP 在加载方式上最大的区别。
加载流程可以浓缩成下面这张图:
┌─────────────────────┐
│ application.yaml │ ← 配置入口(workspace、mcp.clients)
└──────────┬──────────┘
│
┌───────────────┼────────────────┐
▼ ▼ ▼
CustomTools McpToolkitConfig skillRepository()
(@Tool 注解) (McpClientWrapper) (FileSystemSkillRepository)
│ │ │
└──────►Toolkit◄─┘ │
│ │
▼ ▼
ReActAgent.builder().toolkit(...).skillRepository(...)
│
▼
ReActAgent(推理循环中把 schema 暴露给 LLM)
下面分别展开。
三、自定义工具(Tool)的加载
3.1 用 @Tool 注解定义工具
最简单的扩展方式:在一个 Spring @Component 上标注 @Tool 方法。项目中的 [CustomTools.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/tool/CustomTools.java) 就是范例:
@Component
public class CustomTools {
@Tool(
name = "get_current_datetime",
description = "获取当前日期和时间",
readOnly = true,
concurrencySafe = true
)
public String getCurrentDateTime() {
LocalDateTime now = LocalDateTime.now();
return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
@Tool 注解的常用属性:
| 属性 | 说明 |
|---|---|
name | 工具名(默认取方法名) |
description | 面向 LLM 的工具说明,决定模型何时调用 |
readOnly | 是否只读(影响权限判定) |
concurrencySafe | 是否可并发调用 |
stateInjected | 是否注入 AgentState |
dangerousFiles / dangerousDirectories | 自定义危险路径 |
3.2 通过 Toolkit.registerTool 注册
在 [AgentConfig.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/config/AgentConfig.java#L34-L51) 中,Toolkit 由 Spring 装配:
@Bean
public Toolkit toolkit(CustomTools customTools,
List<McpClientWrapper> mcpClientWrappers) {
Toolkit toolkit = new Toolkit();
// 1. 注册自定义工具(扫描 @Tool 注解)
toolkit.registerTool(customTools);
// 2. 注册所有 MCP 客户端
for (McpClientWrapper wrapper : mcpClientWrappers) {
toolkit.registerMcpClient(wrapper).block();
}
return toolkit;
}
Toolkit.registerTool(Object) 内部的扫描逻辑(见 [Toolkit.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/io/agentscope/core/tool/Toolkit.java#L154-L197)):
- 遍历对象的所有声明方法;
- 对带
@Tool注解的方法,解析name/description/converter等元数据; - 用
ToolSchemaGenerator生成参数的 JSON Schema; - 包装成
ReflectiveFunctionTool(ToolBase的子类),统一进入权限、并发、流式输出的处理链。
注册完成后,工具名会被记入 ToolRegistry,并在每次推理时通过 ToolSchemaProvider 把可见工具的 schema 一并提交给 LLM。
四、MCP 客户端的加载
4.1 配置驱动:application.yaml
MCP 客户端完全由配置驱动,无需写代码:
mcp:
clients:
- name: file-list-server
url: http://localhost:8765/mcp
transport: auto
transport: auto表示根据 URL 后缀自动判断:以/sse结尾走 SSE,否则走 Streamable HTTP。name是 MCP 客户端的唯一标识,用于日志与工具归属。
4.2 构造 McpClientWrapper 列表
[McpToolkitConfig.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/config/McpToolkitConfig.java) 把 yaml 中的每条配置转成一个 McpClientWrapper:
@Bean
public List<McpClientWrapper> mcpClientWrappers(McpClientsProperties props) {
List<McpClientWrapper> wrappers = new ArrayList<>();
for (McpClientConfig cfg : props.getClients()) {
wrappers.add(buildClient(cfg)); // 单个 client 失败不影响其他
}
return wrappers;
}
private McpClientWrapper buildClient(McpClientConfig config) {
McpClientBuilder builder = McpClientBuilder.create(config.getName());
if ("sse".equalsIgnoreCase(transport)
|| ("auto".equalsIgnoreCase(transport) && url.endsWith("/sse"))) {
builder.sseTransport(url);
} else {
builder.streamableHttpTransport(url); // 默认走 Streamable HTTP
}
return builder.buildSync();
}
AgentScope 的 MCP 客户端支持三种传输:
| 传输方式 | 适用场景 | Builder |
|---|---|---|
| Streamable HTTP | 默认推荐,性能更好 | McpClientBuilder.streamableHttp() |
| SSE | 兼容老版本 MCP 服务器 | McpClientBuilder.sse() |
| STDIO | 启动本地子进程型 MCP 服务器 | McpClientBuilder.stdio() |
4.3 注册到 Toolkit
回到上面 Toolkit toolkit(...) 这个 Bean——它会把 List<McpClientWrapper> 全部注入:
for (McpClientWrapper wrapper : mcpClientWrappers) {
try {
toolkit.registerMcpClient(wrapper).block();
log.info("Registered MCP client: {}", wrapper.getName());
} catch (Exception e) {
log.warn("Failed to register MCP client '{}': {}", wrapper.getName(), e.getMessage());
}
}
Toolkit.registerMcpClient 把工作委托给 McpClientManager:
- 调用 MCP 协议的
listTools拿到远端工具列表; - 把每个远端工具包装成
AgentTool,注册到ToolRegistry; - 同时记录这些工具属于哪个 MCP 客户端,便于后续按 client 维度卸载。
注册完成后,MCP 工具和本地 @Tool 方法在 Toolkit 里地位完全一致——LLM 看到的都是 JSON Schema,调用流程也走统一的 ToolExecutor。
4.4 高级用法:Builder API
如果想对 MCP 工具做更细粒度的控制(白名单、分组、预设参数),可以用流式 API:
toolkit.registration()
.mcpClient(mcpClientWrapper)
.enableTools(List.of("read_file", "list_files")) // 只启用这两个工具
.group("file-ops") // 归入 file-ops 分组
.presetParameters(Map.of( // 自动注入参数
"read_file", Map.of("encoding", "UTF-8")))
.apply();
五、Skill 的加载
Skill 是 AgentScope 里最「轻量」的扩展方式——不写一行 Java 代码,只要放一个 Markdown 目录就够了。
5.1 Skill 目录结构
项目中的 demo skill 位于 [workspace/skills/demo-skill/SKILL.md](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/workspace/skills/demo-skill/SKILL.md):
workspace/skills/
└── demo-skill/
└── SKILL.md
SKILL.md 由两部分组成:YAML frontmatter + Markdown 正文。
---
name: demo-skill
description: 示例技能,展示如何为 Agent 添加自定义能力
version: 1.0.0
---
# Demo Skill
## 使用场景
当用户询问时间时,Agent 可以使用此技能中的工具。
## 可用工具
- `getCurrentDateTime` - 获取当前日期时间
如果还需要附带参考资料或脚本,可以扩展成:
demo-skill/
├── SKILL.md # 必需
├── references/ # 可选:参考文档
│ └── style-guide.md
└── scripts/ # 可选:可被 shell 调用的脚本
└── run.sh
5.2 FileSystemSkillRepository:从磁盘加载
[AgentConfig.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/config/AgentConfig.java#L53-L57) 中只用了两行就完成了 Skill 仓库的装配:
@Bean
public FileSystemSkillRepository skillRepository() {
Path skillsPath = Paths.get(workspacePath, "skills");
return new FileSystemSkillRepository(skillsPath, false);
}
构造参数:
baseDir:技能根目录(这里是./workspace/skills)writeable:是否允许运行时写入/删除 skill
FileSystemSkillRepository 的加载细节(见 [FileSystemSkillRepository.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/io/agentscope/core/skill/repository/FileSystemSkillRepository.java#L179-L220))有几个值得注意的点:
- 目录扫描:
getAllSkills()遍历baseDir下的一级子目录,每个子目录必须包含SKILL.md; - 按需缓存:用
Map<Path, Snapshot>缓存已解析的AgentSkill,并以 SKILL.md 的mtime + size作为失效依据——重复调用getAllSkills()不会重复读盘; - 资源加载策略:构造时通过
lazy参数控制是否预读所有支持文件。demo 项目里lazy=false,会把 SKILL.md 和支持文件都读进内存; - 目录消失时自动清理缓存,避免已删除的 skill 仍被引用。
5.3 把 SkillRepository 注入 Agent
ReActAgent 在构造时接收 skillRepository:
@Bean
public ReActAgent chatAgent(OpenAIChatModel model, Toolkit toolkit,
WorkspaceLoader workspaceLoader,
FileSystemSkillRepository skillRepository) {
return ReActAgent.builder()
.name("chat-agent")
.sysPrompt(sysPrompt)
.model(model)
.toolkit(toolkit) // 工具 + MCP 都在这里
.skillRepository(skillRepository) // skill 仓库
.enableMetaTool(true)
.maxIters(10)
.build();
}
5.4 Agent 如何“使用” Skill
这是 Skill 和 Tool/MCP 在机制上最大的不同:
- 推理前:Agent 把所有 Skill 的
name + description列在 system prompt 的<available_skills>块里(仅元信息,不占大量 token); - 推理中:当 LLM 觉得需要某个 Skill 时,会调用内置工具
load_skill_through_path:load_skill_through_path(skillId, path="SKILL.md")—— 读取 Markdown 正文load_skill_through_path(skillId, path="references/style-guide.md")—— 读取任意资源文件
- 加载后:Agent 把 Skill 内容拼入上下文,再按 Skill 中的指令执行后续动作(可能调用其他工具)。
这种「按需加载」的设计让 Skill 适合承载体积较大、低频使用的能力——既不挤占每次推理的上下文,又能在需要时被精准唤起。
5.5 多源 Skill 仓库
FileSystemSkillRepository 只是其中一种实现。AgentScope 还提供:
| 实现 | 适用场景 |
|---|---|
GitSkillRepository | 团队共享、版本化管理的 skill 库 |
NacosSkillRepository | 配置中心动态分发 |
MysqlSkillRepository | 数据库存储、带审计 |
ClasspathSkillRepository | 随 jar 打包的内置 skill |
可以多次调用 .skillRepository(repo) 接入多个源,后注册的同名 skill 会覆盖先注册的。
六、三种能力如何在 ReActAgent 中协作
6.1 装配关系
回到 [AgentConfig.java](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/config/AgentConfig.java#L59-L85),可以清晰看到三者的汇合点:
ReActAgent.builder()
.name("chat-agent")
.model(model) // LLM
.toolkit(toolkit) // ← Tool + MCP 在这里
.skillRepository(skillRepository) // ← Skill 在这里
.enableMetaTool(true) // 让 Agent 能自我管理工具分组
.maxIters(10)
.build();

6.2 推理循环里的统一视图
每次用户发消息,ReActAgent 的 ReAct 循环大致这样运转:
- 构造 system prompt:包含原始 sysPrompt、可用 skill 列表、当前激活工具分组的说明;
- 收集 tool schemas:
Toolkit.getToolSchemas()返回所有可见工具(含@Tool、MCP、内置 meta tool),交给 LLM; - LLM 决策:模型返回文本或 tool_call;
- 工具执行:调用
Toolkit.callTools(),由ToolExecutor并发执行(受concurrencySafe标记约束); - 结果回填:工具结果作为
ToolResultBlock加入消息,进入下一轮推理; - 循环直到:模型不再调用工具,或达到
maxIters。
Skill 在这套循环中扮演的是「上下文增强」的角色——它本身不参与工具 schema 列表,而是通过 load_skill_through_path 把指令注入对话,再由 LLM 在后续轮次里调度 Tool/MCP 去落地。
6.3 工具分组(Tool Group):动态开关
Toolkit 还支持把工具划分到不同的 group 里,运行时按需激活。enableMetaTool(true) 会注入一个名为 reset_equipped_tools 的内置工具,让 LLM 自己决定哪些分组可见。这在工具数量多、需要按场景裁剪时非常有用。
ToolGroup database = new ToolGroup(
"database",
"数据库相关工具",
ToolGroupScope.SESSION,
false); // 初始未激活
database.addTool("db_query");
toolkit.registerToolGroup(database);
七、配置一览
把整个加载链路用 [application.yaml](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/resources/application.yaml) 串起来:
agentscope:
workspace: ./workspace # Skill 和 subagent 的根目录
sessions-dir: ./workspace/sessions
mcp:
clients: # MCP 客户端列表
- name: file-list-server
url: http://localhost:8765/mcp
transport: auto
- 改
workspace路径,Skill 和 subagent 的加载位置会同步变化; - 增减
mcp.clients条目,就能挂载/卸载外部 MCP 服务器; - 自定义工具通过 Spring
@Component自动扫描注入,不在这里配置。
八、快速验证:一次端到端测试
配置就绪后,启动 Spring Boot 应用(mvn spring-boot:run),用一条 PowerShell 命令即可验证整条加载链路是否打通。下面这个用例会让 Agent 调用 MCP 工具 file-list-server 来列出当前目录下的文件:
PS C:\Users\conca> Invoke-RestMethod -Uri http://localhost:8080/api/chat `
>> -Method Post `
>> -Headers @{"Content-Type"="application/json; charset=utf-8"} `
>> -Body ([System.Text.Encoding]::UTF8.GetBytes('{"message":"请列出当前目录下的文件"}'))
sessionId reply
--------- -----
62186f9a-d1f3-4277-8476-fabebf684d23 起来你的系统根目录是 **C:\** ,以下是该目录下的...
8.1 这条命令做了什么
| 要素 | 说明 |
|---|---|
| 接口 | POST /api/chat,由 [ChatController](file:///c:/me/kaiyuan/agenttrace/agentscope2.0demo/src/main/java/com/conca/chat/controller/ChatController.java) 暴露 |
| 请求体 | {"message":"请列出当前目录下的文件"},用 UTF-8 字节传输,避免中文乱码 |
| 触发的工具 | Agent 收到"列出文件"的请求后,会调度 MCP 客户端 file-list-server(即 application.yaml 里配置的那个) |
| 返回的 sessionId | 62186f9a-d1f3-4277-8476-fabebf684d23,对应 workspace/sessions/__anon__/ 下的状态文件,可用于多轮对话 |
8.2 背后发生了什么
这一次看似简单的调用,实际上完整跑通了本文介绍的三条加载链路:
- Tool 链路:
get_current_datetime已注册到Toolkit(虽然本次对话没用上,但 schema 已暴露给 LLM); - MCP 链路:Agent 通过
file-list-server这个 MCP 客户端调用了远端的文件列表工具——这正是reply里能看到目录内容的原因; - Skill 链路:
demo-skill的name + description已出现在 system prompt 的<available_skills>块里,Agent 可以按需加载。
用一张时序图概括:
PowerShell 客户端
│
│ POST /api/chat {"message":"请列出当前目录下的文件"}
▼
ChatController ──► ReActAgent.chat(msgs)
│
│ 1. 构造 system prompt(含 <available_skills>)
│ 2. 收集 tool schemas(@Tool + MCP 工具)
│ 3. 调用 LLM
▼
OpenAIChatModel (via OpenRouter)
│
│ 返回 tool_call: file-list-server 的某工具
▼
Toolkit.callTools()
│
│ 通过 MCP 协议调用 http://localhost:8765/mcp
▼
MCP Server (file-list-server)
│
│ 返回目录列表
▼
ReActAgent 拼装最终回复
│
▼
ChatController 返回 {sessionId, reply}
8.3 关于中文乱码
上面示例中,如果直接用 Invoke-RestMethod 而不指定 UTF-8 编码,回复可能出现类似 çèµ·æ¨çç³»ç» 的乱码——这是 PowerShell 5.x 默认按系统编码(GBK)解析响应体导致的。解决方案有两种:
方案一:强制 UTF-8 编码请求体(推荐)
$body = [System.Text.Encoding]::UTF8.GetBytes('{"message":"请列出当前目录下的文件"}')
Invoke-RestMethod -Uri http://localhost:8080/api/chat `
-Method Post `
-Headers @{"Content-Type"="application/json; charset=utf-8"} `
-Body $body
方案二:切换到 PowerShell 7+
PowerShell 7 默认使用 UTF-8,无需额外处理:
pwsh -c 'irm http://localhost:8080/api/chat -Method Post -ContentType "application/json; charset=utf-8" -Body ''{"message":"请列出当前目录下的文件"}'''
8.4 更多测试场景
通过调整 message 字段,可以分别验证三类能力的加载是否正常:
| 测试 message | 期望触发 | 验证目标 |
|---|---|---|
现在几点了? | get_current_datetime(@Tool) | 自定义工具注册成功 |
请列出当前目录下的文件 | file-list-server(MCP) | MCP 客户端连接与工具注册 |
加载 demo-skill 技能 | load_skill_through_path(内置工具) | Skill 仓库扫描与按需加载 |
加载 demo-skill 后,告诉我它能做什么 | Skill 加载 + 后续推理 | Skill 与推理循环的协作 |
每次对话返回的
sessionId都可以用于后续追问,实现多轮上下文。例如:{"message":"还有别的吗?","sessionId":"62186f9a-d1f3-4277-8476-fabebf684d23"}。
九、设计要点小结
回到 AgentScope 2.0 的工具加载机制,有几个设计决策值得品味:
- 统一入口
Toolkit:本地工具、MCP 工具、子 Agent 工具在注册后共用同一套执行、权限、流式输出机制,LLM 侧无需感知差异; - 配置驱动 MCP:通过 yaml + Spring 自动装配,新增一个 MCP 服务器只需改配置;
- Skill 的按需加载:用
<available_skills>列元信息 +load_skill_through_path按需取内容,平衡能力扩展与上下文成本; - 缓存与失效:
FileSystemSkillRepository用 mtime+size 做缓存键,热加载友好; - 失败隔离:单个 MCP client 注册失败只 warn 不抛,避免一处坏掉影响整体启动;
- 分组与 meta tool:当工具数量增长后,Tool Group +
reset_equipped_tools提供了运行时动态裁剪工具表面的能力。
十、结语
AgentScope 2.0 把「Agent 用工具」这件事拆成了三层:
- 想写业务逻辑 → Tool(
@Tool注解,最快上手) - 想复用生态 → MCP(标准协议,跨语言)
- 想不动代码就扩展 → Skill(Markdown 指令包,按需加载)
三者通过 Toolkit + skillRepository 在 ReActAgent 中汇合,给 LLM 呈现出一张统一的「工具表 + 技能目录」。理解了这条加载链路,后续无论是扩展现有 Agent,还是把项目迁移到生产环境,都能有的放矢。
下一篇我们会单独聊一聊这套架构在可观测性(Langfuse/OTel)上的实践——本文先聚焦「能跑起来」。
更多推荐


所有评论(0)