每日 AI 实战案例

AI 自动生成 API 文档:从 8 万行无注释代码到完整 API 手册只用了 4 小时

2026-09-01
进阶开发
4 小时
使用工具:
Claude CodeGitHub MCP自定义代码分析 MCP
AI实战API文档Claude Code代码分析自动化Go

案例摘要

团队的微服务项目积累了 8 万行 Go 代码,几乎没有注释和 Swagger 定义。手动写 API 文档评估要 2 周,最终用 Claude Code + GitHub MCP + 自定义代码分析 MCP,4 小时生成了完整的 API 文档,覆盖 47 个接口、120+ 参数说明、14 个完整请求/响应示例。本文复盘工具组合、分步 Prompt 模板、以及踩过的坑(接口签名推断错误、参数类型歧义、嵌套结构解析失败)。

案例背景

我们的团队维护一个面向 B 端的订单与履约微服务,2019 年由 3 个外包同学起步,到 2026 年已经迭代了 7 年、积累 8.1 万行 Go 代码,分布在 6 个子服务(order、fulfill、inventory、settle、notify、gateway)中。团队从最早的"先跑通再说"到后来的"需求永远排不完",文档这件事一直被推后——代码里几乎没有注释,也没有任何 Swagger / OpenAPI 定义,只有少量写在 Confluence 上、最近一次更新停在 2023 年 Q2 的接口片段。

事情的引爆点是 2026 年 8 月中旬的一次接入事故:外部合作方按 Confluence 上的旧文档接 order.v2.create 接口,结果字段 shipAddress 在 v2.3 已经被拆成 province + city + district + detail,对方写死的取值逻辑直接拿到空字符串,导致一批订单创建后无法履约,回滚 + 人工补偿花了团队整整两天。事后复盘,所有人都同意:必须把 API 文档补齐,而且要能跟代码同步。

但补文档的工时评估让人绝望:

  • 接口数:order 服务 21 个、fulfill 14 个、inventory 7 个、settle 5 个,合计 47 个对外接口
  • 平均参数量:每个接口请求参数 8-15 个、响应字段 12-30 个,估算 120+ 个参数说明
  • 示例:合作方对接最看重示例,至少需要 14 个核心接口的完整请求 / 响应 JSON 样例
  • 人工预估:每个接口(读代码、定参数、写示例、复核)按 1.5 小时算,47 个接口 = 70+ 人时 ≈ 2 周 / 1 人,且容易因为读者疲劳漏掉边界字段

业务侧不可能给 2 周窗口。最终我们决定用 Claude Code + GitHub MCP + 自定义代码分析 MCP 走自动化路线,实际完成时间是 4 小时,文档输出后由 2 个同学各花了 1 小时做人工审核修正,当天就合并进 main 分支并发布到内部文档站。本文复盘整个流程。

工具组合与分工

工具分工为什么这样分工
Claude Code主推理引擎长上下文(200K)能吞下整个子服务的 router + handler + model;Go 类型推断比通用人更准;输出 Markdown 可直接成型
GitHub MCP拉代码、提 PR、跑 CI不在本地拉仓库,直接让 Agent 通过 MCP 读远程仓库文件,避免污染本地;每生成一批接口文档就提 PR 触发 CI(lint + markdownlint)
自定义代码分析 MCP结构化提取接口元数据Claude 直接读 Go 源码做正则匹配会漏掉反射路由、中间件包了一层等场景;我们写了个 MCP 专门做 AST 级提取,输出标准化 JSON(method、path、handler 包路径、入参结构体名)
本地 markdownlint + 自写校验脚本文档质量门禁校验每个接口段是否包含必备小节(描述、方法、路径、请求参数表、响应字段表、示例、错误码),缺一即 CI 失败

关键原则:MCP 出"骨架数据",Claude 出"语义解释",人只做"语义复核"。让 AI 做它擅长的(模式识别、批量格式化),让人做 AI 做不稳的(判断字段语义、补充业务背景、修边界场景)。

实施步骤

整个流程分 5 步,前 4 步纯 AI 执行,最后 1 步人工兜底。下面每一步都给出可复制的 Prompt 模板和操作说明。

第 0 步:代码扫描和接口清单提取(30 分钟)

这一步的目标只有一个:先把 47 个接口的"骨架清单"打出来,包括 HTTP 方法、URL 路径、handler 函数所在的文件:行号、入参结构体名、出参结构体名。这一步不能让 Claude 直接读源码自由发挥——它会漏,尤其是用了 gin.RouterGroup 嵌套、group.Use(middleware) 包了一层鉴权的情况。

我们写了一个自定义 MCP(基于 Go 的 go/parser + ast 包),它的工作流程是:

  1. 扫描所有 *.go 文件,找到 gin.Engine / gin.RouterGroup 的 GET/POST/PUT/DELETE/PATCH 调用
  2. 通过 AST 解析出每个调用注册的路径和 handler 函数引用
  3. 顺着 handler 函数的参数类型,找到 *gin.Context 之后的入参结构体(通常是 BindJSON / ShouldBind 的目标)
  4. 对每个结构体递归展开字段,标注每个字段的 json tag、validate tag、Go 类型
  5. 输出一个标准化 JSON:
{
  "service": "order",
  "routes": [
    {
      "method": "POST",
      "path": "/api/v2/order/create",
      "handler": "github.com/xxx/order/internal/handler.OrderHandler.Create",
      "file": "internal/handler/order_handler.go:128",
      "middleware": ["jwt.auth", "ratelimit.global", "trace.inject"],
      "request_struct": "internal/model.OrderCreateReq",
      "response_struct": "internal/model.OrderCreateResp"
    }
  ]
}

为什么必须用 AST 而不是让 Claude 自己读:我们一开始尝试让 Claude 直接读 router 文件,结果它漏掉了 11 个接口,原因是这些接口是动态注册的(通过配置文件 + reflect 动态挂载到 RouterGroup)。AST 也读不到这部分,但至少它能明确告诉你"我读到了 X 个,剩下 Y 个是动态注册的",让人知道边界在哪。最终动态路由这 3 个接口我们让人工补。

这一步的 Prompt 很简单,因为重活在 MCP 里:

调用 code-analysis MCP 的 `extract_routes` 工具,对仓库 github.com/myteam/order-service
当前 main 分支进行扫描,输出全部 HTTP 路由的元数据 JSON。

参数:
- repo: myteam/order-service
- branch: main
- scan_paths: ["internal/handler", "internal/router"]
- output_format: json
- include_middleware: true

扫描完成后:
1. 把结果保存到 ./artifacts/order-routes.json
2. 输出一份简报:共扫描到多少路由、按 HTTP 方法分布、按服务模块分布
3. 标注哪些路由是静态注册(AST 能读到)、哪些是动态注册(AST 读不到,需人工补)

实际产出(order 服务):

模块GETPOSTPUTDELETE合计
order496221
fulfill543214
inventory42107
settle22105
合计151711447

清单出来之后,我们就有了"批量任务列表",后续每一步都可以基于这个清单做并行处理。

第 1 步:逐接口签名分析和参数推断(1 小时 30 分钟)

这是最容易翻车的一步,目标是从骨架 JSON 出发,为每个接口生成完整的"参数表 + 字段语义"。难点有三个:

  1. 字段语义:结构体字段叫 Province,Claude 知道是"省份",但 ProvCode 是省份编码还是省份名缩写?需要看 validator tag 或者在代码里搜引用
  2. 嵌套结构:OrderCreateReq 里嵌套 AddressInfo、AddressInfo 又嵌套 GeoPoint,需要递归展开
  3. 类型歧义:Go 里 int64 是时间戳还是 ID?string 是手机号还是订单号?要靠 json tag + validator tag + 上下文综合判断

每个接口用 1 个独立的 Claude Code 子任务处理,并行跑 4-6 个。Prompt 模板如下:

你是 Go 微服务接口分析专家。请基于下面的输入,生成接口的完整参数文档段。

## 输入
1. 接口元数据(来自 code-analysis MCP):
{{ route_metadata_json }}

2. handler 函数源码(已通过 GitHub MCP 拉取):
{{ handler_source_code }}

3. 入参结构体源码(含所有嵌套结构体,递归拉取):
{{ request_struct_source }}

4. 出参结构体源码:
{{ response_struct_source }}

5. 相关 model/DTO 的 validator tag 和 swagger 注释(如果有):
{{ validator_tags }}

## 任务
1. 解析请求结构体,**递归**展开所有嵌套字段,输出一张参数表,列:字段路径、Go 类型、JSON key、是否必填、约束(来自 validator tag)、语义说明、示例值
2. 解析响应结构体,同样递归展开,输出响应字段表
3. 对每个字段做"语义推断"——只允许基于以下证据:
   - json tag 命名
   - validator tag(如 `validate:"required,min=11,max=11"` → 手机号)
   - 字段在 handler 中的使用方式(搜该字段在代码中的所有引用)
   - 字段在 model 中的注释(如果存在)
   **禁止臆测**。如果证据不足,"语义说明"列写"未知,待人工确认"。
4. 输出格式严格按下述 Markdown 模板:

### POST /api/v2/order/create

**功能描述**:[1-2 句话,基于 handler 中的注释 + 业务逻辑推断]

**请求参数**:

| 字段路径 | 类型 | 必填 | 约束 | 语义 | 示例 |
|---|---|---|---|---|---|
| orderNo | string | 是 | len=20 | 订单号,20 位字符 | "OD20260901000123456789" |
| shipAddress.province | string | 是 | - | 省份名 | "广东省" |
| shipAddress.geo.lat | float64 | 否 | - | 纬度(未知单位,待确认) | 23.1291 |

**响应字段**:

| 字段路径 | 类型 | 语义 | 示例 |
|---|---|---|---|
| code | int | 业务状态码,0 表示成功 | 0 |
| data.orderId | string | 订单 ID | "ORD20260901000123" |

**错误码**:[从 handler 的 switch/case 或 errors.New 中提取]

**依赖的中间件**:jwt.auth, ratelimit.global

**未决问题(需要人工确认)**:
- shipAddress.geo.lat 字段单位未明确(度 / 弧度 / 其他)
- ...

