🐎 第8章 器以载道:Agent SDK 智能体开发套件
📄 一句话概括

把 SDK 包成 HTTP 服务,是「给别人用」最省事的形态。 骨架只有三步:收请求、跑一次、把结论序列化回去。 实测缺参数那份直接返回 400,一次模型调用都没发生。这一步能省掉大量无意义的开销。

📝 本章和前面几章的关系

前面九节都是零件,这一节把它们拼成一个能给别人用的东西。 这一节有真跑:处理函数直接调,以及真的起了一个 HTTP 服务、发了一次请求。

一、服务的三步

收:解析请求,校验必填。缺参数直接 400,别往下跑。 跑:调一次,拿到结果。这是唯一慢的地方。 回:序列化成 JSON。状态码要能表达失败。

三步里最容易被跳过的是第一步的校验——而它恰好是最省钱的一步。

二、真跑:处理函数直接调

演示里两个请求:给 path 的返回 200 和一份结论,不给 path 的直接 400。 400 那份一次模型调用都没发生。这就是「先校验再干活」的价值。 反过来说,如果先调模型再校验,你会为每一个错误请求付一次钱。

这个顺序在任何「包一层服务」的场景里都一样:便宜的先做。

三、真跑:包一层 HTTP

同一份处理函数挂到一个端口上,POST 一次,回 200 和同一份 JSON。 从函数到服务,加的东西只有三样:解析请求体、设置响应头、写回字节。 所以「先写函数再包壳」是更省事的顺序:函数能直接调、能单测,壳随时能换。

反过来先写壳,逻辑和 HTTP 细节会缠在一起,测都没法测。

四、服务化以后多出来的四件事

并发:同时来十个请求怎么办。限并发,或者排队。 超时:一次跑五分钟,客户端早断了。服务端也要有自己的超时。 重试:客户端重试会跑两遍。要么加幂等键,要么让重复执行无害。

配额:一个人把额度用完。按调用方限额。 四件事都不难,但都得做。不做的话,服务会在第一个热门需求上垮掉。

五、一条部署建议

把「跑一次」的逻辑留在纯函数里,HTTP 那层只做解析和序列化。 这样本地能直接调、能写单测、能换框架——壳可以换,逻辑不用动。 还要给每次请求生成一个标识,日志里带上它。出问题时能按标识把整条链路串起来。

这两条加起来,就是这个服务以后能不能维护的分界线。

六、动手 Demo:一个最小的 Web 服务:请求进来,结论出去

把 SDK 包成 HTTP 服务,是所有「给别人用」的形态里最省事的一种。 这里真跑两层:处理函数直接调,以及真的起一个服务、发一次请求。

def _handle(req, calls=3):
    path = req.get(u"path", u"")
    if not path:
        return {"status": 400, "body": {"error": u"缺 path"}}
    return {"status": 200, "body": {...}}

实跑输出:

--------------------------------------------------------------------
一个最小的 Web 服务:请求进来,结论出去
--------------------------------------------------------------------

  把 SDK 包成 HTTP 服务,是所有「给别人用」的形态里最省事的一种。
  它的骨架只有三步:收请求、跑一次、把结论序列化回去。

  ① 服务的三步
  --------------------------------------------------------------------
| 步 | 做什么 | 注意什么 |
|---|---|---|
| 收 | 解析请求,校验必填 | 缺参数直接 400,别往下跑 |
| 跑 | 调一次,拿到结果 | 这一步是唯一慢的地方 |
| 回 | 序列化成 JSON | 状态码要能表达失败 |


  ② 真跑:处理函数直接调
  --------------------------------------------------------------------
  请求 {"path": "src"} → 状态码 200,
  返回 {"path": "src", "steps": ["列出文件", "读入口", "数函数"], "turns": 3, "cost_usd": 0.012}
  请求 {} → 状态码 400,返回 {"error": "缺 path"}
  缺参数那份直接 400,一次模型调用都没发生——这一步能省掉大量无意义的开销。

  ③ 真跑:包一层 HTTP
  --------------------------------------------------------------------
  POST 到 127.0.0.1:60182 → 状态码 200
    {"path": "src", "steps": ["列出文件", "读入口", "数函数"], "turns": 3, "cost_usd": 0.012}
  同一份处理函数,既能在本地直接调,也能挂到端口上。这就是「先写函数再包壳」的好处。

  ④ 服务化以后多出来的四件事
  --------------------------------------------------------------------
| 多出来什么 | 具体问题 | 至少要做一样 |
|---|---|---|
| 并发 | 同时来十个请求怎么办 | 限并发,或者排队 |
| 超时 | 一次跑五分钟,客户端早断了 | 服务端也要有超时 |
| 重试 | 客户端重试会跑两遍 | 加幂等键 |
| 配额 | 一个人把额度用完 | 按调用方限额 |

  这四件事都不难,但都得做。不做的话,服务会在第一个热门需求上垮掉。

  ⑤ 一条部署建议
  --------------------------------------------------------------------
  把「跑一次」的逻辑留在纯函数里,HTTP 那层只做解析和序列化。
  这样本地能直接调、能写单测、能换框架——壳可以换,逻辑不用动。
  ! 这里的服务是最小可跑实现:单线程、无认证、无并发控制,只用于演示形状。

①三步:收(解析并校验)、跑(唯一慢的地方)、回(序列化,状态码要能表达失败)。 ②缺参数那份直接 400,一次模型调用都没发生。先校验再干活,能省掉大量无意义的开销; 反过来先调模型再校验,你会为每一个错误请求付一次钱。 ③从函数到服务,加的东西只有三样:解析请求体、设置响应头、写回字节。 所以「先写函数再包壳」更省事——函数能直接调、能单测,壳随时能换。 ④服务化多出四件事:并发、超时、重试(加幂等键)、配额。都不难,但不做会在第一个热门需求上垮掉。

30 秒实验

  1. 把「先校验」改成「先调模型再校验」,重跑一遍。缺参数那个请求会发生什么?成本差多少?
  2. 给服务加一个「同一个 path 一分钟内只跑一次」的缓存,重跑一遍。它解决的是四件事里的哪一件?
  3. 把处理函数里的模型调用换成一个会抛异常的版本,重跑一遍。现在的代码会返回什么状态码? 该返回什么?

常见误区

  1. 先调模型再校验参数。每个错误请求都要付一次钱。
  2. 逻辑和 HTTP 细节写在一起。没法单测,也没法换框架。
  3. 不设服务端超时。客户端断了,服务还在跑,白花钱。
  4. 重试不做幂等。同一个请求跑两遍,可能改两次文件。
  5. 日志里没有请求标识。出问题时串不起整条链路。
自测三问(点开对答案) 1. 服务的三步是什么? —— 收(解析并校验)、跑(调一次)、回(序列化成 JSON,状态码表达失败)。 2. 为什么校验要放在最前面? —— 因为它是唯一不花钱的一步。先调模型再校验,每个错误请求都要付一次钱。 3. 服务化之后要多处理哪四件事? —— 并发、超时、重试(幂等)、配额。

服务能跑了,最后一节收口:CLI 和 SDK 到底怎么选。

相关笔记