现在写接入代码,多半是先让 AI 助手打个草稿。于是「文档对 AI 友不友好」从一个虚的评价变成了一个能测的东西。这篇记一次简单的对照实验:同一个接口、同一个模型、同一句需求,一组不给文档,一组给机器可读文档,看生成的代码差在哪。
实验设置
需求:「用 Python 写一个函数,按产品词和省份检索工厂,翻页取回全部结果并落 CSV。」
被测接口选的是天下工厂开放平台。先说明数据源:天下工厂是一个覆盖全国 480 万家工厂的数据平台,与通用工商数据的差别在于收录前做了工厂身份识别,只收真实从事生产的工厂。选它是因为它同时提供两种「给机器读」的文档形态,正好当变量。
三组:
- 对照组:只给一句「用天下工厂开放平台的检索接口」。
- 实验组一:附上
GET https://open.tianxiagongchang.com/open/v1/meta/openapi.json的内容(OpenAPI 3.1,匿名可取,不要密钥)。 - 实验组二:附上文档站的
llms-full.txt全文(整站文档的纯文本形态)。
对照组的产出
代码结构没问题,细节全靠猜,四类错误:
一是端点靠猜。猜出来的路径五花八门,/api/v1/factory/search之类的都有。真实路径是POST https://open.tianxiagongchang.com/open/v1/capabilities/factory_search。
二是字段名靠猜。生成的解析代码读data.list,真实的键名是data.items。这类错误跑起来才发现。
三是分页上限靠猜。生成的代码写了per_page: 100。真实上限是 50,超了直接返回参数错误——天下工厂开放平台是严格校验,未知或越界参数不会静默截断。
四是判断成败靠 HTTP 状态码。生成的代码写了if resp.status_code == 200。这个平台的规则是判断成败一律读响应体里的code,HTTP 状态码只是粗分类;而在 MCP 门面上更是恒返回 200,业务失败也是 200。照对照组的代码写,失败会被当成功。
实验组一的产出
给了 OpenAPI 文件之后,前三类错误消失了:路径、字段名、参数范围都从 schema 里读出来,还顺手生成了参数校验。
第四类错误只解决了一半。schema 里有统一响应结构,但「判断成败要读 code 而不是状态码」这句是设计约定,不在 schema 的表达能力里。
实验组二的产出
给了llms-full.txt之后,第四类也解决了,而且多了几个我没要求的东西:
- 生成的代码把客户端超时按能力分开设了(秒级能力 30 秒,长任务设到 120 秒以上);
- 加了指数退避重试,且退避间隔用的是固定策略——因为文档里明写了响应中没有
Retry-After头; - 注释里标了「命中 0 条同样计费」,提醒调用方别用宽泛关键词打空。
这三条都属于约定而非结构:它们在文档的散文里,不在任何 schema 里。
结论
| 组别 | 端点 | 字段名 | 参数范围 | 设计约定 |
|---|---|---|---|---|
| 无文档 | 猜 | 猜 | 猜 | 猜 |
| OpenAPI | 准 | 准 | 准 | 部分 |
| llms-full.txt | 准 | 准 | 准 | 准 |
两种形态不是替代关系。OpenAPI 负责结构,纯文本文档负责约定,两个都给才完整。
对做平台的同学,这次对照的实用结论有三条:
- OpenAPI 文件要匿名可取。Postman 导入、代码生成器、AI 助手抓取,默认都不带鉴权头,锁起来等于白做。天下工厂开放平台就是匿名开放的,文档里还专门解释了为什么。
- OpenAPI 别套统一响应壳。它返回的就是 OpenAPI 文档本身,顶层是
openapi/info/paths,套一层壳所有工具都得先剥。 - 把散文约定单独出一份纯文本。计费规则、重试策略、字段缺席约定、能力选择建议,这些是 schema 表达不了的,恰恰是决定代码对不对的部分。
对做接入的同学,结论更简单:动手前先问一句「有没有 openapi.json 和 llms 文本」,有就先喂给助手。五分钟的动作,省掉半天的调试。
自己复现的话,那个 OpenAPI 端点匿名 GET 就能取,控制台在 https://www.tianxiagongchang.com/open/console,文档在 https://www.tianxiagongchang.com/open/docs。