这一步的核心约束是"禁止臆测"。我们第一轮让 Claude 自由发挥,结果它给 lat 字段填了"纬度,单位为度"——但代码里没有任何地方写单位,其实业务上用的是 GCJ02 坐标系的度数,这个信息在某个内部 wiki 上,AI 根本读不到。强制要求"证据不足就标未决",把 AI 的"幻觉"暴露出来给人处理,是这一步最关键的设计。

实际跑下来,47 个接口总共标注了 38 个"未决问题",平均每个接口 0.8 个,集中在:

  • 字段单位(金额、时间戳、坐标)
  • 业务状态码的语义(code=1003 是什么意思)
  • 枚举值的完整取值范围(status 字段代码里只有 1, 2, 3,但 DB 里出现过 4)
  • 软删除字段的语义(is_deleted 是 bool 还是 0/1 int)

这 38 个问题在人审环节一次性集中处理,比边写边查快得多。

并行策略:用 Claude Code 的 Batch 模式,同时开 4 个子任务(按服务拆分:order-1、order-2、fulfill、其他)。每个子任务处理 12-15 个接口,单接口平均 2 分钟,整体 30 分钟出完,但加上 Claude 的排队和重试,实际 1.5 小时收口。

第 2 步:请求 / 响应示例生成(45 分钟)

合作方对接最看重的不是参数表,而是可以直接 copy 的 JSON 示例。这一步要解决两个问题:

  1. 示例必须类型合法(string 不能给数字、嵌套结构不能漏字段)
  2. 示例必须业务合法(订单号格式、手机号位数、金额单位这些要看起来"对")

这一步的 Prompt:

你是 API 示例生成专家。基于下面的接口参数表,生成 1 个完整的请求示例和 1 个完整的响应示例。

## 输入
{{ 接口参数表 markdown }}

## 业务上下文(从仓库的 README、内部 wiki、单元测试中聚合)
- 订单号格式:OD + yyyyMMdd + 10 位流水,如 OD20260901000123456789
- 手机号:中国大陆 11 位,1 开头
- 金额单位:分(int64),如 19900 表示 199.00 元
- 时间戳:unix 秒(int64)
- 省份/城市:使用民政部标准名称,"省"/"市"后缀保留
- 坐标系:GCJ02,单位为度

## 任务
1. 生成一个**完整**的请求 JSON 示例,要求:
   - 所有必填字段都给值
   - 所有可选字段也给值(用合理默认值,便于合作方理解字段含义)
   - 嵌套结构完整展开,不要用 "..." 省略
   - 值符合业务上下文中的格式约定
2. 生成一个**成功**的响应 JSON 示例(code=0)
3. 生成一个**常见失败**的响应 JSON 示例(选 1 个最常见的错误码)
4. 输出格式:

#### 请求示例
```json
{
  "orderNo": "OD20260901000123456789",
  "shipAddress": {
    "province": "广东省",
    "city": "深圳市",
    "district": "南山区",
    "detail": "科技园xxx路xxx号",
    "geo": {
      "lat": 22.5431,
      "lng": 113.9536
    }
  },
  "items": [
    {
      "skuId": "SKU001",
      "quantity": 2,
      "unitPrice": 19900
    }
  ]
}

成功响应

{ ... }

失败响应(错误码 1003:库存不足)

{ ... }

注意

  • 不要编造业务上下文中没有的字段格式
  • 嵌套数组至少给 2 个元素,展示数组结构
  • 金额、时间、手机号必须符合业务上下文

**踩过的坑**:第一次跑的时候,Claude 给手机号填了 `"13800138000"`,看起来没问题——但订单创建接口的 `customerPhone` 字段在 validator 里写的是 `validate:"required,e164"`,正确格式是 `+8613800138000`。后来我们在 Prompt 里加了"**严格遵守 validator tag,必要时调整业务上下文中的格式约定**",并把 validator tag 单独抽出来作为强约束。

示例生成的并行策略和第 1 步类似,按服务拆 4 路并行,平均每个接口 1 分钟,整体 45 分钟出完。

### 第 3 步:Markdown 文档组装和格式化(30 分钟)

前两步产出的是 47 个独立的接口文档段(散落在 `./artifacts/order/`、`./artifacts/fulfill/` 等目录下)。这一步要把它们组装成一份结构化、可检索的 API 手册。

组装任务的 Prompt:

你是 API 文档组装专家。基于下面的接口文档段(每个接口一个独立的 Markdown 文件),组装一份完整的 API 手册。

输入

  • ./artifacts/order/*.md (21 个接口文档段)
  • ./artifacts/fulfill/*.md (14 个)
  • ./artifacts/inventory/*.md (7 个)
  • ./artifacts/settle/*.md (5 个)

输出

一份完整的 Markdown 文件,结构如下:

订单与履约服务 API 文档

版本

  • 文档版本:v2.4
  • 生成时间:2026-09-01
  • 对应代码分支:main @ commit abc1234

通用约定

请求头

Header必填说明
Authorization是Bearer ${token}
X-Request-Id否链路追踪 ID,建议传入
Content-Type是application/json

通用响应结构

{
  "code": 0,
  "msg": "ok",
  "data": { ... },
  "traceId": "abc123"
}

错误码总表

[从所有接口文档段中聚合错误码,去重后按 code 排序]

接口列表

订单服务(Order)

[按字母序排列 21 个接口]

履约服务(Fulfill)

[14 个]

库存服务(Inventory)

[7 个]

结算服务(Settle)

[5 个]

附录

字段单位约定

  • 金额:分(int64)
  • 时间戳:unix 秒
  • 坐标:GCJ02,度

枚举值速查

[从所有接口文档段中聚合枚举字段,列全取值]

变更日志

  • 2026-09-01:首次自动生成,覆盖 47 个接口

**组装时的关键约束**:

1. **去重**:错误码、枚举值要在文档末尾的附录里聚合一次,但接口段里保留原始引用(便于读者上下文理解)
2. **排序**:接口按服务分组,组内按 URL 字母序,不要按"重要性"排(主观排序在维护时会乱)
3. **交叉引用**:如果接口 A 的响应字段引用了接口 B 创建的资源(如 `orderId`),在字段说明里加 `[详见 POST /api/v2/order/create]` 链接
4. **版本对齐**:文档版本号 = 代码 commit hash 前 7 位,文档生成时间用 ISO 8601

组装完成后,通过 GitHub MCP 提 PR:

调用 GitHub MCP 的 create_pull_request 工具:

  • repo: myteam/api-docs
  • title: "docs: 自动生成订单与履约服务 API 文档 v2.4"
  • head: feat/auto-api-doc-20260901
  • base: main
  • body: |

    变更说明

    • 首次自动生成,覆盖 47 个接口、120+ 参数说明、14 个完整示例
    • 由 Claude Code + code-analysis MCP 生成,待人工审核
    • 对应代码 commit: myteam/order-service@abc1234
    • 38 个未决问题已标注在文档中的"未决问题"小节
  • commits:
    • file: docs/api/v2.4/order-fulfill-api.md content: {{ assembled_markdown }}

PR 提交后会触发 CI(markdownlint + 自写校验脚本),校验每个接口段必须包含 7 个必备小节(功能描述、请求参数表、响应字段表、请求示例、成功响应、失败响应、错误码),缺一即失败。第一次跑 CI 失败了 3 个接口(缺失败响应示例),重跑一次后通过。

### 第 4 步:人工审核和修正(1 小时)

AI 生成的文档即使 Prompt 再严格,也会有边界 case 漏掉。我们安排 2 个同学各 1 小时做交叉审核:

- **同学 A**(订单服务 owner):审核 order + settle 共 26 个接口
- **同学 B**(履约服务 owner):审核 fulfill + inventory 共 21 个接口

审核重点放在三件事:

1. **未决问题清零**:第 1 步标注的 38 个未决问题,由对应 owner 当场确认。绝大多数是字段单位、枚举值这种 DBA / 业务同学一眼能看出答案的问题,平均每个 30 秒,合计 20 分钟清完
2. **示例业务正确性**:金额单位、时间戳格式、手机号格式这些容易"看起来对但实际错"的地方,人工抽样 5-10 个接口对照线上数据验证
3. **隐式契约补充**:AI 读不到的"代码里没写但业务上必须遵守"的规则——比如 `order.v2.create` 接口的 `items` 数组虽然没在 validator 里限制长度,但业务上单次最多 100 个 SKU(由 `ratelimit.global` 中间件控制)。这类规则由 owner 人工补到"业务约束"小节

**人工修正的统计**:

| 修正类型 | 数量 | 占比 |
|---|---|---|
| 未决问题清零 | 38 | 67.9% |
| 示例值修正 | 9 | 16.1% |
| 隐式契约补充 | 6 | 10.7% |
| 字段语义修正 | 3 | 5.4% |
| **合计** | **56** | 100% |

也就是说,AI 生成的 47 个接口文档里,**人工总共改了 56 处**,平均每个接口 1.2 处——比"从零写"节省的工作量是数量级的。而且改的都是"补全"而非"重写",review 的认知负担远低于自己写。

人工修正后直接 push 到 PR 分支,CI 再跑一次通过,merge 进 main,发布到内部文档站。整个流程从启动到文档上线,**实际耗时 4 小时**。

## 关键 Prompt 模板

下面是整个流程中最核心的 3 个 Prompt 模板,复制即用。

### 模板 1:接口元数据提取(第 0 步)

你是 API 文档自动化流水线的调度器。请按以下步骤执行接口元数据提取:

步骤

  1. 调用 code-analysis MCP 的 extract_routes 工具,对目标仓库做 AST 扫描:

    • repo: {{ repo_full_name }}
    • branch: {{ branch }}
    • scan_paths: ["internal/handler", "internal/router", "internal/api"]
    • include_middleware: true
    • output_format: json
  2. 调用 GitHub MCP 的 get_file_content 工具,拉取以下文件用于后续上下文:

    • README.md
    • docs/architecture.md(如果存在)
    • Makefile(用于识别服务入口)
  3. 对 extract_routes 的输出做以下处理: a. 按 service 分组 b. 在每个 service 内按 HTTP 方法 + URL 路径排序 c. 标注每个路由的"注册类型":static(AST 直接读到)/ dynamic(通过 reflect 或配置注册,AST 读不到) d. 对 dynamic 路由,输出"待人工补充"清单

  4. 输出两个产物:

    • ./artifacts/{{ service }}/routes.json:标准化路由元数据
    • ./artifacts/{{ service }}/routes-summary.md:人类可读的简报,含统计表格和待办清单

校验条件(任一不满足即视为本步失败,停止后续步骤)

  • 扫描到的路由数 >= 40(业务上已知至少 47 个)
  • 静态路由占比 >= 90%
  • 每个路由都包含 file:line 引用,便于后续追溯

### 模板 2:接口参数推断(第 1 步,核心)

你是 Go 微服务接口分析专家。请基于下方输入,生成接口的完整参数文档段。

输入

  1. 接口元数据 JSON: {{ route_metadata_json }}

  2. handler 函数源码: {{ handler_source_code }}

  3. 入参结构体源码(含所有嵌套结构体,递归展开): {{ request_struct_source }}

  4. 出参结构体源码(含所有嵌套结构体): {{ response_struct_source }}

  5. handler 中引用的 model/DTO 源码片段(用于语义推断): {{ model_source_snippets }}

任务

  1. 递归解析请求结构体,输出参数表,列:字段路径、Go 类型、JSON key、是否必填、约束、语义说明、示例值
  2. 递归解析响应结构体,同样输出字段表
  3. 字段语义推断的证据链必须满足以下任一: a. json tag 命名清晰(如 customer_phone → 客户手机号) b. validator tag 提供约束(如 validate:"required,e164" → E.164 格式电话) c. 字段在 handler / model 代码中有明确使用(如被传给 time.Unix() → 时间戳) d. 字段在 model 注释中有说明 若证据不足,"语义说明"列必须填"未知,待人工确认",并在"未决问题"小节列出。禁止根据字段名自由发挥。
  4. 从 handler 的 switch err := ...; case errors.New(...) 或 return c.JSON(..., ErrorResponse{Code: xxx}) 中提取错误码,输出错误码表
  5. 输出格式严格遵循下述 Markdown 模板(每个小节必须有,缺一即不合格):

{{ METHOD }} {{ PATH }}

功能描述:[1-2 句话,仅基于代码可见证据,禁止臆测业务背景]

中间件链:[从 route_metadata_json 的 middleware 字段抄写]

请求参数:

字段路径类型必填约束语义示例
..................

响应字段:

字段路径类型语义示例
............

错误码:

codeHTTP 状态含义
0200成功
1001400参数错误
.........

未决问题(需人工确认):

  • [问题 1,含字段路径和待确认点]
  • [问题 2]
  • ...

自检(输出前必须逐项确认)

  • 所有必填字段的"约束"列非空(来自 validator tag)
  • 所有嵌套结构递归展开,未出现 "..." 省略
  • 所有"语义"列要么有证据链,要么标注"未知,待人工确认"
  • 错误码表至少包含成功码(0)和 1 个失败码
  • 未决问题小节即使为空也保留,写"无"

### 模板 3:示例生成(第 2 步)

你是 API 示例生成专家。基于下方接口参数表,生成完整的请求/响应 JSON 示例。

输入

{{ 接口参数表 markdown,来自第 1 步输出 }}

业务上下文(强约束,违反即不合格)

  • 订单号格式:OD + yyyyMMdd + 10 位流水号
  • 客户手机号:E.164 格式,+86 前缀
  • 金额单位:分(int64),19900 表示 199.00 元
  • 时间戳:unix 秒(int64)
  • 省份/城市/区县:民政部标准名称,保留"省"/"市"/"区"后缀
  • 坐标系:GCJ02,单位为度,6 位小数
  • SKU 编号:SKU + 6 位数字
  • 订单状态枚举:1=待支付、2=已支付、3=履约中、4=已完成、5=已取消

任务

  1. 生成 1 个完整请求示例:

    • 所有必填字段给值
    • 所有可选字段也给值(用合理默认值)
    • 嵌套结构完整展开,数组至少 2 个元素
    • 值严格遵守上述业务上下文
    • 如果字段有 validator tag,值必须通过 validator
  2. 生成 1 个成功响应示例(code=0):

    • 响应字段完整展开
    • 业务状态码 = 0
    • 包含 traceId
  3. 生成 1 个常见失败响应示例:

    • 从错误码表中选 1 个最常见的失败码
    • msg 字段给出符合错误码语义的错误说明

输出格式

请求示例

{ ... }

成功响应

{ ... }

失败响应(错误码 {{ 选择的错误码 }}:{{ 错误码含义 }})

{ ... }

自检

  • 所有 string 字段值用双引号
  • 所有数字字段没有引号
  • 所有必填字段都有值
  • 示例值通过 validator tag 约束
  • 嵌套数组 >= 2 个元素
  • 金额、时间、手机号、坐标格式符合业务上下文

## 效果数据

| 指标 | 纯人工方案 | AI 辅助方案 | 提升 |
|---|---|---|---|
| 总耗时 | 70+ 人时(≈ 2 周 / 1 人) | 4 小时(AI 3h + 人审 1h) | **17.5x** |
| 接口覆盖数 | 47 | 47 | 持平 |
| 参数说明数 | 120+ | 128 | +6.7%(AI 多挖了一些隐藏字段) |
| 完整请求/响应示例数 | 14(计划) | 47(每接口都有) | **3.4x** |
| 错误码说明数 | 估约 30 | 42 | +40% |
| 字段语义准确率 | ~95%(人写也会出错) | 91%(56 处人工修正 / 总字段数) | -4% |
| 文档格式一致性 | 中(多人手写风格不一) | 高(统一模板) | 显著提升 |
| 可维护性 | 低(人写完没人愿意改) | 高(脚本化,可随代码 re-gen) | 质变 |
| 协作方对接平均耗时 | 3-5 天 | 1-2 天 | -60% |

**关键观察**:

1. **AI 方案不是"更快的人",而是"另一种产物"**——人工方案计划只出 14 个示例(按"核心接口"优先),AI 方案直接全量 47 个示例都出。质量上 AI 略低(91% vs 95%),但覆盖面碾压
2. **可维护性是最大的隐性收益**:以前文档更新要排需求,现在每次发版前跑一遍流水线,diff 出来人审 10 分钟就能合并,文档和代码的漂移问题从根上解决
3. **协作方对接提速 60%**:合作方拿到的不是"14 个核心接口文档",而是全量 47 个接口的完整手册,自助对接不需要再开会对齐字段

**成本核算**:

| 项目 | 金额 |
|---|---|
| Claude Code API 调用(约 2000 万 token) | ¥XXX |
| GitHub MCP 调用 | 免费(已付费组织账号) |
| 自定义 code-analysis MCP 开发 | 2 人天(一次性投入) |
| 人工审核 | 2 人 × 1 小时 = 2 人时 |
| **合计**(除一次性投入) | 远低于 2 周人力成本 |

## 踩坑经验

下面是我们在 4 小时里实际踩到的坑,以及对应的解决方案。这部分是整个案例最有价值的部分——Prompt 模板可以照抄,但这些坑每个团队都会遇到。

### 坑 1:接口签名推断错误(动态路由被漏掉)

**现象**:第 0 步用 AST 扫描 order 服务,输出 18 个路由,但实际线上有 21 个。漏掉的 3 个是 `order.batch.create`、`order.batch.cancel`、`order.batch.query`——它们是通过配置文件 + `reflect` 动态注册的:

```go
// internal/router/dynamic.go
func RegisterBatchRoutes(r *gin.Engine, cfg *config.BatchConfig) {
    handlerType := reflect.TypeOf(&BatchHandler{})
    for _, route := range cfg.Routes {
        method := reflect.ValueOf(handlerType).MethodByName(route.Handler)
        r.Handle(route.Method, route.Path, method.Interface().(gin.HandlerFunc))
    }
}

