面向 LLM 的程序设计 4:API 版本化与演进——在「模型会记忆旧文档」前提下的兼容策略

用三句话先说明白

  1. 人会照旧说明书办事,模型也一样。 它见过的文档、缓存里的接口描述、网页上没刷新的说明、向量库里还没更新的片段,都可能比真实系统更旧。于是系统已经升级了,它还在用老地址、老字段名、老例子去调用。

  2. 给人改流程,可以发邮件、约下周再切换;给 API 改接口,没有这种「大家一起听一场会」的机会。 调用方五花八门:服务器上的代码、浏览器、命令行、还有「模型根据提示词自动拼出来的请求」。如果接口不标版本、只在原地址上悄悄改含义 ,就很容易出现:线上已经是新规则,模型脑子里还是旧规则,报错东一块西一块,很难想到根因是「文档和代码对不上」。

  3. 本篇讲怎么少踩这个坑:网址里的版本号 划清界限;小改动尽量别弄坏老用户 ;大改动开新地址 ;用标准响应头告诉机器「这条接口快不用了」;发布时代码、文档、工具描述、向量库 尽量同一班车更新。


本篇在系列里的位置 :前面三篇分别讲了能力怎么暴露契约怎么定死响应怎么方便模型读 。这一篇讲 API 怎么像产品一样升级 ,以及 LLM 场景下常见的「契约漂移」 怎么缓解。

摘要(做法一览)

  • 对外接口建议在网址里写主版本,例如 /v1//v2/,让人和机器一眼知道「这是哪一版整套约定」。
  • 在同一版里,尽量只做加字段、加可选参数 这类老客户端还能用 的改动;要改到老客户端一定会坏 时,用新路径发布,不要偷偷改旧路径。
  • 旧版接口可以在响应里带上 DeprecationSunsetLink 等标准头(含义见下文),让监控和客户端能自动知道「该迁移了」。这些名字来自 RFC,生产环境请以你们选定的规范为准。
  • 发版时把 实现、OpenAPI、工具 JSON、迁移说明、文档/向量库 绑在一起更新。

关键词:API 生命周期;路径版本;向后兼容;破坏性变更;契约漂移;Deprecation;Sunset;RFC 8594;RFC 8288;RFC 9745;OpenAPI;LLM 工具定义


1 问题从哪来?

1.1 「消费者」不只是你们自己的代码

只要东西进了模型或进了检索库,都可能影响它怎么调你的 API,例如:

  • 各种文档 :Swagger、导出的 openapi.json、Wiki、PDF、被切成小块放进向量库的说明
  • 模型侧带着的内容:系统提示词里的 JSON 例子、Agent 里写死的工具说明、聊天里用户贴过的旧请求。
  • 模型当场编出来的调用 :根据上面这些材料现拼 的 URL 和字段,没人保证和线上最新版一致

所以要解决的,不只是「版本号写什么字符串」,而是:很多份、很旧的副本里,到底该信哪一版? 换版本时能不能被看见、能报警、能回退

1.2 三种常见翻车

  1. 名字没变,意思变了 :地址和字段名都和以前一样,但含义或默认值改了;旧提示词生成的请求以前能过,现在 silently 错
  2. 只升了一半 :规范已经是 v2,工具描述或向量库里还是 v1,模型两套字段混着用
  3. 嘴上说不让用,系统不知道:没有机器可读的「下线时间」,网关没法按旧流量做告警,业务也没法排期。

2 版本怎么放?------优先写在网址里

2.1 为什么 LLM 场景更推荐 /v1/ 这种「路径版本」?

版本可以放在网址HTTP 头子域名 等地方。对 Agent + 文档 来说:

  • 写在路径里 :每个示例 URL 都带着版本,人眼扫、搜文档、模型复述地址时,最不容易丢
  • 只写在头里 :用 curl 或生成代码时容易忘带;日志里若只看 URL,看不见版本,排障麻烦。
  • 更适合做小开关、小特性,不太适合单独承担「整包换一代契约」;除非你们中间件能保证每个请求都自动带头。

