第 6 章 · Agent 与工具集成实战
本章目标:组合多个工具构建一个实用的旅行规划助手,掌握工具选择调优与多步推理控制。
6.1 实战目标
构建一个能回答"北京到上海旅游怎么安排"的助手,它需要组合两个工具:
getWeatherTool:查目的地天气(决定穿什么、去哪玩);calculatorTool:精确计算预算(LLM 算术不可靠,必须外挂)。
6.2 定义第二个工具:计算器
typescript
// src/mastra/tools/calculator-tool.ts
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
export const calculatorTool = createTool({
id: 'calculator',
description: '执行精确的四则运算,输入合法算术表达式',
inputSchema: z.object({
expression: z.string().describe('算术表达式,如 "(1200 + 350) * 2"'),
}),
outputSchema: z.object({
result: z.number(),
}),
execute: async ({ context }) => {
const { expression } = context;
// 安全白名单校验:只允许数字与运算符,防止注入
if (!/^[\d+\-*/().\s]+$/.test(expression)) {
throw new Error('非法表达式');
}
// 用 Function 构造器求值(表达式已过白名单)
const result = new Function(`return (${expression})`)() as number;
return { result };
},
});注意白名单正则——任何把模型输出直接拼进求值器的代码都必须做这层防御。
6.3 组合注册与调用
typescript
// src/mastra/agents/trip-agent.ts —— 双工具 Agent
import { Agent } from '@mastra/core/agent';
import { weatherTool } from '../tools/weather-tool';
import { calculatorTool } from '../tools/calculator-tool';
export const tripAgent = new Agent({
id: 'trip-planner',
name: 'Trip Planner',
instructions: `
You are a travel planning assistant.
- Use getWeather to check destination conditions.
- ALWAYS use the calculator for any arithmetic (never compute in your head).
- Present plans as: 天气概况 / 行程建议 / 预算明细.`,
model: 'openai/gpt-5-mini',
tools: {
weatherTool,
calculatorTool,
},
});typescript
// 调用:一次请求可能触发多个工具、多轮循环
const res = await tripAgent.generate(
'帮我规划周六上海一日游,两个人预算 2000 元,含门票餐饮交通',
);
console.log(res.text);6.4 观察多步推理过程
一次提问背后是完整的 agentic 循环:
text
User: 帮我规划上海一日游…
├─ [step 1] tool-call: weatherTool({ city: "上海" })
│ └─ { temperature: 22, condition: "多云" }
├─ [step 2] tool-call: calculatorTool({ expression: "150*2 + 400 + 300*2" })
│ └─ { result: 1300 }
└─ [final] 天气概况:多云 22°C…预算明细:门票 300×2 + 交通 400 + 餐饮 300×2 = 1300 元…在 Studio 的对话面板可以逐步展开每次工具调用;这就是"模型决策 → 执行 → 回灌 → 再决策"的循环。
6.5 工具选择调优技巧
模型不调用或乱用工具时,按以下顺序排查:
| 症状 | 原因 | 修复 |
|---|---|---|
| 从不调用工具 | description 太模糊 | 写清触发条件:"当用户询问 X 时使用" |
| 该用 A 却用了 B | 两个工具 description 语义重叠 | 明确划分边界并互斥描述 |
| 参数总是错 | 缺 .describe() 或约束太松 | 补充参数说明、收紧 Zod 校验 |
| 无限循环调用 | 没有终止信号 | instructions 中要求"拿到结果即回答" |
typescript
// 从响应中读取结构化的工具调用记录(用于审计与调试)
for (const msg of res.response.messages ?? []) {
if (msg.role === 'tool') {
console.log('工具调用:', JSON.stringify(msg.content));
}
}成本提示
每轮工具循环都是一次新的 LLM 请求。任务越复杂 token 消耗越高,生产环境务必设置步数上限。
本章小结
- 多工具 Agent = 各司其职的工具集合 + 明确分工的 instructions;
- 涉及计算的任务必须外挂工具,不能信任 LLM 心算;
- 白名单校验是一切动态求值类工具的安全底线;
- 工具行为问题优先从 description 和 schema 两处排查。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 让 LLM 计算订单总额时,正确做法是?
2. 模型从不调用你定义的工具,最应该先检查什么?
3. 对执行用户输入表达式的求值类工具,必不可少的安全措施是?
4. 关于多步工具调用的成本,正确的认识是?
🛠️ 动手实践
- 给 tripAgent 追加第三个工具
currencyConvert(汇率换算),并在 instructions 中规定预算同时以人民币和美元展示。 - 把 weatherTool 的 description 改成一句模糊的"处理数据",对比改造前后模型调用准确率的变化。
- 在 Studio 中发起 10 次相同提问,统计 calculatorTool 的调用成功率。