AST 完全读不到这部分——它只能看到 r.Handle(...) 调用,但参数都是运行时变量。

解决方案:

  1. MCP 加一个"动态路由探测器":扫描 r.Handle / r.Any 等动态调用,如果参数不是字符串字面量,就标记为"动态路由,待人工补充"
  2. Prompt 里加"已知边界":明确告诉 Claude "AST 扫描可能漏掉 reflect 注册的路由,请检查仓库内是否有 reflect.ValueOf(...).MethodByName(...) 模式,如果有,把相关文件列出来作为待办"
  3. 兜底:人工补这 3 个接口,每个 5 分钟(因为代码结构清楚,照着静态路由的模板填就行)

经验:任何自动化都有边界,提前在 Prompt 里告诉 AI 边界在哪,比让 AI 假装无所不能更重要。

坑 2:参数类型歧义(int64 是 ID 还是时间戳)

现象:OrderCreateResp 结构体里有个字段 createTime int64,Claude 在"语义说明"列填了"创建时间,时间戳"。人审时发现这个字段其实是 订单创建时间的 unix 毫秒,不是秒——代码里 time.Unix(0, createTime * 1e6) 暴露了单位,但 Claude 没注意到 * 1e6 这个细节。

类似的还有:

  • userId int64:Claude 标"用户 ID"——对的
  • merchantId int64:Claude 标"商户 ID"——对的
  • updatedAt int64:Claude 标"更新时间,时间戳"——单位错,实际是毫秒
  • expireTime int64:Claude 标"过期时间"——字段语义错,实际是"距创建多少秒后过期"的相对时间,不是绝对时间戳

根因:Go 的 int64 是个"万能数字类型",类型本身不携带语义。Claude 默认推断"以 Time 结尾的 int64 = 时间戳",但项目中混用了秒、毫秒、相对时间三种约定。

解决方案:

  1. 第 1 步 Prompt 加"字段单位强校验"规则:
    - 任何 int64 字段,如果字段名包含 time/timestamp,必须查 handler 中该字段的使用方式:
      - 如果被传给 time.Unix(x, 0) → 秒
      - 如果被传给 time.Unix(0, x) 或 time.UnixMilli(x) → 毫秒
      - 如果被传给 time.Unix(0, x*1e6) → 毫秒(手动 *1e6 转 纳秒)
      - 如果被用在 time.Sleep(time.Duration(x) * time.Second) → 相对秒数
    - 找不到使用方式的,"语义"列填"未知时间单位,待人工确认"
    
  2. 业务上下文加全局约定:在 Prompt 的"业务上下文"段里写明"本服务所有时间字段统一为 unix 毫秒,除非代码中显式转换",让 AI 优先按毫秒推断
  3. 人审清单加"时间字段必查":所有 time/timestamp 字段在审核时强制对照代码确认单位

