一个工具、两个参数、一问一答。它简单到没有可拆的地方,所以适合当第一个。 查不到的时候不要报错,回一个「查不到」——这类查询工具,查不到是正常结果。 「每日」这两个字跟协议无关,是定时任务的事。
上一节三个 Server,这一节一个工具。两个都叫智能体,复杂度差十倍。 教材把它放在第三个,其实它最该放第一个——先跑通协议,再谈编排。 所以这一节短,但有两个细节值得记住:查不到怎么回,以及「每日」跟 MCP 无关。
一、为什么拿它当第一个跑通
一个工具、两个参数(城市、日期),一次调用一句话。 没有串联、没有写库、没有跨 Server。它能验证的只有一件事:协议通不通。 而这件事恰恰是最该先验证的。工具多了以后报错,你分不清是协议的问题还是编排的问题。
所以顺序应该是:单工具跑通 → 多工具串联 → 加定时 → 加推送。每加一样只改一处。 反过来做的人,最后会在一堆可能性里找 bug。
二、一个工具,返回四个字段
演示里 weather.today 返回:天况、最低、最高、风。 四个字段不是越多越好。天况和温度是必须的,风和湿度是加分项。 返回字段的多少,直接决定模型能说多细。只给「小雨」,它只能说「今天下雨」; 给了温度和风,它才能说「穿件薄外套」。
但字段越多,工具越长,占的上下文越多。 这就是个取舍,没有标准答案。 一个实用的判据:这个字段会不会出现在回答里。 不会就不返回。
三、查不到的时候,别报错
演示里传一个表里没有的城市,工具没有报错,回了一个「查不到」。 这是有意设计的。 工具报错会让模型停下来问用户; 回一个「查不到」,模型可以接着答「苏州的数据我这里没有,换个城市试试」。 判据很简单:这类查询工具,查不到算正常结果,参数错才算错误。
把这两件事混在一起,是很多工具的通病:城市不存在也回 -32602,模型就会以为是自己参数填错了, 反复重试。 错误码要留给「调用方式不对」,业务上的空结果用返回值表达。
四、「每日」是另一件事
加上「每日」两个字,就多出三样东西:定时、要查的城市表、推给谁。 这三样没有一样归 MCP 管。 定时是宿主或系统的任务调度,城市表是提示词里的内容,推送是宿主配的渠道。 所以这类智能体的工程量:工具二十行、提示词十行、定时和推送各三十行。MCP 只占其中一小块。
想清楚这一点,就不会把「我的智能体每天不跑」怪到 MCP 头上。 教材里这一步是在平台上配的定时(本沙箱不执行)。 判据是:到点了宿主有没有触发,触发了有没有调用工具——两件事分开查。
五、动手 Demo:每日天气:最简的一问一答
一个工具、四个字段,问一句答一句。 再传一个不存在的城市,看它怎么回的——这一步是本节的要点。 最后把「每日」要的三样东西拆开,看哪一样归 MCP 管。
s = Server('weather-mcp')
s.add(Tool('weather.today', '查某城市今天的天气:天况、温度、风',
schema({'city': prop('string', '城市名,如 杭州')}, ['city']),
lambda a: {'city': a['city'],
'text': CITY[a['city']][0][1],
'low': CITY[a['city']][0][2],
'high': CITY[a['city']][0][3]}))
print(call(s, 'weather.today', {'city': '杭州'}))
print(call(s, 'weather.today', {'city': '苏州'})) # 不报错,回查不到
实跑输出:
--------------------------------------------------------------------
每日天气:最简的一问一答
--------------------------------------------------------------------
① 为什么拿它当第一个
--------------------------------------------------------------------
| 项 | 这个案例 | 别的案例 |
|---|---|---|
| 工具数 | 1 个 | 三到六个 |
| 参数 | 城市、日期 | 一串坐标或 id |
| 要不要串联 | 不要,一问一答 | 要,参数来自上一步 |
| 要不要写库 | 不要 | 多数要 |
它简单到没有可拆的地方。
【第一次跑通 MCP 就该从这种形状开始】——先确认「协议能通」,再谈「智能」。
跑通之后再往上加:加定时、加推送、加多城市。每加一样只改一处。
② 一个工具,两个参数
--------------------------------------------------------------------
问一句:
用户:今天杭州天气怎么样
模型:weather.today(city=杭州)
工具:多云,19 到 27 度,东风 2 级
模型:今天杭州多云,19 到 27 度,东风 2 级,出门穿件薄外套就行。
一次调用、一句话。【这里没有任何「智能」,是提示词在管说话的风格。】
③ 查不到的时候
--------------------------------------------------------------------
传一个表里没有的城市:
{"city": "苏州", "text": "查不到", "low": 0, "high": 0, "wind": ""}
它没有报错,回了一个「查不到」。【这是有意设计的。】
工具报错会让模型停下来问用户;
回一个「查不到」,模型可以接着答「苏州的数据我这里没有,换个城市试试」。后者体验好得多。
判据是:【这类查询工具,查不到算是正常结果,不算错误。】 参数错才算错。
④ 加定时,就变成「每日」
--------------------------------------------------------------------
| 要做的事 | 在哪一层 | MCP 管不管 |
|---|---|---|
| 每天八点跑一次 | 宿主或系统的定时任务 | 不管 |
| 查哪几个城市 | 提示词里写死一张表 | 不管 |
| 查到之后推给谁 | 宿主的通知渠道 | 不管 |
| 怎么查 | 工具 weather.today | 管 |
四行里 MCP 只管最后一行。【「每日」这两个字跟协议一点关系都没有】,是定时任务加推送渠道的事。
所以这类智能体的工程量:工具二十行、提示词十行、定时和推送各三十行。【MCP 只占其中一小块。】
⑤ 往上加的三条路
--------------------------------------------------------------------
加城市:提示词里那张表从三行变十行,工具不动。
加内容:再挂一个「空气质量」工具,还是同一个 Server。
加推送:宿主那边配一个渠道,工具不动。
三条路都只改一处。
【这就是「工具与话术分开」的好处】:换推送渠道不用碰工具,换数据源不用改提示词。
①第一张表四行,最后一行「要不要写库」是不要。四个「不要」说明它简单到没有可拆的地方。 ②第二段那个工具的返回只有四个字段。判据是「这个字段会不会出现在回答里」。 ③第三段那次「查不到」是关键:它不是错误,是正常结果。模型可以接着说下一句。 ④第四段那张四行的表,前三行都不归 MCP 管。「每日」这两个字跟协议无关。 ⑤最后那句比例值得记:工具二十行、提示词十行、定时和推送各三十行。MCP 只是一小块。
30 秒实验
- 给工具加一个「空气质量」字段,看模型的回答能多说什么。
- 把「查不到」改成抛异常,再跑一遍,看回答变成什么样。
- 列出你要每天查的三个城市。想想这张表该写在哪一层。
常见误区
- 「智能体越复杂越厉害。」这个案例一个工具一个参数,照样是智能体。复杂度不是标准。
- 「查不到就报错最严谨。」报错会让模型停下来问用户。查询类工具,空结果是正常返回。
- 「每天不跑是 MCP 的问题。」定时不在协议里。先查宿主有没有触发。
自测三问(点开对答案)
1. 为什么这个案例适合当第一个跑通的? —— 一个工具两个参数,没有串联和写库。它只能验证协议通不通,而这正是该先验证的。 2. 查不到的时候,该报错还是该回空结果? —— 回空结果。查询类工具查不到是正常结果;错误码留给「调用方式不对」。 3. 「每日」要的三样东西,哪一样归 MCP 管? —— 只有「怎么查」这一样。定时、城市表、推送渠道都在 MCP 之外。从一问一答往前走一步:加一个「附近」的要求,看坐标这个参数怎么把一句话变成四个数。
- ☐ (本篇完,下一篇 9.4 附近餐厅推荐智能体)