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

这个案例比前几个多三步:注册、申请 Key、查额度。 三步都是商务,不是技术。 Key 写在哪有四种写法,从「写在代码里」到「放服务端密钥库」,安全性差一个量级。 拿到 Key 之后的事情全是真的:起进程、发请求、拿结果。

📝 本章和前面几章的关系

前四个案例都是「装个 Server 就能用」。这一个不是:它要先申请。 教材这一节专门有一段讲 API 怎么拿,说明这一步确实是门槛。 本沙箱没有账号、没有 Key、没有额度,所以申请那一段照原样说明;拿到 Key 之后的调用是真跑的。

一、多出来的三步

注册账号、申请 API Key、查额度。三步里没有一行代码。 但它们是真正的门槛。 前面几个案例装完就能跑,这一个装完跑不起来——它会回一个 401。 401 这个错最容易被误解成「代码写错了」。其实九成是 Key 没配对,或者额度用完了。

所以这一节的第一句话应该是:先去把 Key 拿到,再回来看代码。 教材里这一段是在 Variflight 的站点上申请的(界面操作本沙箱不执行)。 判据是:拿到一串字符,而且能看到额度还剩多少次。

二、Key 写在哪,安全性差一个量级

四种写法:写在代码里、写在配置的 env 里、从系统环境变量读、放在服务端密钥库。 写在代码里最差:一提交就泄露,而且历史记录里永远留着。 写在配置的 env 里好一点,但配置文件可能被同步到网盘、被备份带走。

从环境变量读是本地长期用的常见做法;上线的话,Key 应该在服务端的密钥库里,客户端根本看不到。 演示里的写法是 os.environ.get('VARIFLIGHT_KEY', ''), 读不到就用空串——这样代码里永远不会出现 Key。 后面那行判断别省:没有 Key 就直接回一句「去申请一个」,别发一个空 Key 出去换 401。

三、一个工具,三件事一起返回

flight.search 返回:航班号、起飞、到达、价格、状态。 五样里「状态」最容易被忽略。演示里最便宜那班是 MU5137,890 元,但状态是「延误 25 分钟」。 便宜和准时是两件事,工具得把两者都返回,让模型自己权衡。

如果工具只返回价格,模型就会推最便宜的那班,用户到了机场才发现延误。 所以设计工具时问一句:这个决定还需要什么信息? 需要的一起返回,别让模型猜。

四、额度是设计约束,不是意外

额度有三种:每天 N 次、每分钟 N 次、总量。 做定时任务最容易撞第一种;批量查最容易撞第二种;测试阶段撞得最快的是第三种。 所以这一类 Server 要把额度当设计约束:缓存、限频、先问清楚再查。

一个实用的做法:工具侧加一层本地缓存,同一个查询十分钟内只发一次。 这不是协议的事,但它是上线之后第一个要加的东西。没有缓存,第一个月的额度会被测试用完。

五、动手 Demo:航班查询:先要一个 Key

先看这个案例比前几个多出来的三步,再看 Key 的四种写法。 然后真跑一个工具:按出发地目的地查航班,返回时刻、价格、状态。 申请那一段本沙箱不执行,调用这一段是真的。

KEY = os.environ.get('VARIFLIGHT_KEY', '')
if not KEY:
    raise SystemExit('没有配 Key,去申请一个')

s = Server('variflight-mcp')
s.add(Tool('flight.search', '按出发地和目的地查航班,返回时刻、价格、状态',
           schema({'from': prop('string', '出发城市'),
                   'to': prop('string', '到达城市')}, ['from', 'to']), fn))

print(call(s, 'flight.search', {'from': '杭州', 'to': '北京'}))

实跑输出:

--------------------------------------------------------------------
航班查询:先要一个 Key
--------------------------------------------------------------------


  ① 这个案例多了哪一步
  --------------------------------------------------------------------