经验:类型系统的语义不足,要靠 Prompt 注入"项目级约定"来补足。这类坑不会消失,但可以靠"强校验规则"把错误率从 30% 降到 5% 以下。

坑 3:嵌套结构解析失败(深度过深 / 循环引用)

现象:OrderCreateResp 嵌套了 OrderInfo,OrderInfo 嵌套了 AddressInfo,AddressInfo 嵌套了 GeoPoint,GeoPoint 又引用回 OrderInfo(用于反向关联)。Claude 在递归展开时遇到了循环引用,直接停止展开,输出了 "geoPoint": {"orderInfo": "..."}——把整个嵌套结构截断了。

类似的还有:

  • 深度超过 5 层的嵌套,Claude 会在第 5 层自动停止
  • 切片嵌套结构体([]OrderItem,OrderItem 又嵌套 []PromotionRule),二维数组场景容易漏字段
  • 指针字段(*AddressInfo)和值字段(AddressInfo)的区分,Claude 偶尔会混淆 nullable 语义

解决方案:

  1. MCP 在结构体提取阶段就处理循环引用:维护一个 visited map[string]bool,遇到已访问的结构体就标记为 [ref: structName],不再展开
  2. Prompt 显式声明"嵌套深度策略":
    - 嵌套结构递归展开,最多 8 层
    - 遇到循环引用(A 引用 B,B 又引用 A),在字段说明中标注 "[循环引用,见 struct X]",不再展开
    - 指针字段(*T)和值字段(T)在"必填"列区分:指针字段默认可选(除非 validator 标 required),值字段默认必填
    - 切片字段([]T):T 仍然递归展开,参数表里给"T 的字段"列前缀 "[array element]"
    
  3. 针对二维数组:[][]string 这种在参数表里直接标注"二维字符串数组,外层 N、内层 M",不再尝试展开成表格

经验:递归类任务必须给 AI 明确的"终止条件"和"循环处理规则"。让 AI 自己决定何时停止,结果不可预测。

坑 4:错误码提取不全(错误码分散在多层调用链)

现象:handler 里的 return c.JSON(..., ErrorResponse{Code: 1001}) 只能拿到顶层错误码,但实际业务错误码大多定义在 service / repo 层:

// internal/service/order_service.go
func (s *OrderService) Create(ctx context.Context, req *OrderCreateReq) (*OrderCreateResp, error) {
    if err := s.validateStock(req); err != nil {
        return nil, errors.New("1003:库存不足")
    }
    if err := s.repo.Create(ctx, order); err != nil {
        return nil, fmt.Errorf("2001:数据库错误:%w", err)
    }
    ...
}

Claude 在第 1 步只读了 handler 文件,所以错误码表里只有 1001、0,漏掉了 1003、2001 等。

解决方案:

  1. 第 1 步 Prompt 加"错误码跨层提取":
    - 错误码提取不止读 handler,必须追溯 handler 调用的 service 层函数
    - 用 grep 模式 `errors\.New\("[0-9]+:` 和 `fmt\.Errorf\("[0-9]+:` 全仓库搜索
    - 对每个匹配项,输出"错误码 + 错误消息 + 所在文件:行号 + handler 调用链"
    - 错误码表按 code 排序,去重
    
  2. MCP 加 extract_error_codes 工具:用正则扫全仓库的 errors.New("xxx:yyy") 和 fmt.Errorf("xxx:yyy:zzz") 模式,输出标准化列表
  3. 人审清单加"错误码必查":每个接口审核时,对照线上日志的错误码分布,确认文档里没有遗漏

效果:第一轮错误码表只覆盖 18 个,加上跨层提取后达到 42 个,覆盖率 +133%。

坑 5:示例值不符合业务约束(金额单位混乱)

现象:Claude 给 unitPrice 字段填了 199.00——但项目里金额单位统一是分(int64),正确值应该是 19900。这个坑在第 2 步最容易踩,因为 Claude 默认的"金额"概念是"元 + 小数"。

类似的问题:

  • 手机号填 "13800138000"(11 位)但 validator 是 e164,应该 "+8613800138000"
  • 时间戳填 1693526400(秒)但项目用毫秒,应该 1693526400000
  • 坐标填 23.1291(4 位小数)但项目约定 6 位小数,应该 23.129100

解决方案:业务上下文必须作为"强约束"注入 Prompt,见上面模板 3 的"业务上下文"段。这部分内容由人工提前写好(一次性投入 30 分钟),但能避免后续 47 个接口 × 平均 3 个示例字段 = 140+ 处修正。

经验:示例生成的关键不是 AI 的想象力,而是 AI 能不能拿到准确的"业务约束清单"。这清单本来就应该写在项目文档里——如果没有,这次正好补上。

坑 6:中间件影响未在文档体现

现象:order.v2.create 接口挂了 ratelimit.global 中间件,限制单 IP 每秒 10 次请求。但 AI 生成的文档里完全没提限流——因为中间件不在 handler 函数里,Claude 读 handler 源码时根本看不到。

合作方对接时踩了这个坑:批量创建订单时被限流 429,但文档里没写限流策略,合作方以为是接口 bug。

解决方案:

  1. 第 1 步 Prompt 加"中间件影响说明":
    - 接口的 middleware 字段不为空时,必须输出"中间件影响"小节
    - 对每个中间件,说明它对请求方的影响:
      - jwt.auth → 请求必须带 Authorization header
      - ratelimit.global → 限流策略:单 IP 每秒 N 次(N 从中间件配置读取)
      - trace.inject → 建议请求方传 X-Request-Id
      - cors → 允许的 Origin 列表
    - 中间件配置如果在代码里硬编码,直接抄值;如果在配置文件里,标注"配置项:xxx,默认值:yyy"
    
  2. 人审清单加"中间件必查":每个接口审核时,对照中间件列表确认"影响说明"完整

经验:接口文档不止是"参数 + 响应",还要写"调用约束"。中间件、限流、鉴权这些横切关注点,是合作方最容易踩坑的地方。

坑 7:Markdown 格式不统一(多人协作时翻车)

现象:第 1 步并行跑 4 个子任务时,虽然 Prompt 模板一致,但 4 个子任务输出的 Markdown 细节有差异:

  • 表格列宽不一致
  • 代码块语言标识有的写 json、有的写 javascript
  • 标题层级有的用 ###、有的用 ####
  • 字段路径有的用 a.b.c、有的用 a → b → c

解决方案:

  1. 统一模板的"硬约束":在 Prompt 末尾加"格式校验清单",明确每个细节
  2. CI 加 markdownlint:.markdownlint.json 配置严格规则(标题层级、代码块语言、表格格式),CI 失败即拒绝合并
  3. 自写校验脚本:检查每个接口段是否包含 7 个必备小节(功能描述、请求参数表、响应字段表、请求示例、成功响应、失败响应、错误码),缺一即失败

经验:并行任务越多的流水线,越要靠"机器校验"保证一致性。靠 Prompt 约束只能做到 90%,剩下 10% 必须靠 CI。

坑 8:版本对齐失败(文档和代码漂移)

现象:第一次跑完流水线后,文档里写的"对应代码 commit: abc1234",但 PR review 时发现 main 分支已经又合并了 2 个 commit,文档和代码已经不对齐了。

解决方案:

  1. 流水线改为一键触发:CI 配置一个 docs:auto-gen job,监听 main 分支的 push 事件,自动跑完整流水线 + 提 PR
  2. 文档头部的 commit hash 用 git rev-parse --short HEAD 动态获取,不再硬编码
  3. 每周一定时全量重跑:即使 main 没合并,也重跑一次,确保文档和代码不漂移

经验:文档自动化必须做成"持续生成",不是"一次性项目"。否则文档会从"刚生成的"变成"最新的过期文档",比没有还糟。

进阶话题:如何把这套流程复制到其他项目

这套流程跑通后,我们把它复制到了另外 3 个微服务(用户中心、支付网关、消息推送),平均每个项目的接入时间是 1-2 人天,主要是:

  1. 自定义 code-analysis MCP 适配(约 4 小时):每个项目的 router 库可能不同(gin / echo / chi / 原生 net/http),需要写对应的 AST 提取规则
  2. 业务上下文清单整理(约 2 小时):把项目特有的字段单位、枚举值、ID 格式约定写进 Prompt 模板的"业务上下文"段
  3. 错误码提取规则适配(约 1 小时):每个项目的 errors.New 模式可能不同,调整 MCP 的正则
  4. CI 校验脚本接入(约 1 小时):把 markdownlint + 自写校验脚本接入项目的 CI

通用性建议:

项目特征适配难度说明
Go + gin + 标准 MVC低直接套用,几乎不用改
Go + echo / chi低改 AST 提取的库匹配规则
Go + 原生 net/http中路由注册方式不同,需要重新写提取逻辑
Java + Spring Boot中AST 改为 JavaParser,注解驱动的路由提取
Python + FastAPI低FastAPI 自带 OpenAPI,AI 主要补语义和示例
Python + Flask中路由装饰器提取,类似 gin
Node.js + Express中路由注册方式多样,AST 提取复杂
Node.js + NestJS低装饰器驱动,类似 Spring Boot

关键经验:AST 提取逻辑是这套流程的"地基",必须用项目对应的语言生态去做。不要指望一个通用的 MCP 处理所有语言——Go 的 go/parser 比 Java 的 JavaParser 易用,比 Python 的 ast 模块更严格。每个语言生态都有自己的"事实标准"AST 工具,用对工具事半功倍。

经验总结

走完整套流程,我们的核心经验可以归纳为 7 条:

1. AI 擅长"批量格式化",不擅长"语义判断"

AI 在 4 小时内生成 47 个接口的参数表、字段表、示例——这种"模式识别 + 批量格式化"是它的强项。但字段语义判断(int64 是 ID 还是时间戳?status=4 是什么意思?)必须靠人审。不要幻想 AI 能完全替代人,它的价值是"把人的工作从 70 小时压到 1 小时",而不是"让人没事干"。

2. Prompt 模板的"硬约束"比"软建议"重要 100 倍

第一版 Prompt 写"请尽量准确地推断字段语义"——结果 AI 自由发挥,错误率 30%。改成"字段语义必须有证据链,证据不足必须标注'待人工确认'"——错误率降到 5% 以下。Prompt 越严格,AI 输出越可预测;越"宽松友好",AI 越容易幻觉。

