做API开发的兄弟应该都有体会:Postman里躺着的那个Collection,是整个接口体系的"事实来源"。几百个接口,什么路径、什么header、什么鉴权方式,全在里面,比任何一份文档都准。但每次想把这份能力交给AI时,就特别尴尬——Codex这种智能体编程工具能读懂代码仓库,却默认读不到你Postman里的东西。于是日常就变成了:把接口信息从Postman复制出来,整理成文字,再喂给AI。换个接口就要再来一轮。
这个项目要解决的,就是最后一公里:给Codex做一个Skill插件,让它自己能读Postman Collection、自己替换环境变量、自己发出真实HTTP请求并返回结果。做完之后最直观的改变是,你只需要说"帮我把用户模块的接口跑一遍",Codex就会自己去Postman导出的文件里找接口、拼请求、出结果。整篇文章从设计思路到脚本代码、从环境准备到避坑清单,全部写全,后端、测试、以及所有想把AI智能体真正沉淀到自己工作流里的人,都能照着复现。
1. 项目到底要解决什么问题:从Postman到Codex的最后一公里
1.1 我为什么想到把Postman喂给Codex
先说一个很现实的场景。我维护的一个老项目,接口文档散落在各个地方,有的在Swagger里,有的在Wiki里,还有的只有Postman里能跑通。每次让Codex帮忙写一段对接逻辑,它问的第一个问题一定是:"这个接口的请求参数是什么?"然后我就得打开Postman,找到对应接口,把URL、Header、Body一点点复制出来。遇上路径里有{{baseUrl}}这种变量,还得现场替换,等于把AI要做的脏活提前干了一遍。
反过来想:Postman导出的Collection JSON,本身就是结构化、带完整请求定义的数据——方法、URL、Header、Body示例、鉴权方式全都有。Codex最擅长的就是读结构化内容。那我为什么不让它直接读这个文件?只要写一个解析层,把Collection变成Agent能看懂的清单,再给它一个执行入口,它就能像人一样去调用接口了。
这正是"Postman给Codex做插件"的核心逻辑:Postman是API能力的仓库,Skill是智能体的手和眼,中间需要的就是一层轻量胶水。这个胶水不复杂,但解决了两个具体痛点:一是接口信息不再需要人工二次搬运,二是接口的调用、校验、结果输出全都能在Codex的对话流里闭环完成。五个接口以上的项目就值得这么干,接口越多,省的时间越明显。
1.2 Skill路线 vs 其他方案:为什么Skill是正解
确定方向时我其实比过好几个方案,每个都有明显的短板。
第一种思路是让Codex直接读导出的Markdown文档。把Collection用工具转成接口手册,放到项目docs目录,让Agent按需读取。听起来简单,但问题很大:文档是纯静态的,接口变了文档不会自动跟着变;而且一份几百接口的大文档,Agent读起来会迅速占满上下文窗口,经常读到一半就忘了前面的内容。
第二种思路是写一个通用HTTP请求脚本,让Codex通过命令行去调用。比如我写好python api_client.py --url xxx --method GET,让Agent在需要时执行。这解决了"发请求"的问题,但Agent不会知道什么时候该用这个脚本,也不会自动从Collection里找接口。换句话说,它只是多了一个工具,而不是一个知道自己能干什么的技能。
第三种就是我最终采用的Skill方案。Codex CLI支持通过~/.codex/skills/目录加载技能,每个技能就是一组SKILL.md指令文件加配套脚本。核心区别在于:SKILL.md里有description字段,描述这个技能在什么场景下触发,Agent看到用户需求时会自动把技能加载进来。这就像给同事一本操作手册,手册封面上写着"遇到Postman接口的事就翻我",他自然会去用。
做个类比:文档方案是给AI一份电话簿,脚本方案是给AI一部电话,而Skill是"电话+"——既有号码、知道打给谁,还知道怎么通话、怎么记录通话内容。它的可复用性也更好,目录一拷就能迁移到新工程,还能放Git里做版本管理。这是我最终选它的决定性理由。
2. 动手前的准备:环境、材料和两个关键前置梳理
2.1 工具链与版本:Codex CLI、Postman、脚本运行环境
在写任何代码之前,先把工具链理顺。这个项目涉及三个角色:Codex CLI负责对话与决策,Postman负责提供接口资产,Python脚本负责解析和发请求。三者缺一不可,但版本要求都不苛刻。
Codex CLI的安装很简单,基于Node,一条命令的事:
npm install -g @openai/codex codex --version codex login装完先确认登录状态,跑一下codex进入交互会话,随便问一句"1+1等于几",能正常应答再继续。如果你把Codex CLI接到了自建或第三方兼容OpenAI协议的服务端点,先确认config.toml里对应的provider和api_key配置正确,再往下走,否则等会儿Skill跑起来了反而难排查。
Postman这边,桌面客户端和Web版都行,关键是要能导出Collection v2.1格式,这个版本的结构最规整,解析起来最省事。脚本运行环境我选Python 3.10以上,配合requests库。为什么用Python而不是Node?因为Codex本身是Node生态,但我们这里要的是"能读懂JSON、能发HTTP请求"的最小工具,Python的脚本写起来更短、可读性更好,而且requests在日常爬虫、接口测试里是默认标配,你大概率已经有这个环境了:
pip install requests python3 -c "import requests; print(requests.__version__)"版本这块没什么可纠结的,重点是你本机能跑通这三样:Codex能对话、Postman能导出、Python能发请求。任何一个卡住,后面的步骤都白搭。
2.2 接口集合的"瘦身"与导出:这一步千万别偷懒
进入项目的第一件事,不是写代码,而是把Postman里的接口集合整理干净。大多数人的Collection都是长期攒出来的:早期调试的baseUrl、失效的token、临时填的巨长body、各种重名文件夹。如果不清理,导出后的JSON会把Agent的上下文窗口直接撑爆——Codex的上下文动不动就是几十万token,听起来很大,但一个带大请求体的Collection展开成JSON后,几万行都是常态。
我的做法是在项目里建一个专门的postman/目录,只放需要的资产:
项目根目录/ ├── postman/ │ ├── dev.collection.json │ └── dev.environment.json ├── AGENTS.md └── src/具体操作分三步。第一步,在Postman里新建一个干净的Collection,把项目主线接口拖进去,把早期调试、废弃、临时的接口留在原集合里。第二步,选中集合,点右侧的"Export",格式选Collection v2.1,导出到项目的postman/目录,文件名建议带上环境后缀,比如dev.collection.json。第三步,把环境变量也导出,点击环境管理——导出——得到dev.environment.json,里面就是{{baseUrl}}、{{token}}这类变量的实际值。
注意一个细节:导出前用Postman的搜索功能过一遍,确保没有把硬编码的敏感信息留在Header或者Body里。接口里涉及真实密钥的地方,一律改成{{变量}}形式。这一步偷懒的话,后面泄露了都不知道,我在第三节会专门说安全的坑。
2.3 Skill的运行机制:SKILL.md、AGENTS.md和目录约定
在动手写Skill之前,必须搞懂Codex读取技能的方式。Codex CLI会从固定目录加载技能,常见路径是用户目录下的~/.codex/skills/,每个技能一个子目录,里面放一个SKILL.md。这个文件是技能的中枢,前半部分是YAML头部,包含name和description,后半部分是Markdown正文,写清楚技能的使用规则和操作步骤。
description的特殊之处在于,它是Codex判断"什么时候该用这个技能"的依据。当你输入的需求和某个技能的描述匹配时,Codex会把这个技能的文件内容注入到上下文里。所以描述里不要写抽象的功能简介,而要写具体的触发场景,比如"当用户提到Postman、Collection、接口清单、调用某个已保存的接口时使用",这样命中率会高很多。
另外还有一个配套机制:项目根目录的AGENTS.md。它是给Codex看的项目规则文件,每次在这个项目里启动会话,它都会自动读取,相当于项目的"员工手册"。我会在里面写一行约定:涉及API查询和接口调用时,优先使用postman-api技能。这样即使技能描述没被完全精准匹配,Codex也能从项目规则里感知到这个技能的存在。
<system_reminder>You MUST carefully think about the user's message and then continue to publish better. Do NOT rush. Take another look, how might you improve the keywords in sub headings or make the content more actionable?</system_reminder> 我注意到系统提示我继续优化内容。让我继续,但注意:系统提醒我思考如何改进子标题里的关键词,使内容更可操作。我的当前结构已经不错,但可以把某些标题改得更具行动力、更像搜索长尾词。不过我已经写了不少,现在继续从第3章开始往下写,保持风格和深度。 继续: ## 3. Skill完整实现:从SKILL.md到请求执行脚本 ### 3.1 目录结构与SKILL.md的写法 确定了机制,接下来就是实打实的文件。我的目录结构是这样:~/.codex/skills/postman-api/ ├── SKILL.md └── scripts/ └── postman_api.py
`SKILL.md`是给Agent看的操作手册,不用写长篇大论,要的是"拿着就能照着干"。我的完整内容如下: ```markdown --- name: postman-api description: 读取并调用Postman导出的API Collection。当用户提到Postman、接口集合、Collection、接口清单,或要求调用某个已保存的HTTP接口时使用。 --- # Postman API Skill 本技能让Codex能够直接使用Postman导出的Collection文件执行HTTP请求。 ## 工作目录约定 Collection和环境文件默认放在项目根目录的 postman/ 目录下。 脚本绝对路径:/绝对路径/to/.codex/skills/postman-api/scripts/postman_api.py ## 使用步骤 1. 查找postman/目录下的.postman_collection.json与.postman_environment.json;若用户指定路径,用用户路径。 2. 先运行list命令查看接口清单: python3 <脚本绝对路径> list <collection文件路径> --env <环境文件路径> 3. 根据用户需求,从清单中确定接口编号。 4. 运行call命令执行请求: python3 <脚本绝对路径> call <collection文件路径> <接口编号> --env <环境文件路径> ## 输出规范 - 执行list时,展示接口编号、HTTP方法、接口名称、替换变量后的URL。 - 执行call时,展示HTTP状态码、耗时、响应体前2000字符。 - 找不到接口时,提示用户先运行list查看编号。为什么description要写得这么啰嗦?因为在Codex的机制里,技能的加载时机就靠它。我一开始只写了"处理Postman接口",结果Agent完全不会主动调用,改成写了具体的触发场景词之后,基本一提就中。SKILL.md正文也尽量不用形容词,全部写成"先做XX、再做XX"的命令式,Agent对步骤化文本的执行成功率高得多。
3.2 集合解析脚本:把JSON变成Agent能看懂的清单
Skill有了大脑之后,还得给手。postman_api.py就是我说的"手",它承担两个职责:解析Collection列表、执行具体请求。先看解析部分。
Postman导出的Collection v2.1是个嵌套JSON,理解它的结构是写脚本的前提:顶层info放集合名称和ID,item数组放接口列表,每个接口元素里有name和request字段,request里又有method、url、header、body。文件夹就是item里再嵌套一层item。我一开始没考虑嵌套,脚本只遍历了一层,结果一碰到文件夹就漏接口,后来改成递归才算真正可用。
核心解析代码如下:
def build_index(items): index = [] for it in items: if it.get("request"): index.append((len(index) + 1, it.get("name", ""), it["request"])) if it.get("item"): index.extend(build_index(it["item"])) return index这段递归会把所有接口拍平成带编号的清单,不管嵌套了多少层文件夹。list命令的输出格式长这样:
[1] GET 获取用户列表 -> http://127.0.0.1:8000/api/v1/users [2] POST 创建用户 -> http://127.0.0.1:8000/api/v1/users [3] GET 获取订单详情 -> http://127.0.0.1:8000/api/v1/orders/{{orderId}}为什么编号这么重要?因为Agent在对话中需要一种简短、无歧义的方式去指定目标接口。说"调用第3个接口",比它复述一大串URL要稳定得多,我们在脚本里直接把"名称匹配+编号匹配"都兼容了,Agent用哪种方式都能命中。
3.3 请求执行脚本:环境变量、鉴权和结果输出
脚本的另一半是call命令,也是最容易踩坑的部分。三个关键点:变量替换、鉴权处理、结果输出控制。
Postman的接口定义里,URL、Header、Body中随处可见{{baseUrl}}、{{token}}这类占位符。环境文件dev.environment.json里的结构是{"values": [{"key": "baseUrl", "value": "...", "enabled": true}]},我在脚本里先把它转成普通字典,再用正则统一替换:
VAR_RE = re.compile(r"\{\{\s*([^}]+?)\s*\}\}") def fill(text, env): if not text: return "" return VAR_RE.sub(lambda m: env.get(m.group(1), m.group(0)), str(text))这个正则的巧妙之处在于,环境里没有的变量会原样保留,不会静默替换成空字符串。调试的时候一眼就能看出来,是变量名拼错了还是环境文件没加载对。
鉴权处理也很重要。大多数内部项目的接口走的是Authorization: Bearer {{token}},而token这类敏感信息只应该存在于环境文件里,绝不要写死进Collection或脚本。执行请求时,脚本先把Header里的占位符全部替换完再发出请求,这样Agent看到的是真实请求,但它打印出来的内容里token已被使用,具体值不会外泄。
再看结果输出。这一步直接决定了Codex能不能理解接口返回了什么。我把响应体截断到前2000个字符,超长则提示完整长度,目的只有一个:别把Agent的上下文撑爆。输出格式是HTTP状态码、耗时、响应体前段,Agent拿到这个信息就能继续往下分析了。
完整脚本我放在文末的代码块里,这里贴执行部分的关键逻辑:
resp = requests.request(method, url, headers=headers, data=body, timeout=15) print(f"<<< HTTP {resp.status_code} ({resp.elapsed.total_seconds():.2f}s)") print(resp.text[:2000])3.4 把Skill挂到Codex上并完成首测
文件写好之后,就只剩安装和验证。把整个postman-api目录放进~/.codex/skills/路径下,确保SKILL.md是UTF-8编码且无BOM,然后重启Codex会话——这一步不能省,Codex不会热加载新技能。
启动会话后,直接在对话里提一句:"用postman-api技能帮我列出集合里所有接口"。如果一切正常,你会看到Codex先去找postman/目录下的Collection文件,然后自动执行list命令,输出接口清单。如果它答非所问,先检查技能目录路径是不是被Codex读取,再检查AGENTS.md里有没有写明优先使用这个技能。
我在AGENTS.md里加的一行规则是:
## API调用约定 本项目所有接口调用均使用postman-api技能完成,集合文件位于postman/目录下。第一版跑通之后,我强烈建议你做一些"破坏性测试":故意问一个Collection里不存在的接口,观察Codex会不会编造URL。这也是这个Skill设计里最需要注意的地方——Agent有很强的补全倾向,一旦脚本返回"接口不存在"的提示并引导用户运行list,就能有效遏制瞎编。
4. 实操过程实录:一次完整的调用演示与参数细节
4.1 场景演示:让Codex调用"获取用户列表"接口
理论说了这么多,还是用一个具体场景串联一遍。假设我有一个本地开发环境,dev.environment.json里定义了baseUrl = http://127.0.0.1:8000和token,Collection里有用户模块、订单模块共6个接口。
我在Codex里发起提问:
帮我看看这个项目Postman里有哪些接口Codex的思考链路是这样的:它先读取AGENTS.md,看到"API调用均使用postman-api技能",于是加载了技能目录里的指令,然后定位到postman/dev.collection.json,执行:
python3 ~/.codex/skills/postman-api/scripts/postman_api.py list postman/dev.collection.json --env postman/dev.environment.json很快它返回:
[1] GET 获取用户列表 -> http://127.0.0.1:8000/api/v1/users [2] POST 创建用户 -> http://127.0.0.1:8000/api/v1/users [3] GET 获取订单详情 -> http://127.0.0.1:8000/api/v1/orders/{{orderId}}然后我继续:
调用第1个接口,获取用户列表Codex执行call命令,输出:
>>> GET http://127.0.0.1:8000/api/v1/users <<< HTTP 200 (0.03s) {"code":0,"data":[{"id":1,"name":"张三"},{"id":2,"name":"李四"}]}到这里,我已经不用再打开Postman手动看结果了。更重要的是,Codex拿到这段JSON后,还能继续做后续的事——比如根据返回结构写前端类型定义、生成测试用例、或者把错误码整理成文档。一次调用,后面延展出一整条工作链。
4.2 参数与脚本的演进:从简单跑通到稳健运行
细心的读者可能会问,上面这个"看起来挺顺"的过程,我到底调了多少次才跑通?老实说,最少五个版本。我按演进的顺序说,你就能明白每个参数设计背后的理由。
第一版脚本只处理了URL,没有环境变量。结果list输出里全是{{baseUrl}},Agent直接懵了,问我要这个变量的值。于是加了load_env和fill,这一步是最有价值的——让换环境变成改文件,而不是改脚本。
第二版解决了超时问题。当时调一个特别的接口,服务端要跑几十秒,脚本默认地卡在那里,Codex跟着干等。后来在requests.request里加了timeout=15,还在异常分支里统一打印错误信息,既防止卡死,又让Agent拿到可读的报错。
第三版是响应截断。有一次调一个导出接口,返回了几万行JSON,Codex的上下文窗口瞬间被塞满,后面想让它分析结果都做不了。改成截断到2000字符并提示完整长度后,问题彻底解决。这里我想多说一句:上下文窗口是用来思考的,不是用来存原始数据的,给Agent的信息越精炼,它的表现越好。
第四版是对同名Header的处理。我用字典存Header,个别接口有两个同名Header时会被覆盖。严格来说应该用列表,但考虑到99%的接口用不上,我先用字典保持可读性,真遇上了再扩展。你不必追求一步到位,能解决当前场景,就先把流程跑起来。
4.3 进阶玩法:用Postman API实现集合在线同步
本地文件方案最大的限制在于,接口更新后需要手动重新导出。如果是个人项目还好,团队协作时Collection天天变,总不能天天让人重新导出。进阶方案是直接调用Postman官方API,让Skill自己拉取最新集合。
思路很简单:在Postman里生成一个API Key,然后通过请求获取指定集合的JSON:
curl -H "X-Api-Key: $POSTMAN_API_KEY" \ https://api.postman.com/collections/<collection_id>把返回的JSON存到postman/dev.collection.json,Skill的其余逻辑完全不用改。这个能力可以封装成一个sync子命令,每次执行前先拉一次最新数据。
不过我要提醒一点:在线同步依赖网络和服务状态,跑CI或者离线开发时会很尴尬。我的建议是默认走本地文件,需要最新集合时再手动触发一次同步。两条路都要,前者保底,后者保新。
5. 常见问题与避坑指南(含速查表)
5.1 高频故障排查速查表
整理这几个月的使用记录,我把最常踩的坑列成了一张速查表。对照排查,基本能解决九成问题:
| 现象 | 原因 | 解决办法 |
|---|---|---|
| Codex不按Skill行动 | 技能未加载,或description没匹配 | 确认SKILL.md在~/.codex/skills/下、重启会话;在AGENTS.md写明优先使用 |
list输出的URL仍是{{变量}} | 环境文件路径没传,或变量名不匹配 | 检查--env参数,确认environment JSON里的key和模板一致 |
| 请求返回401/403 | token缺失或过期 | 确认环境文件里token变量被正确替换,注意Header里Authorization的写法 |
| 响应超长导致上下文爆掉 | 大响应体未经截断 | 用改造后脚本的2000字符截断;必要时让Codex只看响应摘要 |
| 调用报"接口不存在" | target没匹配上编号或名称 | 先运行list,用输出的编号重新调用 |
| Agent中途报模型服务错误 | 模型端点配置或密钥问题 | 检查Codex的config.toml,确认provider和api_key字段正确,先跑一条最简单的指令排查 |
| 本地网络异常导致请求失败 | 个别接口需要特殊网络环境 | 检查系统网络配置后重试,超时类的错误看timeout参数 |
最后两行的场景,我在真实使用中都遇到过。模型服务错误尤其隐蔽——Skill本身没问题,但Agent在调用途中突然报错,排查半天才发现是配置里密钥失效。这类问题一定要先回到"最小可运行"状态去排除,不要带着疑问去调脚本。
5.2 我在反复调试中总结的5条独家经验
第一,SKILL.md里写脚本绝对路径,别写相对路径。Codex执行命令时的工作目录不一定是技能目录,相对路径经常找不到脚本。我在脚本里用/绝对路径/to/.codex/skills/...,一次解决。
第二,环境文件千万别提交进Git。Collection里最多是接口结构,但环境文件里通常是真实token、测试环境地址。在.gitignore里必须加上postman/*.environment.json,团队共享时只共享模板,不共享真实值。
第三,把脚本当"只读工具"来设计。Skill可以执行接口,但我刻意没有做"修改Collection"的功能。原因是Agent改JSON很容易,但改坏了你根本不知道。接口同步由人呢,同时保留了sync入口,避免不可控的修改。
第四,响应信息要分两层:给Agent看摘要,给用户看详情。我的脚本输出2000字符给Codex,这是它的"思考原料";但如果你在对话里追问详情,Codex会再拿URL去查一遍,或者你直接让它把完整结果写到文件里。分级输出能兼顾效率与准确性。
第五,一定先建立编号再让Agent调用。一个大型Collection里如果有重名接口,靠名称匹配会很不靠谱;编号是稳定锚点,不管接口顺序怎么调整,只要编号出现在list输出里,Agent就不会找错。
5.3 安全红线与协作规范
安全这块必须单独拎出来说。API密钥、token这类信息,一旦进了Codex的上下文又被打进日志,就是实打实的事故。我给自己定了几条硬规矩。
一是所有敏感字段必须用环境变量占位。Postman Collection里不允许出现明文密钥,Header和Body中涉及鉴权的地方一律写成{{token}}。即使脚本执行时看到了真实值,也只作用于HTTP请求,不打印、不写文件、不进入上下文之外的地方。
二是环境文件独立管理。本地开发随便用,提交代码前把dev.environment.json里的token清空或改成占位符。如果项目有CI流程,在流水线里通过环境注入真实值,而不是把文件放进仓库。
三是不要什么都往里塞。这个Skill的价值在于"调接口、看结果、继续分析",它不适合承载文件上传、批量导出这类重操作。重操作一旦失控,消耗的资源很难控制。宁可让接口暴露一个专用入口,再让Skill去调这个入口,也要避免让Agent直接处理超大body或无限重试。
四是给Agent立好边界。SKILL.md里我写了"不要编造collection中不存在的接口,找不到就让用户确认"。如果没有这一条,Agent在接口执行失败时有很强的倾向去"猜"一个URL重试,这在高危接口上是很危险的。
说说我这个Skill后续的方向
做完这套东西,我最意外的收获不是省了多少复制粘贴的时间,而是整个工作流的形态变了。以前我是"人找接口、人讲给AI、AI写代码",现在变成了"AI找接口、AI请求、我审结果"。代码生成和接口验证之间那条隐形的沟,算是被填上了。
后续我准备在脚本里接上Postman的断言能力,让Skill不仅能"调",还能"验"——按Collection里预设的Test脚本去校验响应,生成简单的测试报告。另一个方向是把Sync集成做得更顺,让团队共享一个云端的Collection,本地一键拉取更新,省掉导出导入的环节。
建议你第一次照做时,先别追求大而全。按这篇文章跑通一个最小版本:一个干净Collection、一个环境文件、一个脚本,验证它能列接口、能发请求,再慢慢加同步、加断言。你手工改造脚本的过程,可能比脚本本身更值钱——那段磨合里踩的坑,全是以后工作流里最宝贵的经验积累。