案例:天气与旅行 Agent
目标
这个案例用两个小 Agent 展示如何从单一工具调用,扩展到带记忆和工作流的业务助手:
| 角色 | 解决的问题 | 核心能力 |
|---|---|---|
weatherAgent | 查询指定城市天气 | 调用 weatherTool,把外部 API 结果转成中文回答 |
tripAgent | 根据天气规划旅行 | 调用 weatherTool,结合 Memory 保留偏好 |
tripPlanningWorkflow | 生成可复现行程 | 解析目的地、查询天气、生成分日计划 |
它们不是 Study Agent 主线的一部分,而是用来演示“一个工具如何被多个 Agent 和 Workflow 复用”。
文件位置
| 文件 | 作用 |
|---|---|
apps/study-agent/src/mastra/tools/weather-tool.ts | 封装天气查询工具 |
apps/study-agent/src/mastra/agents/weather-agent.ts | 最小天气查询 Agent |
apps/study-agent/src/mastra/agents/trip-agent.ts | 带 Memory 的旅行规划 Agent |
apps/study-agent/src/mastra/workflows/trip-planning-workflow.ts | 确定性旅行规划 Workflow |
apps/study-agent/src/mastra/index.ts | 注册 agents、tools、workflows |
Weather Tool 的边界
weatherTool 是最底层能力。它只做三件事:
- 接收城市名。
- 调用
wttr.in获取天气 JSON。 - 返回稳定结构:当前天气和未来三天预报。
inputSchema: z.object({
city: z.string().describe('要查询天气的城市名称,例如:北京、上海、Tokyo、London'),
})输出结构保持稳定,方便 Agent、Workflow 或未来前端复用:
current.temperaturecurrent.weathercurrent.humiditycurrent.windSpeedforecast[].dateforecast[].minTempforecast[].maxTempforecast[].weather
设计重点:Tool 不负责写旅行建议,也不负责猜用户偏好。Tool 只负责“查天气”这个可验证动作。
Weather Agent 的设计
weatherAgent 是最小的工具调用案例。
flowchart LR
User[用户输入城市或天气问题] --> Agent[weatherAgent]
Agent --> Tool[weatherTool]
Tool --> API[wttr.in]
API --> Tool
Tool --> Agent
Agent --> Reply[中文天气摘要]它的 instructions 重点是让模型知道什么时候调用工具:
- 用户问城市天气时,调用
weather工具。 - 用户只输入城市名时,也直接查询。
- 工具失败时,提示用户检查城市名称。
这个 Agent 适合讲解 Tool description 和 Agent instructions 的关系:Tool 描述负责说明工具能力,Agent instructions 负责说明业务场景和回答格式。
Trip Agent 的设计
tripAgent 复用了同一个 weatherTool,但目标变成“旅行建议”。
flowchart TB
User[用户提出旅行需求] --> Agent[tripAgent]
Agent --> Memory[Memory: 最近消息]
Agent --> Weather[weatherTool]
Weather --> Agent
Agent --> Reply[天气总结 + 活动建议 + 追问]它比 weatherAgent 多了两个教学点:
| 点 | 说明 |
|---|---|
| 复用工具 | 同一个 weatherTool 可以服务不同 Agent,不需要复制 API 调用代码 |
| 使用 Memory | lastMessages: 20 让 Agent 能在同一对话里引用用户偏好 |
例如用户先说“我喜欢轻松一点,不想赶景点”,后面再问“那杭州三天怎么安排”,tripAgent 应该把“轻松”作为偏好使用。
Trip Planning Workflow 的设计
如果要稳定地产出行程表,Workflow 比 Agent 自由发挥更合适。
flowchart LR
A[userMessage] --> B[parse-destination]
B --> C[fetch-weather]
C --> D[build-itinerary]
D --> E[plan]Step 1:解析目的地
parse-destination 从用户文本中提取:
city:目的地,默认北京。days:天数,限制在 1 到 7 天。style:旅行风格,目前支持悠闲、美食、文化。
这是一个教学版解析器,用正则和关键词完成,不依赖模型。
Step 2:查询天气
fetch-weather 根据目的地调用天气服务,并把结果拼进 workflow 数据流。
这一层和 weatherTool 逻辑相似,但放在 Workflow step 中,方便展示“工作流也能直接执行外部查询”。真实项目里可以进一步抽成共享函数,避免重复 API 逻辑。
Step 3:生成行程
build-itinerary 根据 style 选择活动池,并按每天的天气生成:
morningafternooneveningweatherNote
如果天气描述包含“雨”,上午和下午会加室内备选提示。
Agent 和 Workflow 怎么选
| 场景 | 优先选择 | 原因 |
|---|---|---|
| 用户只问“北京天气怎么样” | weatherAgent | 开放式问答 + 单次工具调用 |
| 用户说“我喜欢美食,帮我安排旅行” | tripAgent | 需要结合偏好和上下文 |
| 产品需要固定 JSON 行程表 | tripPlanningWorkflow | 步骤稳定、输出可验证 |
| 要在前端展示每一步进度 | tripPlanningWorkflow | step 边界清楚,方便调试 |
简单判断:如果流程顺序必须稳定,用 Workflow;如果用户问题开放、需要自然语言协调,用 Agent。
在 Studio 中验证
启动 Studio:
npm run final:dev验证 Weather Agent
对 weatherAgent 输入:
北京今天天气怎么样?检查:
- 是否调用
weather工具。 - 回答是否包含当前温度、天气、湿度或风速。
- 工具失败时是否给出清楚错误说明。
验证 Trip Agent
对 tripAgent 连续输入:
我喜欢轻松一点的旅行,不想赶景点。想去杭州玩 3 天,帮我安排一下。检查:
- 是否调用
weather工具。 - 是否先总结天气,再给行程建议。
- 第二轮是否能引用“轻松一点”的偏好。
验证 Trip Planning Workflow
运行 tripPlanningWorkflow,输入:
{
"userMessage": "想去杭州玩3天,喜欢吃"
}检查输出:
city应为杭州。days应为3。style应为美食。plan应包含每天的上午、下午、晚上安排。
常见错误
| 错误 | 影响 | 修正方向 |
|---|---|---|
| Agent instructions 没说何时调用工具 | 模型可能只聊天不查天气 | 明确“天气问题必须调用 weather 工具” |
| Tool 输出字段频繁变化 | Agent、Workflow、前端都难以复用 | 用 outputSchema 固定字段 |
把旅行建议写进 weatherTool | 工具职责变重,复用性下降 | Tool 只查天气,建议放 Agent 或 Workflow |
| 让 Agent 自由生成固定行程表 | 输出不稳定,难评估 | 用 Workflow 固定步骤和 schema |
| 依赖 Memory 存权限或套餐 | 容易串话或污染上下文 | 权限、租户、套餐放 RequestContext |
给 AI 的提示词
请基于现有 weatherTool 设计一个新的户外活动 Agent。
要求:
- 不新增依赖。
- 复用 weatherTool,不复制天气 API 请求逻辑。
- instructions 说明什么情况下调用工具。
- 如果需要固定输出结构,优先用 Workflow。
- 最后给出 Studio 中的验收问题。小结
这个案例展示了三层设计:
- Tool 封装可验证动作。
- Agent 负责开放式用户协作和自然语言回答。
- Workflow 负责稳定流程和结构化输出。
理解这三层后,再回到 Study Agent,就更容易判断哪些能力应该写成工具,哪些逻辑应该放进 Agent instructions,哪些流程应该升级为 Workflow。