跳转到内容

HTTP 与 API 基础

一个程序向另一个程序查询订单

Section titled “一个程序向另一个程序查询订单”

运行第 42 章实践前,请先完成本 Primer。读懂例子不需要账号或编程经验;运行程序需要 Python Primer、Python 3.12,以及 code/part-vii/README.md 规定的依赖环境。第 43、54 章还会复用这些基础。

语言模型说出订单号,并不会自动打开订单数据库。必须有一个程序发出请求,另一个有权读取记录的程序作出回应。发请求的一方叫客户端(client),回应的一方叫服务器(server)。这是一次通信中的角色,不一定对应两台计算机。本例两端都在本机运行,只读取虚构记录。

API(application programming interface,应用程序编程接口)约定双方怎样交流,包括允许请求什么、结果表示什么。HTTP提供通用的请求与响应规则,JSON 则可以承载消息中的数据。这些都不依赖语言模型。

例 HTTP.1:读取虚构订单 A-104。 本地脚手架绑定到 127.0.0.1,这是指向本机的回环地址,再由操作系统分配空闲端口。仅为说明地址结构,假设分到的是 8765,那么 URL 就是 http://127.0.0.1:8765/orders/A-104。http 是协议方案,地址和端口标识接收服务,/orders/A-104 是该服务中的路径。远程服务可能改用 HTTPS 主机名;HTTPS 通过 TLS 保护传输,却不能保证业务答案正确。

GET /orders/A-104 HTTP/1.1
Host: 127.0.0.1:8765
Authorization: Bearer demo-placeholder-key

GET 请求读取一份表示。首行还写明路径和 HTTP 版本。随后是请求头(headers),例如接收请求的主机和客户端提供的凭证。一个空行把头部与可能存在的消息体(body)分开,本请求没有消息体。这里展示的密钥是特意公开的占位值,只用于本地样例。

HTTP/1.0 200 OK
Content-Type: application/json
{"order_id":"A-104","status":"processing","as_of":"2026-09-14T00:00:00Z","source_version":"ka3-orders-v1"}

脚手架使用的标准库处理器以 HTTP/1.0 响应;收到 HTTP/1.1 请求,不等于必须原样返回版本号。为便于阅读,这里省略自动生成的 Date、Server 和 Content-Length 头,消息体完整保留。200 是状态码,Content-Type 说明怎样解释消息体。这里的消息体是 JSON:花括号中放带引号的字段名和字符串值,用逗号分隔。status 表示订单状态,200 表示这次 HTTP 请求的结果,它们回答的是不同问题。

as_of 是快照时间,末尾的 Z 表示 UTC。响应成功,只能证明这份虚构快照怎么记载,并不是刚刚观测了真实订单。在真实服务中,重复读取可能得到更新后的状态,仍然符合“读取”的含义。

请求
  1. 模型之外客户端:URL + 凭证
  2. 文本片段GET /orders/A-104
  3. 处理步骤服务器核对订单归属

实际本地端口由操作系统分配。

响应
  1. 文本片段HTTP 200:请求成功
  2. 模型之外消息体:processing + as_of
  3. 处理步骤使用数据前先校验

超时是客户端观察,不是订单状态。

图 HTTP.1:请求结果与订单状态位于不同字段。 原创教学图示 · CC BY-SA 4.0

图 HTTP.1 标出了 URL、凭证、请求结果和业务数据分别处于哪里。客户端先检查响应,再使用订单状态。若应用随后请求模型服务,它就在第二次通信中充当客户端;订单服务器与模型服务器仍然是不同角色。

表 HTTP.1:实际本地接口及其含义。

请求条件结果客户端如何处理
带占位凭证 GET 自己的 A-104200,处理中快照校验消息体,保留时间
凭证缺失或错误401,unauthenticated修正身份认证,不盲目重试
GET 另一位虚构用户的 B-205403,forbidden停止;身份有效不代表有权限
GET 不存在的 A-999404,not_found说明没有可用记录
用 POST 尝试未提供的写操作405,method_not_allowed脚手架只允许 GET
GET /rate-limit429,rate_limited;Retry-After: 1若重试预算允许,至少等待指定的 1 秒
客户端等 /slow 0.02 秒,服务器延迟 0.15 秒客户端超时仅凭这一观察,不知道服务器是否完成

HTTP 状态码首位表示大类:2xx 通常表示成功,4xx 表示请求不能按原样完成,5xx 表示服务器一侧失败。还需要看具体代码。超时(timeout)是客户端给等待设置的上限,不是另一种 HTTP 状态码。客户端停止等待之后,服务器仍可能完成工作。

重试(retry)会再发一次请求。本书只读路径最多尝试两次,每次超时上限 0.2 秒,总期限 1 秒,退避等待从 0.05 秒开始。若 429 要求 Retry-After 1,剩余预算已经不够,就停止而不是提前重试。真实服务可能需要不同设置;这些是明确给定的教学选择,不是性能建议。限流(rate limit)是服务规则,API Key 不能绕过它。

