做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进入交互会话随便问一句11等于几能正常应答再继续。如果你把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_reminderYou MUST carefully think about the users 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.pySKILL.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放集合名称和IDitem数组放接口列表每个接口元素里有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, headersheaders, databody, timeout15) 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和tokenCollection里有用户模块、订单模块共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里加了timeout15还在异常分支里统一打印错误信息既防止卡死又让Agent拿到可读的报错。第三版是响应截断。有一次调一个导出接口返回了几万行JSONCodex的上下文窗口瞬间被塞满后面想让它分析结果都做不了。改成截断到2000字符并提示完整长度后问题彻底解决。这里我想多说一句上下文窗口是用来思考的不是用来存原始数据的给Agent的信息越精炼它的表现越好。第四版是对同名Header的处理。我用字典存Header个别接口有两个同名Header时会被覆盖。严格来说应该用列表但考虑到99%的接口用不上我先用字典保持可读性真遇上了再扩展。你不必追求一步到位能解决当前场景就先把流程跑起来。4.3 进阶玩法用Postman API实现集合在线同步本地文件方案最大的限制在于接口更新后需要手动重新导出。如果是个人项目还好团队协作时Collection天天变总不能天天让人重新导出。进阶方案是直接调用Postman官方API让Skill自己拉取最新集合。思路很简单在Postman里生成一个API Key然后通过请求获取指定集合的JSONcurl -H X-Api-Key: $POSTMAN_API_KEY \ https://api.postman.com/collections/collection_id把返回的JSON存到postman/dev.collection.jsonSkill的其余逻辑完全不用改。这个能力可以封装成一个sync子命令每次执行前先拉一次最新数据。不过我要提醒一点在线同步依赖网络和服务状态跑CI或者离线开发时会很尴尬。我的建议是默认走本地文件需要最新集合时再手动触发一次同步。两条路都要前者保底后者保新。5. 常见问题与避坑指南含速查表5.1 高频故障排查速查表整理这几个月的使用记录我把最常踩的坑列成了一张速查表。对照排查基本能解决九成问题现象原因解决办法Codex不按Skill行动技能未加载或description没匹配确认SKILL.md在~/.codex/skills/下、重启会话在AGENTS.md写明优先使用list输出的URL仍是{{变量}}环境文件路径没传或变量名不匹配检查--env参数确认environment JSON里的key和模板一致请求返回401/403token缺失或过期确认环境文件里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、一个环境文件、一个脚本验证它能列接口、能发请求再慢慢加同步、加断言。你手工改造脚本的过程可能比脚本本身更值钱——那段磨合里踩的坑全是以后工作流里最宝贵的经验积累。