3. 自定义 MCP 是"必要的过度设计"

一开始我们想直接用 Claude 读源码 + GitHub MCP,结果漏了 11 个接口、错误码提取不全、嵌套结构截断。自定义 code-analysis MCP 用 AST 做结构化提取,把"不可预测的自由发挥"变成"可预测的骨架数据"——一次性投入 2 人天,但后续每个项目复用只需 4 小时适配。通用 MCP 解决不了语言/框架特定的提取逻辑。

4. 业务上下文清单是"隐性资产"

整理"金额单位是分"、"时间戳是毫秒"、"手机号是 E.164"这些约定的过程,本身就是在沉淀项目知识。这份清单不仅喂给 AI,也可以作为新人 onboarding 材料、作为 code review 的 checklist。很多项目的"隐性知识"从来没被写下来过,AI 流水线倒逼它显性化。

5. 人工审核必须"有重点"

让人工审核 47 个接口 × 平均 20 个字段 = 940 个字段,人会疯。审核重点必须是:

  • 未决问题清零(AI 主动暴露的"我不知道")
  • 示例值业务正确性(看起来对但实际错的)
  • 隐式契约补充(代码里没写但业务必须遵守的)

这三类问题占人工修正的 95%。其他细节靠 CI 校验。

6. CI 校验是"最后一道防线"

markdownlint + 自写校验脚本(必备小节检查、字段路径格式、代码块语言标识)能挡住 90% 的格式问题。人工 review 应该只看"语义",格式问题交给机器。我们的 CI 配置:

# .github/workflows/docs-check.yml
name: Docs Check
on:
  pull_request:
    paths:
      - 'docs/api/**'
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: markdownlint
        uses: DavidAnson/markdownlint-cli2-action@v13
        with:
          globs: 'docs/api/**/*.md'
      - name: section check
        run: node scripts/check-api-doc-sections.js docs/api/
      - name: example check
        run: node scripts/check-api-doc-examples.js docs/api/

check-api-doc-sections.js 校验每个接口段是否包含 7 个必备小节,check-api-doc-examples.js 校验每个接口是否有完整的请求/响应 JSON 示例且能通过 JSON 解析。

7. 文档自动化是"持续工程"不是"一次性项目"

第一次跑通流水线只是开始。真正有价值的是把流水线接入 CI,让文档随代码 commit 自动 re-gen。我们现在的做法:

  • main 分支 push 触发:自动跑完整流水线,提 PR
  • 每周一定时全量重跑:即使 main 没合并,也重跑一次防漂移
  • 发版前必跑:发版 checklist 里有"文档已更新且通过 CI"这一项,没打勾不能发版

文档和代码的漂移问题,只能靠"持续生成"解决,不能靠"人记得改"。

附录:完整 Prompt 模板汇总

下面是整个流程的完整 Prompt 模板汇总,按步骤组织。每个模板都包含:输入、任务、输出格式、自检清单。复制即用,按需替换 {{ }} 占位符。

第 0 步:接口元数据提取 Prompt

你是 API 文档自动化流水线的调度器。请按以下步骤执行接口元数据提取。

## 步骤

### 1. AST 扫描
调用 code-analysis MCP 的 `extract_routes` 工具:
- repo: {{ repo_full_name }}
- branch: {{ branch }}
- scan_paths: {{ scan_paths }}
- include_middleware: true
- output_format: json

### 2. 上下文文件拉取
调用 GitHub MCP 的 `get_file_content`:
- README.md
- docs/architecture.md(如果存在)
- Makefile

### 3. 结果处理
a. 按 service 分组
b. 每个组内按 HTTP 方法 + URL 排序
c. 标注注册类型:static / dynamic
d. 对 dynamic 路由输出"待人工补充"清单

### 4. 产物输出
- ./artifacts/{{ service }}/routes.json
- ./artifacts/{{ service }}/routes-summary.md

## 校验条件
- 扫描路由数 >= 40
- 静态路由占比 >= 90%
- 每个路由含 file:line 引用

第 1 步:接口参数推断 Prompt

(见上文"关键 Prompt 模板 - 模板 2",此处略)

第 2 步:示例生成 Prompt

(见上文"关键 Prompt 模板 - 模板 3",此处略)

第 3 步:文档组装 Prompt

你是 API 文档组装专家。基于下方接口文档段,组装完整 API 手册。

## 输入
- ./artifacts/{{ service }}/*.md

## 输出结构
# {{ service_name }} API 文档

## 版本
- 文档版本:{{ version }}
- 生成时间:{{ timestamp }}
- 对应代码:{{ repo }}@{{ commit_short }}

## 通用约定
### 请求头
| Header | 必填 | 说明 |
### 通用响应结构
### 错误码总表(聚合去重)

## 接口列表
### {{ service }}(共 N 个)
[按 URL 字母序]

## 附录
### 字段单位约定
### 枚举值速查
### 变更日志

## 组装规则
1. 错误码在附录聚合一次,接口段保留引用
2. 接口按 URL 字母序,不按"重要性"
3. 交叉引用用 [详见 METHOD /path] 链接
4. 版本号用 commit hash 前 7 位
5. 时间用 ISO 8601

第 4 步:人工审核 Checklist

## 人工审核 Checklist

### 未决问题清零(必做)
- [ ] 所有"未知,待人工确认"字段已确认
- [ ] 所有时间字段单位已确认(秒/毫秒/相对)
- [ ] 所有枚举字段取值范围已补全

### 示例业务正确性(抽样 5-10 个接口)
- [ ] 金额单位为分(int64)
- [ ] 时间戳单位与项目约定一致
- [ ] 手机号格式符合 validator
- [ ] 坐标精度符合项目约定
- [ ] 订单号/SKU 号格式正确

### 隐式契约补充
- [ ] 限流策略已写入"中间件影响"
- [ ] 业务约束(如数组最大长度)已补充
- [ ] 跨接口字段引用关系已标注

### 格式校验(CI 已覆盖,人工可跳过)
- [ ] markdownlint 通过
- [ ] 必备小节齐全
- [ ] 代码块语言标识正确

结语

这次 API 文档自动化的实践,让我们对 AI 在"工程化场景"中的定位有了更清晰的认识:

  1. AI 不是"代替人",而是"放大人"——它把 70 小时的体力活压到 4 小时,让人能把时间花在真正需要判断的地方
  2. Prompt 工程 + 工具链 + 流程设计,三者缺一不可——光有好 Prompt 没有自定义 MCP 会漏接口,光有 MCP 没有 Prompt 会幻觉,光有两者没有 CI 会格式混乱
  3. "显性化隐性知识"是最大收益——整理业务上下文、字段单位、隐式契约的过程,本身就是项目知识沉淀

我们计划把这套流程推广到团队其他微服务,并把自定义 code-analysis MCP 开源(适配 gin / echo / chi 三个 Go 框架)。如果你也在为"祖传代码补文档"发愁,欢迎参考这套方案——4 小时把 2 周的活干完,剩下的时间可以真正去写代码。


附:流水线代码结构

api-doc-pipeline/
├── mcp/
│   └── code-analysis/           # 自定义代码分析 MCP
│       ├── main.go
│       ├── ast_extractor.go     # AST 路由提取
│       ├── struct_parser.go     # 结构体递归解析
│       ├── error_code_scanner.go
│       └── dynamic_route_detector.go
├── prompts/
│   ├── 0-extract-routes.md
│   ├── 1-infer-params.md
│   ├── 2-generate-examples.md
│   ├── 3-assemble-docs.md
│   └── 4-review-checklist.md
├── scripts/
│   ├── check-api-doc-sections.js
│   ├── check-api-doc-examples.js
│   └── run-pipeline.sh
├── artifacts/
│   ├── order/
│   ├── fulfill/
│   ├── inventory/
│   └── settle/
└── docs/
    └── api/
        └── v2.4/
            └── order-fulfill-api.md   # 最终产出

附:自定义 code-analysis MCP 核心代码片段(AST 路由提取)

package main

import (
    "encoding/json"
    "go/ast"
    "go/parser"
    "go/token"
)

type RouteInfo struct {
    Method         string   `json:"method"`
    Path           string   `json:"path"`
    Handler        string   `json:"handler"`
    File           string   `json:"file"`
    Line           int      `json:"line"`
    Middleware     []string `json:"middleware"`
    RequestStruct  string   `json:"request_struct"`
    ResponseStruct string   `json:"response_struct"`
    RegisterType   string   `json:"register_type"` // static / dynamic
}

func ExtractRoutes(repoPath, scanPath string) ([]RouteInfo, error) {
    fset := token.NewFileSet()
    var routes []RouteInfo

    pkgs, err := parser.ParseDir(fset, scanPath, nil, parser.ParseComments)
    if err != nil {
        return nil, err
    }

    for _, pkg := range pkgs {
        for filename, file := range pkg.Files {
            ast.Inspect(file, func(n ast.Node) bool {
                call, ok := n.(*ast.CallExpr)
                if !ok {
                    return true
                }

                sel, ok := call.Fun.(*ast.SelectorExpr)
                if !ok {
                    return true
                }

                method := sel.Sel.Name
                if !isHTTPMethod(method) {
                    return true
                }

                // 静态路由:参数都是字面量
                if len(call.Args) >= 2 {
                    if pathLit, ok := call.Args[0].(*ast.BasicLit); ok {
                        route := RouteInfo{
                            Method:       method,
                            Path:         pathLit.Value,
                            File:          filename,
                            RegisterType: "static",
                        }
                        // 解析 handler 引用、入参/出参结构体
                        route = enrichRoute(route, call.Args[1], fset)
                        routes = append(routes, route)
                    } else {
                        // 动态路由:参数不是字面量
                        routes = append(routes, RouteInfo{
                            Method:       method,
                            File:          filename,
                            RegisterType: "dynamic",
                        })
                    }
                }
                return true
            })
        }
    }

    return routes, nil
}

func isHTTPMethod(name string) bool {
    switch name {
    case "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "Any":
        return true
    }
    return false
}

func enrichRoute(route RouteInfo, handlerExpr ast.Expr, fset *token.FileSet) RouteInfo {
    // 解析 handler 函数引用,定位到函数声明
    // 读取函数参数,找 *gin.Context 之后的入参结构体
    // 读取函数返回值,找出参结构体
    // ...(实现略)
    return route
}

func main() {
    routes, err := ExtractRoutes("./internal/handler", "./internal/router")
    if err != nil {
        panic(err)
    }
    json.NewEncoder(os.Stdout).Encode(routes)
}

附:CI 校验脚本片段(必备小节检查)

// scripts/check-api-doc-sections.js
const fs = require('fs');
const path = require('path');

const REQUIRED_SECTIONS = [
  '功能描述',
  '请求参数',
  '响应字段',
  '请求示例',
  '成功响应',
  '失败响应',
  '错误码',
];

function checkFile(filePath) {
  const content = fs.readFileSync(filePath, 'utf8');
  const errors = [];

  // 按 ### 切分接口段
  const sections = content.split(/^### /m).slice(1);

  for (const section of sections) {
    const title = section.split('\n')[0];
    for (const required of REQUIRED_SECTIONS) {
      if (!section.includes(`**${required}**`) && !section.includes(`### ${required}`)) {
        errors.push(`${title}: 缺少必备小节 "${required}"`);
      }
    }
  }

  return errors;
}

function walk(dir) {
  const results = [];
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      results.push(...walk(full));
    } else if (entry.name.endsWith('.md')) {
      results.push(full);
    }
  }
  return results;
}

const files = walk(process.argv[2] || 'docs/api/');
let hasError = false;

for (const file of files) {
  const errors = checkFile(file);
  if (errors.length > 0) {
    hasError = true;
    console.error(`❌ ${file}:`);
    errors.forEach(e => console.error(`   - ${e}`));
  } else {
    console.log(`✅ ${file}`);
  }
}

process.exit(hasError ? 1 : 0);

附:错误码跨层提取正则

# 错误码提取正则(Go)
errors\.New\("(?<code>[0-9]+):(?<msg>[^"]+)"\)
fmt\.Errorf\("(?<code>[0-9]+):(?<msg>[^"]+):[^"]*"\)

# 错误码提取正则(Java)
throw new BusinessException\((?<code>[0-9]+),\s*"(?<msg>[^"]+)"\)
return Result\.fail\((?<code>[0-9]+),\s*"(?<msg>[^"]+)"\)

# 错误码提取正则(Python)
raise BizError\((?<code>[0-9]+),\s*"(?<msg>[^"]+)"\)
return \{.*"code":\s*(?<code>[0-9]+).*"msg":\s*"(?<msg>[^"]+)"\}

附:业务上下文清单模板(每个项目维护一份)

# {{ project_name }} 业务上下文清单

## 字段单位约定
- 金额:分(int64),如 19900 表示 199.00 元
- 时间戳:unix 毫秒(int64),如 1693526400000
- 坐标:GCJ02,单位为度,6 位小数
- 时长:秒(int64)

## ID 格式约定
- 订单号:OD + yyyyMMdd + 10 位流水
- 用户 ID:snowflake int64
- SKU 编号:SKU + 6 位数字
- 手机号:E.164 格式,+86 前缀

## 枚举值
- 订单状态:1=待支付、2=已支付、3=履约中、4=已完成、5=已取消
- 支付方式:1=微信、2=支付宝、3=银行卡、4=余额
- 履约状态:1=待发货、2=已发货、3=已签收、4=已退货

## 业务约束
- 单订单最多 100 个 SKU
- 单次批量创建最多 50 个订单
- 金额上限:单订单 100 万元(100000000 分)
- 软删除字段:is_deleted(bool)

附:动态路由探测正则

# 动态路由探测(Go reflect 模式)
reflect\.ValueOf\([^)]+\)\.MethodByName\(
r\.Handle\([^,]+,\s*[a-zA-Z_]+\)  # r.Handle(method, path) 但 path 是变量
r\.Any\([^,]+,\s*[a-zA-Z_]+\)

# 动态路由探测(Java 反射模式)
getMethod\(
invoke\(

附:流水线一键执行脚本

#!/bin/bash
# scripts/run-pipeline.sh

set -e

REPO=$1
BRANCH=${2:-main}
SERVICE=${3:-all}

echo "=== API 文档自动化流水线 ==="
echo "Repo: $REPO"
echo "Branch: $BRANCH"
echo "Service: $SERVICE"

# 第 0 步:接口元数据提取
echo ""
echo "[Step 0] 接口元数据提取..."
claude-code run --prompt prompts/0-extract-routes.md \
  --var repo=$REPO --var branch=$BRANCH

# 第 1 步:接口参数推断(并行)
echo ""
echo "[Step 1] 接口参数推断..."
for service_dir in artifacts/*/; do
  service_name=$(basename $service_dir)
  if [ "$SERVICE" != "all" ] && [ "$SERVICE" != "$service_name" ]; then
    continue
  fi
  claude-code run --prompt prompts/1-infer-params.md \
    --var service=$service_name &