读取通常可以重试,因为它没有请求改变业务状态。HTTP 把这种方法称为安全方法(safe method)。幂等(idempotent)指重复同一操作的预期效果与只做一次相同,不要求响应文字完全一致,也不表示服务器不记录日志。取消订单超时则不同:如果服务没有持久的幂等约定,重发可能重复产生副作用。第 42 章会用静态例子解释写操作。

凭证不要进入 Notebook 输出和记录

Section titled “凭证不要进入 Notebook 输出和记录”

API Key 是服务器核验的凭证,不是模型知识。本地客户端选做练习通过环境变量把值交给进程:

终端窗口
export BOOK_DEMO_KEY=demo-placeholder-key
import os
key = os.environ["BOOK_DEMO_KEY"]

第一行给从当前 shell 启动的进程提供一个命名值,Python 表达式负责读取。变量不存在时会抛出 KeyError,应报告配置错误,而不是带空凭证发请求。不要打印变量、把真实密钥放进 URL,或存进 Notebook 输出。环境变量能避免把值写死在代码中,但本身不是密钥保险箱。保存的 trace 用 redacted 替代凭证;自动演示只使用固定的公开占位值。

本地与远程 API 共享请求—响应结构,但信任关系、延迟、身份认证和传输保护不同。本例绑定回环地址,避免作为远程服务暴露。它不是生产用身份认证实现,不能只改一个监听地址就拿去对外提供服务。

流式传输改变的是片段到达的时机

Section titled “流式传输改变的是片段到达的时机”

服务器不必等整个消息体准备好才发送,也可以边处理边送出片段,这叫流式传输(streaming)。脚手架的 /stream 使用 text/event-stream,发送以下三个事件:

data: {"order_id": "A-104"}
data: {"status": "processing"}
data: {"complete": true}

空行结束一个事件。一次网络读取可能停在 processing 中间,也可能同时读到几个事件;网络数据块既不等于事件,也不等于模型 token。接收方需要缓存字节、正确解码 UTF-8,并收集完整事件。本样例的应用规则规定,只有收到 complete: true 才形成可用结果。如果第一个事件之后连接就断了,即使 HTTP 状态为 200,响应也未完成。流式 JSON 答案同样可能需要完整拼接和校验后才能使用。

运行本地路径,查看成功与失败

Section titled “运行本地路径,查看成功与失败”

按要求创建环境后,在书籍根目录运行 python code/knowledge-assistant/http_scaffold.py。程序会自行启动回环服务器、取得空闲端口、执行表中的读取及流式通信、演示超时,最后关闭服务。输出不含真实凭证。data/part-vii/http-run.json 保存实际运行记录,明确标注为本地 HTTP,而不是模型生成。

Notebook 展示 get(base, "/orders/A-104"):base 是 server() 给出的实际地址,不是示意用的 8765。返回值依次为 HTTP 状态、解析后的消息体和 Retry-After。流式消息体保留为文本,供检查事件结构。程序与 Tool、MCP 适配器共用 lookup 函数,不是三套独立订单数据库。

客户端通过约定的 API 向服务器发请求。HTTP 规定消息含义,JSON 表示数据。凭证、状态码、超时和流式完成标记都要分别解释。一次成功读取报告的是来源中的记录;写操作还需要另一份授权与效果契约。

  1. 响应同时出现 HTTP 200 和 JSON "status":"processing",哪一个告诉你订单是否发货?
  2. 读取请求等了 0.02 秒后超时,能否认定服务器从未查过订单?Trace 应怎样记录?
  3. 流式响应只收到订单 ID,客户端能否显示“处理中”?还缺少什么?
  4. 为什么 401 与暂时性 503 的重试处理不同?在总预算 1 秒的规则下,Retry-After 1 是否允许立即重试?
  1. 消息体字段描述订单,200 描述请求。本快照说的是处理中,不是已发货。把它当作当前状态前,还要检查时间。
  2. 不能。超时只证明客户端没有在时限内收到可用结果。应记录尝试以及完成状态未知,不能标记成“确定未执行”。
  3. 不能。还缺状态事件和完成标记。订单号本身不能证明状态,即使前一个例子用了同一个号。
  4. 401 需要有效身份认证,原样重复错误凭证不会修好它。暂时性 503 可能允许有限重试。Retry-After 1 要求等待;剩余时间不足 1 秒时,应停止,不能忽略要求提前重发。
  • RFC 9110,§§3—6、9.2、10.2.3、11、15。定义 HTTP 角色、消息、安全与幂等语义、身份认证及状态码。
  • RFC 8259,§§2—8。规定 JSON 消息体和 UTF-8 数据交换。
  • RFC 6585,§4。定义 429 和可选的 Retry-After,不规定统一限流策略。
  • WHATWG Server-sent events,§§9.2.4—9.2.6,核验于 2026-09-14。解释本地流式样例采用的事件边界。