Minicode 打造系列 02 - 基于 AI SDK 的工具调用与多轮调用
一句话总结
让 LLM 学会"使用工具",实现链式调用和自主推理
核心笔记
- ✅ 工具调用 = LLM 生成结构化调用指令,服务器执行后返回结果
- ✅ 链式调用 = 工具结果作为下一轮输入,形成思考链
- ✅ 最多 5 轮循环,防止无限调用
- ✅ 消息累积:每次 tool-call 后追加 assistant 回复和 user 结果
学完你能做什么
- 🔧 定义自己的工具(get_weather、calculate 等)
- 🧠 实现 LLM 自主调用工具的能力
- 🔗 实现多轮链式调用(Chain-of-Thought)
- 📥 处理工具执行结果并反馈给 LLM
你现在的困境
- ❓ 想让 AI 不仅能回答,还能"做事"(查天气、计算)
- ❓ 不知道怎么让 AI 调用外部工具
- ❓ 多轮调用时消息历史怎么管理
- ❓ 工具返回结果后 AI 不继续执行
什么时候用这一招
- 需要 AI 执行实际操作(查信息、操作文件)
- 需要 AI 进行多步推理(先查天气,再推荐穿衣)
- 构建智能助手、客服机器人
- 自动化工作流
核心思路
工具调用流程

消息累积规则

退出条件判断

工具定义结构

内置工具列表

工具定义结构
每个工具包含以下属性:
| 属性 | 说明 |
|---|---|
| id | 工具唯一标识符 |
| description | 工具功能描述(LLM 会看到) |
| inputSchema | 参数 schema,定义输入格式 |
| execute | 异步执行函数,接收参数并返回结果 |
工具示例 - get_weather:
{
id: 'get_weather',
description: '获取指定城市的天气信息',
inputSchema: jsonSchema({
type: 'object',
properties: {
city: { type: 'string', description: '城市名称' }
},
required: ['city']
}),
async execute({ city }, options) {
const weathers = ['晴', '多云', '阴', '小雨', '雷阵雨'];
const weather = weathers[Math.floor(Math.random() * weathers.length)];
const temp = Math.floor(Math.random() * 20) + 10;
return { output: `${city}天气:${weather},${temp}°C`, metadata: { city, weather, temp } };
}
}
内置工具列表
| 工具 ID | 描述 | 必需参数 |
|---|---|---|
| get_current_city | 获取用户当前所在的城市 | 无 |
| get_weather | 获取指定城市的天气信息 | city |
| calculate | 执行数学计算 | expression |
| get_date | 获取当前日期和时间 | 无 |
消息累积规则
| 轮次 | messages 数组内容 |
|---|---|
| 第 1 轮 | [user] |
| 第 2 轮 | [user, assistant, user] |
| 第 3 轮 | [user, assistant, user, assistant, user] |
每轮结束后,将 assistant 回复和 user(工具结果) 追加到 messages,作为下一轮输入。
退出条件
以下任一条件满足时退出循环:
- finishReason !== 'tool-calls'(LLM 不再需要调用工具)
- toolResults.length === 0(没有工具被调用)
- 达到最大轮次限制(5 轮)
跟我做
- step1
- step2
- step3
- step4
Step 1: 定义工具
// src/services/toolService.js
export const TOOLS = [
{
id: 'get_weather',
description: '获取城市天气',
inputSchema: jsonSchema({
type: 'object',
properties: {
city: { type: 'string' }
},
required: ['city']
}),
async execute({ city }) {
return { output: `${city}晴,25°C` };
}
}
];
Step 2: 启用工具调用
const result = await streamText({
model: provider(MODEL),
messages,
tools: TOOLS, // 传入工具定义
toolCallStreaming: true // 开启工具调用流式输出
});
Step 3: 处理工具事件
for await (const delta of result.fullStream) {
switch (delta.type) {
case 'text-delta':
// 普通文本
break;
case 'tool-call':
// 工具调用请求
break;
case 'tool-result':
// 工具执行结果
break;
case 'finish-step':
// 步骤完成,判断是否继续
break;
}
}
Step 4: 管理多轮对话
// 累积用户消息
messages.push({ role: 'assistant', content: assistantMessage });
messages.push({ role: 'user', content: `工具执行结果:\n${toolResultsText}` });
检查点
- 工具定义正确(id、description、inputSchema)
- 工具能被 LLM 识别并调用
- 工具结果正确返回给 LLM
- 多轮调用时消息历史正确累积
- 达到 5 轮上限或 finishReason !== 'tool-calls' 时正确退出
踩坑提醒(FAQ)
Q: LLM 不调用工具怎么办?
检查 system prompt 是否明确告诉 LLM"你可以调用工具"。
Q: 工具参数格式错误?
确保 inputSchema 定义与 execute 函数参数一致。
Q: 无限循环调用?
设置最大轮次限制(建议 5 轮),并检查 finishReason。
Q: 工具返回结果后 LLM 不继续?
在下一轮 messages 中包含完整的工具执行结果文本。
本课小结
Minicode 02 要点总结:
- 工具调用 - 让 LLM 从"回答者"变成"行动者"
- 链式调用 - 通过消息累积实现多轮推理
- 退出检查 - 每轮结束后检查 finishReason 决定是否继续
- 工具结构 - id + description + inputSchema + execute
下课预告
Minicode 打造系列 03 - ReAct 模式实现
- 待补充
- 待补充
- 待补充
- 待补充