本系列 demo 用 /v1//v2/,就是取「一眼能看见」。

2.2 同一个 /vN/ 里面,改动分三类

  • 安全扩展 :加可选字段、响应多几个键、枚举只加不删 (同时想想下游有没有写死 switch)。
  • 结果会变、但 JSON 形状可能没变 :排序、默认每页条数、筛选规则变了------用户会觉得「怎么和以前不一样」。这类别悄悄上线,最好用公告、功能开关、或新参数显式打开
  • 老客户端一定会挂的改法 :删字段、改类型、改必填、改名------应当新开 /vN+1/;旧版在约定期内只修严重问题和安全补丁。

3 「兼容」别靠嘴说,要能检查

评审时少说「这次兼容」,多说下面三类里到底满足哪条(表格第三列是容易忽略的细节)。

说法 白话含义 实际影响(记住这一条就够)
语法 / Schema 兼容 老客户端按老样子发 JSON,服务端还认 若服务端禁止多出来的字段 ,新客户端给旧服务端发新字段 常会 422 。所以「同一路径上随便加必填/新键」往往要配合新路径先放宽、再收紧的步骤。
语义兼容 字段意思、单位、业务规则没变(或可说成「只收更窄、规则更严」) 不能只看「名字没改」,要用例子、监控、回归证明。
行为兼容 分页、排序、默认条数、去重规则一样,或有明确开关 这类改动经常骗过 JSON Schema ,但会让 Agent 输出变来变去

枚举和可选字段 :在同一路径上「枚举多几个值」「可选改成必填」常常是 breaking;非做不可时,想想 默认值、分阶段拒绝、或新路径


4 告诉机器「这条接口要退休了」------几个响应头

下面这些头不改变「这次请求业务上算不算成功」,但告诉客户端和平台:以后别依赖它了,该迁到哪

白话 参考
Sunset 打算哪天起这条地址不再用(HTTP 日期格式) RFC 8594
Deprecation 已经不建议用 ,请迁走;写法 RFC 9745 有规定,常和 Sunset 一起出现 RFC 9745
Link 可带 rel="successor-version" 等,指向下一代地址 RFC 8288

实务提醒(短)

  • Sunset计划,不是法务合同;日期还可能改。但没有它,自动化很难统一喊「要下线了」。
  • Deprecation 历史上写法不统一;新项目应在网关层定一种写法并写进规范(demo 为可读性做了简化,上生产前与你们选定的 RFC/中间件对齐)。
  • 404、5xx 上乱挂下线头容易误解成「资源没了还是版本没了」。常见做法:成功响应专门设计的迁移类 4xx 里带头;纯参数错误可不带。

5 发布时:代码和文档「同一班车」

一次发布,尽量把下面这些绑在一起 (同一工单或同一流水线):代码openapi.json工具用的 JSON (如果有)、迁移说明文档/向量库里相关段落和示例 URL。

  • 对照表 :旧字段名 → 新字段名,单位有没有变(例如字数包不包括空格),附最小 Before/After 例子。
  • CI 里对比 OpenAPI :检出 breaking 变更时,没升路径版本就别合并
  • 工具名 :若用 xxx_v2 这种后缀减少混用,要和 OpenAPI 同源,避免名和路径两套故事。
  • RAG :旧版文档标停更日期 或从索引拿掉,正文链到新版,否则检索会把过期说明当真理喂给模型。

6 上线之后看什么?

  • 流量 :按路径(或你们用的版本头)看 v1 / v2 占比 和错误率;快 Sunset 时给 v1 占比设告警。
  • 契约测试 :至少保证一类典型客户端升级后不会在静默中踩雷。
  • 文档 :Sunset 等词别自己重新定义,链到 RFC + 内部废弃政策 (提前多久、谁批、是否 410 Gone)。

7 LLM 特有风险(一表读完)

