🔌 第9章 基于MCPServer的生活类智能体应用
📄 一句话概括

三个能力分属三个 Server:天气、餐厅、口味偏好。 宿主把它们合并成一张表,模型不需要知道谁是谁——这正是拆 Server 的好处。 三个读操作都不弹确认;换成「订位」就得弹。

📝 本章和前面几章的关系

上一个案例是一个 Server 三个工具。这一个反过来:三个 Server 各一个工具。 形态不同,协议一样。合起来之后工具表变成三行,模型照样挑。 这一节顺带讲一个实际问题:三个里挂掉一个,还能不能给出有用的回答。

一、为什么拆成三个 Server

天气和地点是公共服务,别人维护的,不该自己重写。 口味偏好是私有数据,只能自己存。 两类数据的性质不同:公共的随时会变,私有的只有你自己知道。分开写,升级互不影响。

合成一个 Server 也能跑,代价是以后想换地图服务商,得动整个 Server。 所以拆的依据不是「功能像不像」,是「谁维护、多久变一次」。

二、合并之后,模型看到的是三行

weather.forecast(天气 Server)、poi.search(地图 Server)、pref.get(笔记 Server)。 三行的形状一模一样:名字、描述、参数。模型不知道也不关心它们来自三个进程。 所以「跨 Server 协作」这件事,在模型眼里根本不存在——它只是连着调了三个工具。

复杂的部分在宿主:合并工具表、处理撞名、隔离故障。 模型那边一直是简单的。 理解这一点,就不会把「多 Server 协作」想成什么高级技术。它就是并行装了几个插件。

三、三步走完,一句话出结果

演示里明天杭州小雨,偏好是清淡、不吃辣、人均 150 以内。 三家餐厅里两家在预算内(78 和 65),一家 180 超了。筛选这一步是模型做的。 最后结论一句话:新白鹿,人均 78,评分 4.6。

注意这个结论里三个信息分别来自三个工具:天气来自第一个,餐厅来自第二个,预算来自第三个。 没有一个工具知道全貌,全貌是模型拼出来的。 这是「智能体」和「脚本」的又一个差别。

四、读和写,同一句话里也要分开看

这一轮三次调用全是读,所以一个确认框都不该弹。 但如果第三步换成「帮你订个位」,那就是写。同一句话里可能有读有写,得分开看。 教材里这一节没做到订位这一步,只到「推荐」。 这是有意的:推荐错了用户自己会改,订错了要打电话取消。

所以做这类智能体的顺序是:先做读,跑顺了再加写。别一上来就让它替你花钱。 这一条在七个案例里通用。第 11 章那个「发布网页」也是写,同样要防重。

五、动手 Demo:约会助手:两个 Server 一起干活

造三个小 Server:天气、地图、偏好。前两个各一个工具,第三个只存口味。 三步依次调用,看结论怎么从三份数据里拼出来。 最后让每个 Server 各挂一次,看降级之后还能不能给出有用的回答。

w = Server('weather-mcp'); w.add(Tool('weather.forecast', ..., fn))
m = Server('map-mcp');     m.add(Tool('poi.search', ..., fn))
n = Server('note-mcp');    n.add(Tool('pref.get', ..., fn))

wt = call(w, 'weather.forecast', {'city': '杭州', 'day': '明天'})
pf = call(n, 'pref.get', {'who': '她'})
ps = call(m, 'poi.search', {'city': '杭州', 'type': '餐厅'})['items']
ok = sorted([x for x in ps if x['price'] <= pf['budget']],
             key=lambda x: -x['rating'])
print(ok[0])        # 预算内的最高分

实跑输出:

--------------------------------------------------------------------
约会助手:两个 Server 一起干活
--------------------------------------------------------------------


  ① 为什么要两个 Server
  --------------------------------------------------------------------

| 要什么 | 哪个 Server | 为什么不能合并 |
|---|---|---|
| 当天天气 | 天气 Server | 别人维护的公共服务,不该自己重写 |
| 附近餐厅 | 地图 Server | 同上,而且数据天天变 |
| 两人口味 | 本地笔记 Server | 这是私有数据,只能自己存 |

  三个能力分属三个 Server。【宿主把它们合并成一张表交给模型】,模型不需要知道谁是谁。
  这正是第 2 章讲的那个好处:拆开写,坏一个不影响另外两个。

  ② 合并之后的工具表
  --------------------------------------------------------------------

| 工具 | 来自哪个 Server | 读还是写 |
|---|---|---|
| weather.forecast | weather-mcp | 读 |
| poi.search | map-mcp | 读 |
| pref.get | note-mcp | 读 |


  ③ 三步走完,一句话出结果
  --------------------------------------------------------------------
  明天杭州:小雨
  偏好:清淡、不吃辣,人均 150 以内


| 餐厅 | 评分 | 人均 | 合不合适 |
|---|---:|---:|---|
| 楼外楼 | 4.4 | 180 元 | 超预算 |
| 新白鹿 | 4.6 | 78 元 | 可以 |
| 外婆家 | 4.3 | 65 元 | 可以 |


结论:新白鹿,人均 78,评分 4.6。
  三个读操作,没有一次写。所以这一轮不需要任何确认框。
  如果第 3 步换成「订位」,那就是写操作,必须弹确认。【同一句话里可能有读有写,得分开看。】

  ④ 两个 Server 挂了会怎样
  --------------------------------------------------------------------
  weather 挂了 -> 只能说「天气查不到,建议带伞」
  map 挂了     -> 只能说「天气不错,去哪你自己挑」
  note 挂了    -> 只能说「按人均 150 挑」,猜的
  三种降级都能给出一个「不完美但能用」的回答。【这是拆 Server 的好处】:挂一个不是全挂。
  反过来说:如果一个都挂不起,那就该给它加个兜底,而不是指望它永不失败。

①第一张表三行,最后一列「为什么不能合并」是这一节的核心:公共数据与私有数据分开维护。 ②第二段那张三行的工具表,看不出谁来自哪个 Server。这就是协议统一的效果。 ③第三段三步走完,结论一句话。三个信息来自三个工具,全貌是模型拼的。 ④第四段全读不弹窗。如果换成订位,就得弹——读和写要分开算。 ⑤最后那段降级表值得记:三个都能降级给出「不完美但能用」的回答,这就是拆 Server 的实际好处。

30 秒实验

  1. 给 pref.get 加一个「忌口」字段,看筛选逻辑要改几行。
  2. 把预算改成 60,看还剩几家。一家都不剩时该怎么答。
  3. 把 poi.search 换成「只能查景点」,想想这个智能体会退化成什么。

常见误区

  1. 「多功能就该放一个 Server。」拆的依据是谁维护、多久变一次。公共数据和私有数据分开更稳。
  2. 「跨 Server 协作很复杂。」模型那边只是连着调了三个工具,复杂的是宿主的合并与隔离。
  3. 「先让它能订位才完整。」写操作先别加。推荐错了用户会改,订错了要打电话取消。
自测三问(点开对答案) 1. 为什么这个案例要拆成三个 Server? —— 天气和地图是公共服务,口味是私有数据。两类数据的维护方和变化频率不同。 2. 这一步为什么要弹窗、那一步不用? —— 看读还是写。三次读都不用弹;换成订位就是写,必须弹。 3. 一个 Server 挂了,这个智能体还能用吗? —— 能给出降级回答:缺天气就说「建议带伞」,缺地图就说「去哪你自己挑」。

三个 Server 有点重。下一个案例回到最简:一个工具、两个参数,专门用来第一次跑通。

相关笔记