| 步 | 别的案例 | 这个案例 |
|---|---|---|
| 1 | 拿到 Server 地址 | 注册一个账号 |
| 2 | 填进宿主 | 申请一个 API Key |
| 3 | 直接用 | 把 Key 填进配置或环境变量 |
| 4 | — | 查额度,确认够用 |

  多出来的三步都是「商务」不是「技术」。【这一节在本沙箱不执行】,没有账号、没有 Key、也没有额度。
  但拿到 Key 之后的事情是全真的:起进程、发请求、拿结果。这一段下面照跑。

  ② Key 写在哪
  --------------------------------------------------------------------

| 写法 | 安不安全 | 什么时候用 |
|---|---|---|
| 写在代码里 | 最差,一提交就泄露 | 永不 |
| 写在配置的 env 里 | 一般,本机配置文件可能被同步 | 本地调试 |
| 从系统环境变量读 | 较好,不进版本库 | 本机长期用 |
| 放在服务端密钥库 | 最好,客户端看不到 | 上线 |

演示里从环境变量读,读不到就用空串——【这样代码里永远不会出现 Key】:
  KEY = os.environ.get('VARIFLIGHT_KEY', '')
  if not KEY:  return {'error': '没有配 Key,去申请一个'}
  第二行那一步很多人会省。省了的后果是:拿不到 Key 时它发一个空 Key 出去,回一个看不懂的 401。

  ③ 拿到 Key 之后:一个工具
  --------------------------------------------------------------------
用户说:明天杭州到北京,哪个便宜。

| 航班 | 起飞 | 到达 | 价格 | 状态 |
|---|---|---|---:|---|
| CA1701 | 07:30 | 09:50 | 1280 元 | 准点 |
| MU5137 | 13:10 | 15:35 | 890 元 | 延误 25 分钟 |
| HU7278 | 19:40 | 22:05 | 1050 元 | 准点 |

  最便宜是 MU5137,890 元,延误 25 分钟。
  注意它状态是「延误 25 分钟」。【便宜和准时是两件事,工具得把两者都返回,让模型自己权衡。】

  ④ 额度这件事
  --------------------------------------------------------------------

| 额度类型 | 怎么算 | 什么时候会撞上 |
|---|---|---|
| 每天 N 次 | 按自然日重置 | 做定时任务最容易撞 |
| 每分钟 N 次 | 按滑动窗口 | 批量查的时候撞 |
| 总量 | 用完就没了 | 测试阶段撞得最快 |

  所以这一类 Server 一定要把额度当成设计约束:缓存、限频、先问清楚再查。
  一个实用的做法:工具侧加一层本地缓存,同一个查询十分钟内只发一次。
  【这不是协议的事,但它是上线之后第一个要加的东西。】

①第一张表四行,多出来的三步都在前三行。没有一行是技术活。 ②第二张表四种 Key 写法,从最差到最好。判据是「会不会进版本库」。 ③第三段那句「没有配 Key 就直接退出」很多人会省。省了的后果是:空 Key 发出去,回一个看不懂的 401。 ④第四段那张航班表,最便宜那班延误 25 分钟。便宜和准时是两件事,都得返回。 ⑤最后那段额度表,三种额度撞上的场景不同。上线前先想清楚会撞哪一种。

30 秒实验

  1. 把 Key 改成写在代码里,想想它会在哪些地方留下痕迹。
  2. 给工具加一个「机型」字段,看回答能多说什么。
  3. 算一下每天查 200 次、每次 3 秒,一天的额度够不够。

常见误区

  1. 「Key 写在代码里方便。」一提交就泄露,历史记录里永远留着。本地用环境变量,上线用密钥库。
  2. 「回 401 是代码写错了。」九成是 Key 没配对或额度用完了。先查这两样。
  3. 「只返回价格就行。」用户还要知道准不准点。缺字段,模型只能猜。
自测三问(点开对答案) 1. 这个案例比前几个多出来的三步是什么? —— 注册账号、申请 API Key、查额度。三步都是商务流程,不是技术。 2. Key 最好写在哪? —— 本地用系统环境变量,上线放服务端的密钥库。永远不要写在代码里。 3. 为什么工具要一起返回价格和状态? —— 便宜和准时是两件事。只给价格,模型会推最便宜的那班,用户到机场才发现延误。

航班查的是「什么时候走」,下一个案例查的是「谁在买」——这类数据的口径比接口更容易出错。

相关笔记