Agents 核心概念:模型、工具与调度策略
上节搭好了服务端包结构,这节深入 agents 包的核心设计——如何让 AI 自主决策调用什么工具、怎么调度多步任务,以及自己造不出来时从社区借哪股力。
Agent 是什么
大模型能力出众,一半得益于预训练模型本身越来越强,另一半得益于程序员的奇思妙想——ChatGPT 惊艳所有人之后,大家开始想:这么聪明的东西,能不能帮我们编程、帮我们干活?这种"让模型调用工具、按策略行事"的能力组合,就是 Agent(代理)。
普通用户的聊天是"问什么答什么",全部是文字输入输出。Agent 不一样:
用户输入 → Agent 思考 → 决定是否调用工具 → 调用工具 → 获得结果 → 继续思考 → 回复用户
text
关键区别:
- 普通聊天:用户问什么,模型答什么
- Agent:模型作为中枢大脑,自主决定调用什么工具;工具结果响应回来后,拼接出一个完整的答案
这个循环就是 ReAct 模式(Reason 思考 + Act 行动)。有一个细节容易被忽略:模型怎么知道有哪些工具可用?答案是预制的提示词——系统提示词里明确告诉它有什么工具可以调用(工具的 description 和参数 schema 会随请求一并传给模型)。现在的工具层出不穷(OpenClaw、各类 Harness),但万变不离其宗:流程和架构更复杂了,又结合 MCP 等技术把功能做得更丰富,核心循环还是这一个。
三层架构
┌─────────────────────────┐
│ 调度层 (Orchestrator) │ 控制对话流程、决定下一步
├─────────────────────────┤
│ 工具层 (Tools) │ 搜索、数据库、API 调用、沙箱、CLI
├─────────────────────────┤
│ 模型层 (Model) │ OpenAI / Claude / Gemini(可切换)
└─────────────────────────┘
text
拿 OpenClaw 对照着看,三层立刻具象化:它提供一系列官方工具(工具层),底下可对接各家模型——根据你输入的密钥信息调用不同 provider(模型层,甚至可以让不同模型完成特定任务:一个做前端、一个做后端),而调度层就是 OpenClaw 框架本身在做的事。Codex 也一样有调度层,而且它把 Harness 这一层开放了出来——每个人都可以基于 Codex 的代码做出自己本地的一个类 Codex 工具。想深入理解调度层的设计,可以让 AI 读一读这些主流项目(OpenClaw、Codex)的源码。
模型层:统一接口
所有模型适配器实现同一个接口:
// packages/agents/src/types.ts(真实源码)
export interface IModelAdapter {
chat(request: ChatRequest): Promise<ChatResponse>
chatStream(request: ChatRequest): AsyncGenerator<StreamChunk>
}
typescript
注册多个模型(真实源码节选,完整实现见 15-01):
export class ModelRegistry {
private models = new Map<string, RegisteredModel>()
register(config: ModelConfig): this // 内置协议:openai-compat / anthropic
registerAdapter(id, adapter, info): this // 自定义适配器(mock、Mastra 都走这里)
get(id: string): RegisteredModel // 未注册 id 抛错(消息包含 id)
list(): ModelPublicInfo[] // 脱敏公开视图
}
typescript
工具层:定义与执行
工具需要同时满足两个角色——给 AI 看的描述(让它知道什么时候调用)和给系统用的执行器:
// packages/tools/src/definitions.ts
// 天气查询工具
export const weatherTool: ToolExecutor = {
definition: {
name: 'get_weather',
description: '获取指定城市的当前天气',
parameters: {
city: { type: 'string', description: '城市名', required: true },
unit: { type: 'string', description: '温度单位,celsius 或 fahrenheit' },
},
},
async execute(params) {
const response = await fetch(
`https://api.weather.com/current?city=${params.city}`
)
const data = await response.json()
return { success: true, data }
},
}
typescript
调度层:Agent 循环
调度层实现 Agent 的核心循环——ReAct 模式(Reason + Act):
// packages/agents/src/orchestrator.ts
export class Orchestrator {
constructor(
private model: IModelAdapter,
private tools: ToolRegistry,
) {}
async *run(messages: Message[]): AsyncGenerator<AgentEvent> {
let currentMessages = [...messages]
for (let step = 0; step < 10; step++) { // 最多 10 步
const response = await this.model.chat(currentMessages, {
tools: this.tools.list(),
})
if (response.type === 'text') {
yield { type: 'message', content: response.content }
return // 回复完成
}
if (response.type === 'tool_call') {
yield { type: 'tool_call', name: response.toolName, args: response.args }
const tool = this.tools.get(response.toolName)
const result = await tool.execute(response.args)
yield { type: 'tool_result', name: response.toolName, result }
// 把工具结果加入上下文,继续循环
currentMessages.push(
{ role: 'assistant', content: response.raw },
{ role: 'tool', content: JSON.stringify(result) },
)
}
}
yield { type: 'error', content: '超过最大步数限制' }
}
}
typescript
调度策略
调度策略只有三种,全是基础编程知识,看名词就知道在干什么:
串行调度
工具一个接一个调用,一定等到上一个结果响应回来再做下一件事:
// 适合:每步依赖上一步结果
// 例如:先查天气 → 再根据天气推荐活动
typescript
并行调度
多个独立工具同时调用:
// 适合:工具之间无依赖(多 Agent 团队协作就是并行)
// 例如:同时查天气和查新闻
const results = await Promise.all([
weatherTool.execute({ city: '北京' }),
newsTool.execute({ topic: '科技' }),
])
typescript
条件调度
让模型决定走哪条路:
// 模型决定走哪条路径
if (response.toolName === 'search_database') {
// 数据库查询路径
} else if (response.toolName === 'search_web') {
// 网络搜索路径
}
typescript
外加循环——这四种组合起来,就是后面 16 章多 Agent 协作(Workflows 串行/并行/条件分支)的雏形。
流式输出
Agent 的流式输出比普通聊天复杂——需要同时处理文本流和工具调用:
// packages/agents/src/stream.ts
export type AgentEvent =
| { type: 'text_delta'; content: string } // 文本增量
| { type: 'tool_call'; name: string; args: Record<string, unknown> }
| { type: 'tool_result'; name: string; result: ToolResult }
| { type: 'message'; content: string } // 完整消息
| { type: 'error'; content: string }
// 使用
for await (const event of orchestrator.run(messages)) {
if (event.type === 'text_delta') {
process.stdout.write(event.content)
}
if (event.type === 'tool_call') {
console.log(`调用工具: ${event.name}`)
}
}
typescript
从社区借力:Mastra 与 n8n
一个非常现实的问题:想自己做一个 Agent,直接让 AI 写也能写出来,但封装往往一般。要做好,需要借助社区的力量。两个主流方案:
| 维度 | Mastra | n8n |
|---|---|---|
| 定位 | TypeScript Agent 框架(构建 Agent 的工具集) | 流程编排平台(可视化工作流) |
| 技术栈 | TypeScript,跑 Node/边缘运行时 | Node.js 驱动(对比 Dify 是 Python 写的,并发与技术栈对前端不够友好) |
| 使用方式 | 代码内集成进自己的项目 | 拖拉拽创建工作流,也可让 AI 直接生成整个工作流 |
| 许可 | 开源 | fair-code(源码可见、可自托管,2600+ 集成,云端版 €20/月起——2026-11-07 核实) |
| 适合 | 写代码的前端开发者,要深度定制 | 流程冗长、且有不懂编程的成员参与流程定制的团队 |
为什么选 Mastra
调研过程(用 5-02 的 local-search skill 走完)落在了 Mastra 上,理由很具体:
- TypeScript 写的,对前端技术栈的伙伴天然友好;解释性语言跑在边缘服务器上,能以轻量方式、高并发性能处理 AI 会话消息服务;
- 高定制化,同时把普通程序员能开发的 AI 天花板拉得很高——它不像 OpenClaw 或完整 Harness 那样复杂,而是把主流 AI 程序需要的核心部分在框架里设计好了(结构化输出、人机交互 HITL、护栏/拦截器、处理器、代码模式等),你拿到框架往里填充内容就行;
- 能力覆盖全:Agents(创建 agent、自定义工作流,内含串行/并行/循环)、Harness 级的综合能力(自主思考、自主决策)、Memory(记忆,对标 RAG/知识库)、server(中转服务,对外提供 SSE/流式响应,也可对接各家 provider)。
看 Mastra 文档全是英文也没关系——浏览器翻译成中文后再看,它和主流框架的兼容一目了然。文档站还配了学习平台:什么是代理人(YouTube 视频)、运行你的第一个代理、项目结构、创建代理、提示词构建、调用工具、利用先前的上下文解决后续问题、通过调整模型指令调整行为——和本章讲的三层架构完全对得上。GitHub 29k+ star(2026-11-07 核实),社区活跃度足够。
n8n 的位置
n8n 部署下来就是自己的一套工作流平台,拖拉拽创建工作流(现在也可以交给 AI 定制:告诉它你需要什么样的工作流,它直接写出来)。与其说是"界面化定制",不如说是给了一个底层工具把工作流完全交给 AI 处理。它有不方便写代码的场景:流程冗长,同时需要不懂编程的小伙伴参与流程定制——这时界面化方式就比纯编码好。
把文档丢给 AI:最高效的集成方式
集成 Mastra 到项目,实操就一句话:打开 Claude Code,把项目相关文件 + Mastra 官方文档链接一起作为上下文,发一句"结合上面的文档把 Mastra 接入平台"。课程实测这一轮跑下来,AI 连文档部分都整合好了,还自动创建了两个侧边栏菜单——全程没有任何提示词干预(装了 skills、加了 superpowers 这类技能包后,AI 自己头脑风暴出功能方案)。
找到正确的文档入口有固定路径:Mastra 文档顶部 Integration → Frameworks 可以找到 Hono 的官方集成方式,复制链接交给大模型就能完成整个 Hono 应用的改造。左侧除 Hono 外还有 Express、Next.js、Nuxt(Vue SSR)、React、Spring(Java AOP)等主流框架的集成方案;消息相关叫 Channels;和 Agent UI 相关的有官方 UI 推荐;往下还有 Sandboxes、Observability(观察)、数据库、部署工具、音频、健全、浏览器和文件系统——每个人的集成效果可能不同,但路径一致。
一个兜底技巧:如果直接丢 URL 给大模型不行(有些站点反爬),去它的 GitHub 官方仓库找 docs 目录,里面的 markdown 是纯文本,直接丢给大模型一样有效。
生成方案的架构分层(AI 自己画出来的,与 15-01 的设计完全吻合):服务端网关做统一适配器(鉴权、限流、协议归一)→ 适配器层适配各家模型服务商与 workflow 服务 → 对接上游大模型;智能体有两种接入方式:服务端直接注册,或经代码内的 Mastra 直连;模型调用(上游 URL、API Key)走 .env 配置。
用 AI 生成 Agent 代码
> 请为 @ai-chat/agents 包实现 Orchestrator 调度器:
> 1. 支持 ReAct 循环(思考→调用工具→获得结果→继续思考)
> 2. 最大步数 10 步防止无限循环
> 3. 流式输出 AsyncGenerator<AgentEvent>
> 4. 工具调用结果自动加入上下文
>
> 类型定义参考 packages/agents/src/types.ts
text
落地对照:调度层的两种实现路径
本节示意的是自研调度层(Orchestrator 手写 ReAct 循环)。2026-08 ai-chat-ui 平台实测了另一条路径——用框架承担调度层:Mastra Agent 内置 ReAct 循环(工具调用 → 结果回填 → 继续思考),通过 @toimc/agents/mastra 子路径的 MastraAdapter 包装成 IModelAdapter,注册进同一个 ModelRegistry。三层架构不变,只是「调度层」从手写换成框架:
| 层 | 自研路径(本节示意) | 框架路径(平台实测) |
|---|---|---|
| 模型层 | IModelAdapter + ModelRegistry | 相同(MastraAdapter 是其中一个实现) |
| 工具层 | ToolExecutor(definition + execute) | createTool + zod schema |
| 调度层 | 手写 Orchestrator(步数上限 + 循环) | Mastra Agent 内置循环 |
两条路径的选择标准:需要完全掌控循环细节(自定义步数策略/中断恢复)选自研;要快速获得工具调用 + 记忆 + 监控的完整能力选框架。事件流形态一致——都归一到 AgentEvent/StreamChunk,前端组件无感知。
小结
- Agent 三层架构:模型层(统一接口)→ 工具层(定义+执行)→ 调度层(ReAct 循环);OpenClaw/Codex 内部就是这套
- 调度策略:串行(有依赖)、并行(无依赖)、条件(模型决策)、循环——16 章 Workflows 的雏形
- 自己封装质量一般时从社区借力:Mastra(TS 框架,前端友好)与 n8n(流程编排平台,fair-code 自托管)两条路
- 集成最快路径:官方文档 Integration → Frameworks → Hono 链接直接丢给大模型;反爬站点用 GitHub docs 的 markdown 兜底
- 流式输出用 AsyncGenerator,同时处理文本增量和工具调用事件;自研与框架两条调度层路径事件流形态一致
下一节讲 Hono 框架实战和 mastra.ai 接入——把 Agent 能力暴露为 HTTP API。
↑