done
wait

# 第 2 步:示例生成(并行)
echo ""
echo "[Step 2] 示例生成..."
for service_dir in artifacts/*/; do
  service_name=$(basename $service_dir)
  if [ "$SERVICE" != "all" ] && [ "$SERVICE" != "$service_name" ]; then
    continue
  fi
  claude-code run --prompt prompts/2-generate-examples.md \
    --var service=$service_name &
done
wait

# 第 3 步:文档组装
echo ""
echo "[Step 3] 文档组装..."
claude-code run --prompt prompts/3-assemble-docs.md

# 第 4 步:CI 校验
echo ""
echo "[Step 4] CI 校验..."
npx markdownlint-cli2 'docs/api/**/*.md'
node scripts/check-api-doc-sections.js docs/api/
node scripts/check-api-doc-examples.js docs/api/

echo ""
echo "=== 流水线完成 ==="
echo "产物:docs/api/v2.4/order-fulfill-api.md"
echo "下一步:提 PR + 人工审核"

附:人工审核记录模板

# 人工审核记录

## 基本信息
- 审核人:{{ name }}
- 审核范围:{{ service }} 服务,共 {{ n }} 个接口
- 审核时间:{{ timestamp }}
- 审核耗时:{{ duration }}

## 修正明细

| 接口 | 字段 | 修正类型 | 原值 | 修正后 | 原因 |
|---|---|---|---|---|---|
| POST /api/v2/order/create | createTime | 单位修正 | 时间戳(秒) | 时间戳(毫秒) | 代码中 time.Unix(0, x*1e6) |
| POST /api/v2/order/create | items | 隐式契约 | - | 单次最多 100 个 SKU | ratelimit.global 控制 |
| ... | ... | ... | ... | ... | ... |

## 统计
- 未决问题清零:{{ n1 }} 处
- 示例值修正:{{ n2 }} 处
- 隐式契约补充:{{ n3 }} 处
- 字段语义修正:{{ n4 }} 处
- 合计:{{ n1 + n2 + n3 + n4 }} 处

## 遗留问题
- [ ] {{ 问题 1 }}
- [ ] {{ 问题 2 }}

附:Prompt 工程经验总结

在整个流程中,我们对 Prompt 工程的几条核心经验:

  1. "硬约束"优于"软建议":写"必须基于证据链,证据不足标注待确认",比写"请尽量准确"效果好 10 倍
  2. "自检清单"是质量保证:在 Prompt 末尾加"[ ] 字段语义必须有证据链"这样的自检项,AI 会主动回溯检查
  3. "业务上下文"必须显式注入:AI 读不到内部 wiki,所有项目特有约定必须写在 Prompt 里
  4. "禁止项"比"鼓励项"更有效:写"禁止根据字段名自由发挥",比写"请谨慎推断"效果好
  5. "输出格式"必须严格:给 AI 一个 Markdown 模板,要求"严格遵循",比让它自由发挥格式好得多
  6. "边界声明"很重要:明确告诉 AI "AST 扫描可能漏掉动态路由",比让它假装无所不能更可靠

附:项目级 Prompt 模板版本管理

我们维护一份项目级 Prompt 模板仓库,结构如下:

prompt-templates/
├── api-doc/
│   ├── v1/                      # 初版,自由发挥
│   ├── v2/                      # 加硬约束 + 自检清单
│   ├── v3/                      # 加业务上下文 + 边界声明
│   └── v4/                      # 当前版本,加跨层错误码提取
├── code-review/
├── test-generation/
└── refactor/

每次踩坑后,把对应的修正加到 Prompt 模板的新版本里,旧版本保留作为"踩坑记录"。这样团队新成员可以直接用最新版,不用重新踩一遍坑。

附:常见问题 FAQ

Q1:为什么不用 Swagger / OpenAPI 自动生成?

A:Swagger 注释需要人工写在代码里,我们的代码里完全没有。让 AI 补 Swagger 注释再生成文档,比直接生成 Markdown 文档多一步,且 Swagger 注释的维护成本和直接写文档差不多。我们的方案是"AI 直接生成 Markdown 文档 + 持续 re-gen",跳过 Swagger 中间层。

Q2:AI 生成的文档怎么保证和代码同步?

A:通过 CI 触发自动 re-gen。每次 main 分支 push,CI 跑完整流水线,提 PR。如果文档有变化,PR 里能 diff 出来,人审 10 分钟合并。如果文档没变化,PR 自动关闭。这样文档和代码的漂移最多在一个 commit 周期内。

Q3:自定义 code-analysis MCP 开发成本高吗?

A:Go + gin 的 AST 提取核心代码约 300 行,2 人天开发完成。其他框架(echo / chi / Spring Boot / FastAPI)适配时间 1-2 人天。这是一次性投入,后续每个项目复用只需 4 小时适配。

Q4:AI 生成的文档质量如何保证?

A:三层保证——

  1. Prompt 层:硬约束 + 自检清单 + 边界声明
  2. CI 层:markdownlint + 必备小节校验 + 示例 JSON 解析校验
  3. 人工层:未决问题清零 + 示例业务正确性抽样 + 隐式契约补充

Q5:这套方案适用于非 Go 项目吗?

A:核心流程(AST 提取 → 参数推断 → 示例生成 → 组装 → 人审)通用,但 AST 提取逻辑需要按语言适配。Java 用 JavaParser,Python 用 ast 模块,Node.js 用 @babel/parser。每种语言 1-2 人天适配。

Q6:Claude Code 的 token 消耗大吗?