现象 可以怎么做
提示词里写死了 v1 例子 发 v2 时同步发迁移片段更新工具列表;重要环境用配置中心统一下发
模型混用 v1/v2 字段 路径分版本 + 工具定义和 OpenAPI 同版本 + 错误里写清该用哪版或文档链接(错误体专题可再写)
向量库很旧 文档与 API 同一张生命周期表 ;废弃接口的 chunk 打 deprecated: true 方便过滤

8 Demo 在演示什么?

一个进程里同时提供 两套接口,都对同一批假文档做「截断摘要」------业务相同,只为对比契约差异

  • 共同点 :按长度截断字符串。v2 在 format=bullets 时会多一步「项目符号分行」,表示新版本多了一点行为
  • POST /v1/summarize-document :老字段名;成功时响应带头 DeprecationSunsetLink 指向 v2。细节见 README_完整方案.md
  • POST /v2/summarize-document:新字段名;可选参数演示「响应里多带可选字段」。
  • GET /api-versions :返回 JSON,里面有字段对照和端点,方便 Agent 结构化读迁移说明
  • main.py :依次打 /api-versions、v1、v2,方便本地对照响应头和 body。

uvicorn server_api:app --reload --host 127.0.0.1 --port 8313,再在项目目录运行 python main.py

启动服务后,再在另外一个terminal中运行 python main.py,terminal中返回:

复制代码
=== GET /api-versions ===

{'supported': ['v1', 'v2'], 'default_recommended': 'v2', 'migration': {'v1_to_v2_request': {'doc_id': 'document_id', 'max_words': 'max_length'}, 'v1_to_v2_response': {'summary_text': 'summary', 'approx_word_count': 'word_count'}, 'v1_sunset_hint': 'See HTTP Sunset header on v1 responses (demo uses fixed date).'}, 'endpoints': {'v1_summarize': 'POST /v1/summarize-document', 'v2_summarize': 'POST /v2/summarize-document'}}

=== POST /v1/summarize-document(注意响应头 Deprecation / Sunset / Link)===       

deprecation: true
sunset: Wed, 01 Jan 2027 00:00:00 GMT
link: </v2/summarize-document>; rel="successor-version"
body: {'summary_text': '人工智能在自然语言处理领域取得显著进展。大语言模型能够完成 摘要、问答、翻译等任务。企业可将 LLM 与内部 API 结合,构建智能客服与数据分析应用。', 'approx_word_count': 75}

=== POST /v2/summarize-document(include_metadata=true)===

{'summary': '人工智能在自然语言处理领域取得显著进展。大语言模型能够完成摘要、问答、翻译等任务。企业可将 LLM 与内部 API 结合,构建智能客服与数据分析应用。', 'word_count': 75, 'format': 'plain', 'api_version': 'v2', 'document_id_echo': 'doc_001'} 

9 完整代码与文档

demo/ 目录安装依赖后执行:

uvicorn server_api:app --reload --port 8313

再运行:

python main.py

相关推荐
guslegend2 小时前
系统整体设计方案
人工智能·大模型·知识图谱
deephub2 小时前
ADK 多智能体编排:SequentialAgent、ParallelAgent 与 LoopAgent 解析
人工智能·python·大语言模型·agent
HcreateLabelView2 小时前
引领RFID电子标签打印新时代,打造标识打印系统新标杆
大数据·人工智能
wjcroom2 小时前
以太缄默-理论分析
人工智能·物理学
guslegend2 小时前
4月5日(大语言模型训练原理)
人工智能·大模型
数智化管理手记2 小时前
精益生产合理化建议核心解读:本质、价值与提报规范
大数据·网络·人工智能·低代码·制造
QfC92C02p2 小时前
Hagicode 多 AI 提供者切换与互操作实现方案
人工智能
SUNNY_SHUN2 小时前
VLM走进农田:AgriChat覆盖3000+作物品类,607K农业视觉问答基准开源
论文阅读·人工智能·算法·开源
黎阳之光3 小时前
视频孪生赋能车路云一体化,领跑智慧高速新征程
人工智能·算法·安全·数字孪生