Claude Code + Codex + Copilot 在同一项目里协作,我一个下午的实录
前三篇分别展示了每个工具单独用是什么体验。但真正干活的时候,从来不是"今天用这个、明天用那个"。同一个需求里,你可能五分钟前在终端让 Claude Code 写接口,两分钟前在 VS Code 里让 Copilot 修了个细节,现在又开个 Codex 沙箱试一个不确定的库。这篇用一整个下午的真实工作流,把三个工具串起来。
Claude Code + Codex + Copilot 在同一项目里协作,我一个下午的实录
前三篇分别展示了每个工具单独用是什么体验。但真正干活的时候,从来不是"今天用这个、明天用那个"。
同一个需求里,你可能五分钟前在终端让 Claude Code 写接口,两分钟前在 VS Code 里让 Copilot 修了个细节,现在又开个 Codex 沙箱试一个不确定的库。
这篇用一整个下午的真实工作流,把三个工具串起来。
需求
公司的内部后台(Express + SQLite)管着几百条物料记录。老板说:“能不能导出成 CSV,然后自动发邮件给采购?”
这个需求可以拆成三块:
- CSV 导出——已有查询接口,加个格式转换就行
- 定时任务——每周五下午跑一次
- 邮件发送——用公司内部的 SMTP 服务
前两个没啥问题。第三个有点麻烦——公司的 SMTP 服务器配置很奇葩,需要走 TLS+STARTTLS 的特殊组合。搞不好把邮件服务搞崩了,影响其他模块。
于是分工:Claude Code 负责全局、Copilot 负责细节、Codex 负责试探 SMTP。
第一步:Claude Code 开局
在项目根目录启动:
$ claude
• Scanning directory... Express app detected
- app.js (main routes)
- models/material.js (Material model)
- services/ (business logic)
- config/ (env config)
- package.json (dependencies: express, sqlite3, better-sqlite3)
先让它了解现有结构,然后描述需求:
我需要一个物料记录导出功能:查询 material 表的所有记录,导出为 CSV
文件,然后通过公司 SMTP 服务器发送邮件。CSV 和邮件是两个独立
的服务层。先实现 CSV 导出部分,SMTP 邮件部分先预留接口但暂不
实现——我要先在沙箱里验证邮件方案。
Claude Code 开始动手:
• Reading models/material.js (found: id, name, quantity, unit, supplier, updated_at)
• Reading services/ (3 existing service files)
• Reading config/mail.js (existing SMTP config placeholder)
→ Plan:
1. Create services/csv_exporter.js — query materials, generate CSV
2. Add route POST /materials/export — trigger CSV export
3. Create services/mailer.js — placeholder with send() interface
4. Add tests for CSV exporter
→ Implementing:
[+] services/csv_exporter.js (42 lines)
[~] app.js — add /materials/export route
[+] services/mailer.js (23 lines, placeholder)
[+] tests/csv_exporter.test.js (55 lines)
→ npm test — 5 passed
→ curl test: POST /materials/export → 200, returns CSV content
几分钟,CSV 导出和邮件预留接口都好了。services/mailer.js 还写了清晰的 JSDoc 注释,标了 TODO:待验证 SMTP TLS+STARTTLS 配置。
第二步:切到 VS Code,Copilot 修细节
Claude Code 生成的代码跑通了,但扫一眼发现有东西要改。
services/csv_exporter.js 里有一行:
const csv = records.map(r => Object.values(r).join(',')).join('\n');
逻辑是对的,但如果物料名称里包含逗号,CSV 就炸了。我在这个文件里停下来,用 Cmd+I 唤起 Copilot Agent,在行内直接输入:
this CSV generation will break if values contain commas — use a proper escaping library
Copilot 在内联 diff 里直接替换了这一行:
- const csv = records.map(r => Object.values(r).join(',')).join('\n');
+ import { stringify } from 'csv-stringify/sync';
+ const csv = stringify(records, { header: true });
顺手还改了 package.json——加了 csv-stringify 到 dependencies。
这种小修补是 Copilot 最强的场景——不需要切终端、不需要描述上下文。你看着哪行有问题,光标停在那里,说一句话,改了。
十分钟的代码审查里,Copilot 帮我修了四个地方:CSV 转义、一个缺少的 try-catch、一个错误的 HTTP 状态码、还有一个注释拼写。
第三步:Codex 试水 SMTP
SMTP 这块我信不过。公司的邮件服务器配置让人头疼,之前手写邮件功能踩过两次坑。我不确定 nodemailer 的 TLS+STARTTLS 组合在当前的版本里能不能直接用。
开个 Codex 沙箱,单独验证邮件方案:
$ codex exec --sandbox danger-full-access \
"用 nodemailer 连接一个 SMTP 服务器,要求:
- host: smtp.company.local
- port: 587
- 使用 STARTTLS 升级到 TLS 连接
- 不实际发送邮件,只验证连接是否成功
- 把能用的配置模板打印出来"
注意这里用了 --sandbox danger-full-access——因为要装 npm 包,需要网络。日常用 Codex 沙箱的话默认是不通网的,有网络需求的实验得显式开。
沙箱里,Codex 用了一个很聪明的方法来避免对 SMTP 服务器的实际冲击——自己写了个迷你 server.js 监听端口 2525,模拟 SMTP 的 EHLO → STARTTLS 握手序列:
→ Tool: shell $ npm install nodemailer
→ Tool: write mock_smtp_server.js (25 lines, simulates EHLO+STARTTLS)
→ Tool: shell $ node mock_smtp_server.js &
→ Tool: write test_transport.js
→ Tool: shell $ node test_transport.js
→ Connection successful with config:
{
host: 'localhost',
port: 2525,
secure: false,
requireTLS: true,
tls: { rejectUnauthorized: false }
}
→ Verified: nodemailer STARTTLS handshake works with this config shape
然后把 localhost:2525 换成公司的 smtp.company.local:587,配置结构一模一样。连同 rejectUnauthorized: false 的警告注释也保留——“仅内网使用,生产环境建议配置 CA 证书”。
回到项目里,把验证通过的配置填入 services/mailer.js,删掉 TODO 注释。整个过程公司邮件服务器零接触。
第四步:回到 Claude Code,收尾
SMTP 方案验证完了。再次启动 Claude Code,告诉它:
Codex 沙箱验证了 nodemailer 的 STARTTLS 配置可行。
现在把 services/mailer.js 从 placeholder 升级为完整实现,
用这套 SMTP 配置。sender 和 recipients 从 config/mail.js 读取。
Claude Code 读到 services/mailer.js 里我刚才从 Codex 拿来的配置,接着干活:
• Reading services/mailer.js (updated with verified SMTP config)
• Reading config/mail.js
• Updating services/mailer.js — replacing placeholder with full implementation
[+] send() function with nodemailer transport
[+] sendCSVReport() — exports CSV and attaches to email
• Re-reading services/csv_exporter.js (noticed Copilot's csv-stringify change)
✓ Compatible — mailer calls csv_exporter.generateCSV()
• Running tests: 7 passed
有意思的是它发现了 Copilot 改的 csv-stringify,自动调整了 mailer.js 里的调用方式——我之前用的是手动拼接的 CSV,它改成调 csv_exporter.generateCSV() 获取格式化好的内容。
最后生成 commit message:
feat: add material CSV export and email delivery
- Add CSV exporter service with proper escaping (csv-stringify)
- Add nodemailer-based mailer with SMTP STARTTLS support
- SMTP config verified in sandbox before integration
- Add POST /materials/export route
- Include tests for CSV export
扫一眼 message,准确描述了三个工具各自贡献的部分。确认,提交。
这个下午到底发生了什么
回头整理一下,三个工具各自干了什么:
| 阶段 | 工具 | 干的活 | 为什么用它 |
|---|---|---|---|
| 实现主体功能 | Claude Code | CSV 导出、路由、测试、邮件骨架 | 项目级上下文,一次搞定多文件 |
| 代码审查 | Copilot | CSV 转义修复、try-catch、状态码 | 光标停在有问题的那行,说一句话就改了 |
| 验证危险操作 | Codex | 沙箱里测试 SMTP 连接 | 不想拿公司邮件服务器当小白鼠 |
| 集成收尾 | Claude Code | 实现 mailer、对接配置、提交 | 它知道整个项目的变更上下文 |
没有哪个工具"更好"。是"这个环节最适合用哪个"。
怎么判断什么时候用哪个
几次实战下来,形成了一套判断逻辑,现在基本是条件反射了。
用 Claude Code 的时候:
- 跨多个文件的功能。一个需求需要改三层代码——它一次性全改了
- 需要用
git log或项目历史来理解现有逻辑的时候 - 写测试。它能自己跑、自己修,直到全绿
- 生成 commit message。改了什么它最清楚
用 Copilot 的时候:
- 代码审查阶段。看着 Claude Code 的输出,发现哪行不对,停在那行,说一句话
- 写小函数、补全代码块——手写完函数签名,Tab 就出来了
- 在代码中间插入逻辑——标好位置,
Cmd+I描述要加什么
用 Codex 的时候:
- 不确定一个库能不能用、一个配置对不对。先在沙箱里试
- 涉及外部依赖或可能有副作用的事情。邮件、短信、支付
- 从零搭东西。新项目在沙箱里跑通了再落地
- 学习/探索。沙箱里随便折腾,搞坏了就否决重来
改什么、写什么,这事 Claude Code 处理。一行代码对不对,Copilot 处理。不确定的事,Codex 处理。
三个工具的关系
像是台球桌上的三根杆——开球用重杆,角度球换轻杆,跳球还有专门的跳杆。你不会拿开球杆去打角度球,也不会只带一根杆打完整场。
从装上第一天我就把三个都放在手边。现在每天打开的项目里,Copilot 一直在后台,Claude Code 处理大任务,Codex 偶尔被喊来跑个实验。谁也不取代谁。
下一篇
快速上手阶段的最后一篇。接下来进入深度实战——从零用 Claude Code 重构一个真实项目,看看它在复杂场景下的表现。
更多推荐



所有评论(0)