A:47 个接口、8 万行代码,整个流水线约 2000 万 token,按 Claude Code 当前定价约 ¥XXX。比 2 周人力成本低一个数量级。如果用 GPT-4 等其他模型,成本类似。

Q7:如果代码本身有 bug,AI 会把 bug 写进文档吗?

A:会。AI 生成的是"代码现状"的文档,不是"代码应该是什么"的文档。如果代码有 bug,文档会如实反映(甚至会在"未决问题"里暴露)。这其实是好事——文档促使人发现代码 bug。

Q8:动态路由怎么处理?

A:AST 扫描标记为"动态路由,待人工补充",列出相关代码文件供人参考。我们项目里 47 个接口中有 3 个动态路由,每个 5 分钟人工补,合计 15 分钟。完全自动化动态路由提取难度太大(需要执行代码),收益不高。

Q9:文档生成后怎么发布?

A:我们用 VitePress 搭建内部文档站,Markdown 直接作为 VitePress 内容源。PR 合并后 CI 自动部署到文档站,分钟级生效。也可以用 MkDocs / Docusaurus / GitBook,选团队熟悉的。

Q10:这套方案对团队规模有要求吗?

A:单人也跑得通——4 小时出文档,1 小时人审,1 人 5 小时完成。团队规模大的好处是可以并行审核(按服务拆分),但单人顺序审也只需 2 小时。核心前置投入是自定义 MCP 开发(2 人天),之后每个项目复用成本极低。

Q11:如何处理接口版本兼容(v1 / v2 / v3 并存)?

A:在 AST 提取阶段,路径里的版本号(/api/v1/、/api/v2/)作为分组维度。每个版本独立生成一份文档,避免字段语义混淆。如果 v1 和 v2 的接口字段有差异,在文档头部的"版本变更说明"里列出差异点。我们的项目里 v1 已废弃但代码还在,AI 会同时生成 v1 文档并标注 [deprecated],便于历史接入方参考。

Q12:如何处理接口的废弃标记?

A:在 AST 提取时,扫描 handler 函数上方的注释,如果包含 // Deprecated: 或 // @deprecated,自动在文档里加"⚠️ 已废弃"标签,并列出废弃原因和替代接口。这样合作方一眼能看到哪些接口不要再用。

Q13:如何处理接口的鉴权方式差异?

A:在"中间件影响"小节里明确写出每个接口的鉴权方式(JWT / API Key / OAuth2 / 无需鉴权)。如果同一接口支持多种鉴权方式,列出所有支持的方案,并说明优先级。我们项目里有 3 个接口是"内部调用免鉴权 + 外部调用需 JWT"的双模式,在文档里单独标注。

Q14:如何处理长字段名截断问题?

A:Markdown 表格在字段路径过长时会撑爆布局。我们在 CI 校验脚本里加了一条规则:字段路径超过 50 字符的,在表格里用缩写(如 items[].promotions[].rule.conditions[].minAmount → items[].promotions[].rule.conditions[].minAmount),完整路径在表格下方的"字段路径说明"小节列出。VitePress 支持 Markdown 表格横向滚动,但移动端体验差,缩写 + 说明是折中方案。

Q15:如何处理接口的幂等性说明?

A:在"功能描述"小节末尾,用 [幂等] 或 [非幂等] 标签明确标注。幂等性判断基于:HTTP 方法(GET / PUT / DELETE 默认幂等,POST 默认非幂等)+ handler 代码中是否有幂等键处理逻辑(如 if exists(req.OrderNo) { return existing })。如果代码中有幂等键处理,在文档里说明幂等键字段名和有效期。我们项目里 order.v2.create 用 orderNo 作为幂等键,24 小时内重复创建返回原订单,这个信息对合作方至关重要。


附:版本演进路线图

我们规划了这套流水线的后续迭代方向:

版本计划能力目标
v1.0(当前)Go + gin 全量文档生成已完成
v1.1增加废弃标记 + 鉴权方式说明提升 API 治理
v1.2支持 Go + echo / chi扩大 Go 生态覆盖
v2.0支持 Java + Spring Boot进入 Java 生态
v2.1支持 Python + FastAPI / Flask进入 Python 生态
v3.0自动生成 Postman collection一键导入测试工具
v3.1自动生成 SDK 代码(Go / Java / Python)降低合作方接入成本
v4.0接口变更自动通知(diff 推送钉钉 / 飞书)主动告知合作方 breaking change

v3.0 自动生成 Postman collection 的价值:合作方拿到的不仅是 Markdown 文档,还有一份可以直接导入 Postman 的 collection 文件,点击就能发请求测试。这把"看文档理解接口 → 手写请求测试"的流程压缩到"导入 collection → 一键发送"。

v4.0 接口变更通知 的价值:每次文档 re-gen 后,diff 出有哪些接口的字段、参数、错误码发生了变化,如果是 breaking change(字段删除、类型变更、必填项新增),自动推送给所有接入方团队。避免"代码改了但合作方不知道"的协作事故。


附:踩坑日志完整记录

以下是整个 4 小时流程中,所有踩过的坑的完整时间线记录,供后续团队参考:

2026-09-01 09:00  流水线启动
09:15  [坑 1] AST 扫描漏掉 3 个动态路由,MCP 加动态路由探测器
09:30  [坑 1 解决] 人工补 3 个动态路由,每个 5 分钟
09:45  [坑 2] int64 字段时间戳单位混淆(秒 vs 毫秒)
10:00  [坑 2 解决] Prompt 加"字段单位强校验"规则
10:15  [坑 3] 嵌套结构循环引用导致解析截断
10:30  [坑 3 解决] MCP 加 visited map,Prompt 加循环处理规则
10:45  [坑 4] 错误码只提取到 18 个(漏 service 层)
11:00  [坑 4 解决] 加跨层提取正则,错误码达 42 个
11:15  [坑 5] 示例金额单位用元(199.00)而非分(19900)
11:30  [坑 5 解决] 业务上下文清单注入 Prompt
11:45  [坑 6] 中间件影响未在文档体现
12:00  [坑 6 解决] Prompt 加"中间件影响说明"小节
12:15  [坑 7] 并行任务 Markdown 格式不统一
12:30  [坑 7 解决] CI 加 markdownlint + 必备小节校验
12:45  [坑 8] 文档 commit hash 与 main 漂移
13:00  [坑 8 解决] 流水线改为一键触发 + 每周定时全量重跑
13:00  流水线完成,进入人工审核
14:00  人工审核完成,56 处修正
14:15  PR merge,文档发布到内部文档站

附:与其他 AI 工具的对比

我们评估了 3 种方案后选择了 Claude Code + MCP:

方案优势劣势适用场景
Claude Code + 自定义 MCP(选中)长上下文、可并行、MCP 扩展灵活需要自研 MCP大型项目、需要深度定制
Cursor + GitHub CopilotIDE 内交互、上手快上下文有限、不支持自定义 MCP中小型项目、单人使用
ChatGPT + 手动喂代码零门槛、对话式上下文频繁丢失、无法并行接口数 < 10 的小项目
Swagger 自动生成(传统方案)标准化、生态成熟需要代码里写注释、维护成本高新项目、规范成熟团队

为什么不用 Swagger:我们的代码是 7 年祖传,没有任何 Swagger 注释。让 AI 补 Swagger 注释再生成文档,比直接生成 Markdown 多一步,且 Swagger 注释的维护成本(每次改接口要同步改注释)和直接写文档差不多。我们的方案是"AI 直接生成 Markdown + 持续 re-gen",跳过 Swagger 中间层。

附:内部推广反馈

这套流程在团队内推广 2 周后,收集到的反馈:

  • 正面:"以前对接一个新服务要 3 天看代码,现在 1 天看文档就行"——合作方对接同学
  • 正面:"文档自动 re-gen 后,再也不用担心文档过期了"——后端开发同学
  • 正面:"业务上下文清单整理出来后,新人 onboarding 时间从 2 周缩到 3 天"——团队负责人
  • 中性:"自定义 MCP 开发需要 2 人天投入,小团队不一定愿意投"——其他团队反馈
  • 负面:"AI 生成的文档偶尔有幻觉(字段语义推断错),需要仔细审核"——审核同学
  • 改进建议:"希望能在文档里加'此字段由 AI 推断,置信度 X%'的标记,让人审时重点关注"——审核同学

针对"AI 置信度标记"的建议,我们正在 v1.1 版本中加入:每个字段的"语义说明"列后面加一个 [置信度: 高/中/低] 标记,高 = 有明确证据链、中 = 有间接证据、低 = 主要靠字段名推断。人审时只需重点看"低"置信度的字段。

附:业务上下文清单的演进

业务上下文清单不是一次性写好的,而是随着流水线运行不断完善的。我们的清单经历了 4 个版本:

  • v1(第 1 次跑流水线):只有金额单位、时间戳单位 2 条约定,结果 AI 在示例生成时把手机号、坐标、订单号格式全猜错了
  • v2(第 2 次跑):补全 ID 格式约定(订单号、用户 ID、SKU 编号),手机号格式,坐标系,示例正确率从 60% 提升到 85%
  • v3(第 3 次跑):补全枚举值速查表(订单状态、支付方式、履约状态),错误码语义表,AI 在"功能描述"小节的表达准确率从 70% 提升到 95%
  • v4(当前):补全业务约束(单订单最多 100 个 SKU、金额上限、软删除字段语义),AI 在"中间件影响"和"业务约束"小节的覆盖率达 90%

经验:业务上下文清单是"活的文档",每跑一次流水线、每发现一处 AI 推断错误,就把对应的约定补进清单。清单越完善,AI 输出越准,人审越轻松。这是"AI 流水线的复利效应"。

附:业务上下文清单的维护规范

为了让清单不变成"另一份过期文档",我们制定了维护规范:

  1. 清单版本跟随代码版本:清单文件 business-context.md 放在仓库根目录,每次代码改了字段约定,PR 里必须同步改清单(CI 强制校验)
  2. 清单变更触发文档 re-gen:清单文件变化时,CI 自动触发完整文档流水线,确保文档和清单同步
  3. 清单 review 责任人:每个服务的清单由 service owner 维护,PR 必须有 service owner approve
  4. 清单格式校验:CI 脚本校验清单的必备小节(字段单位、ID 格式、枚举值、业务约束)是否齐全

附:清单格式校验脚本片段

// scripts/check-business-context.js
const fs = require('fs');

const REQUIRED_SECTIONS = [
  '字段单位约定',
  'ID 格式约定',
  '枚举值',
  '业务约束',
];

function checkFile(filePath) {
  if (!fs.existsSync(filePath)) {
    console.error(`❌ 业务上下文清单不存在: ${filePath}`);
    return false;
  }

  const content = fs.readFileSync(filePath, 'utf8');
  const errors = [];

  for (const section of REQUIRED_SECTIONS) {
    if (!content.includes(`## ${section}`)) {
      errors.push(`缺少必备小节: ${section}`);
    }
  }

  // 校验枚举值小节是否有表格
  if (content.includes('## 枚举值')) {
    const enumSection = content.split('## 枚举值')[1].split('##')[0];
    if (!enumSection.includes('|') || !enumSection.includes('---|')) {
      errors.push('枚举值小节必须包含 Markdown 表格');
    }
  }

  if (errors.length > 0) {
    console.error(`❌ ${filePath}:`);
    errors.forEach(e => console.error(`   - ${e}`));
    return false;
  }

  console.log(`✅ ${filePath}`);
  return true;
}

const ok = checkFile('business-context.md');
process.exit(ok ? 0 : 1);

附:业务上下文清单的团队协作模板

为了让团队成员都能贡献清单内容,我们提供了一份协作模板:

# {{ project_name }} 业务上下文清单

> 维护人:{{ service_owner }}
> 最后更新:{{ date }}
> 变更需在本文件 PR 中同步修改,CI 会校验

## 字段单位约定

| 字段类型 | 单位 | 数据类型 | 示例 |
|---|---|---|---|
| 金额 | 分 | int64 | 19900 表示 199.00 元 |
| 时间戳 | unix 毫秒 | int64 | 1693526400000 |
| 坐标 | GCJ02 度 | float64 | 23.129100 |
| 时长 | 秒 | int64 | 3600 表示 1 小时 |

## ID 格式约定

| ID 类型 | 格式 | 长度 | 示例 |
|---|---|---|---|
| 订单号 | OD + yyyyMMdd + 10 位流水 | 22 | OD20260901000123456789 |
| 用户 ID | snowflake | int64 | 1234567890123456789 |
| SKU 编号 | SKU + 6 位数字 | 9 | SKU001234 |
| 手机号 | E.164 | 13 | +8613800138000 |

## 枚举值

### 订单状态(order_status)

| 值 | 含义 | 备注 |
|---|---|---|
| 1 | 待支付 | 创建后默认状态 |
| 2 | 已支付 | 支付回调成功后 |
| 3 | 履约中 | 已分配仓库开始拣货 |
| 4 | 已完成 | 用户签收 |
| 5 | 已取消 | 用户主动取消或超时 |

### 支付方式(payment_method)

| 值 | 含义 | 备注 |
|---|---|---|
| 1 | 微信 | - |
| 2 | 支付宝 | - |
| 3 | 银行卡 | - |
| 4 | 余额 | 仅老用户可用 |

## 业务约束

| 约束项 | 值 | 实现位置 |
|---|---|---|
| 单订单最多 SKU 数 | 100 | ratelimit.global 中间件 |
| 单次批量创建订单数 | 50 | BatchHandler.Create |
| 单订单金额上限 | 100 万元(100000000 分) | OrderService.Create |
| 软删除字段 | is_deleted (bool) | 所有 model 通用 |
| 订单超时自动取消 | 30 分钟 | 定时任务 order_timeout_job |

## 鉴权方式

| 鉴权方式 | 适用接口 | Header |
|---|---|---|
| JWT | 所有对外接口 | Authorization: Bearer ${token} |
| API Key | 内部服务调用 | X-API-Key: ${key} |
| 无需鉴权 | /api/v1/health, /api/v1/metrics | - |

## 限流策略

| 接口 | 限流维度 | 限流值 | 中间件 |
|---|---|---|---|
| 全部对外接口 | 单 IP | 10 QPS | ratelimit.global |
| order.v2.create | 单用户 | 5 QPS | ratelimit.user |
| order.batch.create | 单用户 | 1 QPS | ratelimit.batch |

附:流水线监控指标

为了持续优化流水线,我们采集了以下监控指标:

指标采集方式目标值当前值
流水线总耗时CI 日志< 5 小时4 小时
AST 路由提取覆盖率routes.json vs 线上路由100%94%(动态路由漏 6%)
错误码提取覆盖率文档错误码 vs 线上日志错误码100%95%
字段语义准确率人审修正数 / 总字段数> 95%91%
示例业务正确率人审抽样> 95%92%
文档 re-gen 频率CI 触发次数每周 >= 1每周 2.3 次
人工审核耗时审核记录< 2 小时1 小时
未决问题数文档"未决问题"小节趋势下降v1: 38, v4: 12

这些指标每跑一次流水线自动采集,存到 InfluxDB + Grafana 看板。指标下降时自动告警到飞书群,提示需要优化 Prompt 或 MCP。

附:流水线指标采集脚本

#!/bin/bash
# scripts/collect-metrics.sh

PIPELINE_START=$(date +%s)

# ... 流水线执行 ...

PIPELINE_END=$(date +%s)
DURATION=$((PIPELINE_END - PIPELINE_START))

# 采集路由覆盖率
TOTAL_ROUTES=$(curl -s http://api-gateway/internal/routes | jq length)
EXTRACTED_ROUTES=$(jq '.routes | length' artifacts/order/routes.json)
ROUTE_COVERAGE=$(echo "scale=2; $EXTRACTED_ROUTES * 100 / $TOTAL_ROUTES" | bc)

# 采集错误码覆盖率
DOC_ERROR_CODES=$(grep -c "| [0-9]\+ |" docs/api/v2.4/order-fulfill-api.md)
LOG_ERROR_CODES=$(curl -s http://log-service/api/error-codes | jq length)
ERROR_CODE_COVERAGE=$(echo "scale=2; $DOC_ERROR_CODES * 100 / $LOG_ERROR_CODES" | bc)

# 采集未决问题数
UNRESOLVED=$(grep -c "未知,待人工确认" docs/api/v2.4/order-fulfill-api.md)

# 写入 InfluxDB
curl -XPOST "http://influxdb:8086/write?db=api_doc_metrics" \
  --data-binary "pipeline duration=${DURATION},route_coverage=${ROUTE_COVERAGE},error_code_coverage=${ERROR_CODE_COVERAGE},unresolved=${UNRESOLVED}"

echo "Metrics collected: duration=${DURATION}s, route_coverage=${ROUTE_COVERAGE}%, error_code_coverage=${ERROR_CODE_COVERAGE}%, unresolved=${UNRESOLVED}"

最后更新:2026-09-01 作者:AI 工具学校 相关文档:

相关推荐

查看更多 →
案例开发

AI 辅助数据库零停机迁移:从 MySQL 到 PostgreSQL 只用 3 天

公司核心业务从 MySQL 5.7 迁移到 PostgreSQL 16,数据库有 47 张表、230 万行数据、86 个存储过程和 34 个触发器。手动迁移评估 3 周,最终用 Claude Code + PostgreSQL MCP + 自定义 Schema 转换 MCP,3 天完成全量迁移(Schema 转换 + 数据迁移 + 应用层适配 + 回归测试),停机窗口仅 12 分钟。本文复盘工具分工、分步 Prompt 模板、SQL 方言转换踩过的 23 个坑(AUTO_INCREMENT vs SERIAL、datetime 格式、ON DUPLICATE KEY UPDATE、GROUP_CONCAT、utf8mb4 编码等)、以及零停机迁移的灰度切换策略。

2026-09-10
#AI实战#数据库迁移
案例开发

用 OpenAI Agents API + MCP 把 Codex Harness 改成可编排的 Agent 服务:中型 SaaS 团队实战

OpenAI 在 2026 年 9 月把内部跑 Codex 的 harness 改成了对外可用的 Agents API:原生支持 MCP server 注册、多 Agent 编排、handoff、guardrail、结构化输出。一个中型 SaaS 团队(23 名工程师)用这套 API + 自建的 6 个 MCP server,把内部 17 个零散的 AI 脚本统一成了一个可编排的 Agent 服务,每月节省 480 工时。本文复盘架构设计(Agent 拓扑、handoff 流程、guardrail 配置)、Prompt 模板(主 Agent + 4 个子 Agent)、MCP 集成(自建 6 个 MCP 的分工)、3 个真实业务场景(客户工单自动分流 / 代码评审流水线 / 数据报告自动化)、以及踩过的 11 个坑(handoff 死循环、guardrail 误杀、MCP 超时、token 计费翻倍等)。

2026-09-11
#AI实战#OpenAI
案例开发

AI 日志异常检测:从日均百万条日志中 30 分钟定位 3 个隐藏 Bug

微服务集群每天产生约 120 万条日志(ELK 栈),但 99.9% 是正常日志,真正的异常淹没在海量数据中。团队 2 名 SRE 花一周才能完成一次全量日志审计。我们用 GPT-5 做异常模式识别和语义分类 + 日志分析 MCP 做自动日志检索和聚合 + Slack MCP 做实时通知,构建了端到端的日志异常检测流水线。30 分钟内从 120 万条日志中定位了 3 个隐藏 Bug(一个内存泄漏、一个竞态条件、一个错误的重试逻辑),准确率 91%,误报率仅 4%。本文复盘工具分工、分步 Prompt 模板、检测准确率数据和踩过的坑。

2026-09-09
#AI实战#日志检测
案例开发

AI 监控告警智能分诊:日均 3000 条告警从 25 分钟降到 3 分钟响应

公司的微服务架构每天产生约 3000 条告警(Prometheus + Grafana),值班 SRE 平均响应时间 25 分钟,其中 70% 是误报或低优先级。我们用 Claude 做告警分类和根因推断 + 日志分析 MCP 做自动日志关联 + GitHub MCP 做自动创建 Issue + Slack MCP 做告警通知路由,构建了端到端的告警分诊流水线。误报过滤准确率 94%,真正需要人工处理的告警平均响应时间降到 3 分钟,值班人员工作量减少 68%。本文复盘工具分工、分诊 Prompt 模板、根因推断准确率数据和踩过的坑。

2026-09-02
#AI实战#告警分诊