Codex 实战:用 Git 分支 + pytest 构建可回滚的 AI 开发流程(2026年8月27日)

1. 从一次失控的 AI 修改说起

上周我在维护一个 FastAPI 项目时,让 Codex 帮忙重构用户认证模块。任务本身不复杂:把散落在三个文件里的 token 校验逻辑收敛到一个 service 里。Codex 很快给出了修改,测试也通过了。但合并之后,同事发现登录接口的响应时间从 80ms 涨到了 400ms------Codex 在重构时顺手把 Redis 缓存调用改成了每次都查数据库。

问题不在 Codex 本身,而在于我的工作流:没有先让 AI 制定修改计划,没有限制修改范围,没有在独立分支上验证,也没有用 git diff 逐行审查。这次事故让我重新梳理了一套「可回滚、可审查、可测试」的 AI 辅助开发流程。

这篇文章以真实项目为例,完整演示如何用 Codex 配合 Git 分支和 pytest,构建一条安全可控的 AI 开发流水线。文章中的命令、代码和提示词都可以直接复制使用。

2. 项目背景与目标

为了演示,我准备了一个精简但真实的 FastAPI 项目。它包含用户注册、登录和获取个人信息三个接口,其中登录接口依赖一个简单的 token 校验函数。

项目目录结构如下:

text 复制代码
ai_dev_workflow/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI 入口
│   ├── auth.py              # token 生成与校验
│   ├── models.py            # 数据模型
│   └── storage.py           # 内存存储(模拟数据库)
├── tests/
│   ├── __init__.py
│   ├── test_auth.py         # 认证相关测试
│   └── test_api.py          # 接口测试
├── requirements.txt
└── README.md

核心代码已经写好并通过测试。我们的任务是:让 Codex 把 token 校验逻辑从 main.py 中抽离到 auth.py,并保证所有测试通过、行为不变。

先看当前 main.py 的内容:

python 复制代码
from fastapi import FastAPI, Header, HTTPException
from app.auth import create_token, verify_token

app = FastAPI()

@app.get("/users/me")
def get_current_user(authorization: str = Header(...)):
    # 这里存在重复的 token 解析逻辑,需要重构
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid authorization header")
    token = authorization.removeprefix("Bearer ").strip()
    payload = verify_token(token)
    if payload is None:
        raise HTTPException(status_code=401, detail="Invalid or expired token")
    return {"user_id": payload["user_id"], "name": payload["name"]}

auth.py 中已有 create_tokenverify_token,但缺少一个「从 Authorization 头解析 token」的函数。这正是本次重构的目标:把 main.py 里的解析逻辑下沉到 auth.py,让接口层更干净。

3. 第一步:让 Codex 先读项目,不急着改代码

很多开发者拿到 Codex 第一句话就是「帮我改一下」,这很容易让 AI 在没理解项目结构的情况下动手。正确做法是先让 AI 阅读项目、定位相关文件,并输出一份修改计划。

下面是我使用的第一个 Codex 提示词:

text 复制代码
请阅读当前项目(FastAPI + pytest),不要修改任何代码。

任务目标:
1. 理解项目目录结构和各文件职责。
2. 定位 token 校验逻辑在哪些文件中出现。
3. 找出 main.py 中重复的 token 解析代码。

输出要求:
1. 列出相关文件清单。
2. 说明当前 token 校验逻辑的重复位置。
3. 给出一个重构方案,说明要新增什么函数、修改哪些文件。
4. 明确本次重构不涉及的范围(例如不修改数据模型、不改变接口路径)。

在输出计划之前,不要修改任何文件。

这个提示词的关键在于「不要修改任何代码」和「输出计划之前不要动手」。Codex 会先扫描项目,返回一份重构方案。拿到方案后,我人工确认了以下几点:

  • 新增函数 parse_bearer_token 放在 auth.py
  • main.py 只调用新函数,不保留解析逻辑;
  • 不修改 verify_token 的签名和返回结构;
  • 不改变任何接口路径和参数。

确认无误后,进入下一步。

4. 第二步:创建 Git 分支,隔离 AI 修改

在让 Codex 动手之前,先创建一个独立分支。这样无论 AI 改出什么问题,都不会影响主分支,随时可以丢弃重来。

bash 复制代码
# 进入项目目录
cd ai_dev_workflow

# 确保当前在干净的主分支上
git checkout main
git pull origin main

# 创建并切换到功能分支
git checkout -b refactor/token-parsing

# 确认当前分支
git branch --show-current

输出应为:

text 复制代码
refactor/token-parsing

创建分支后,先跑一遍现有测试,确认基线是绿的:

bash 复制代码
python -m pytest -q

如果基线测试失败,说明项目本身有问题,不应该让 AI 在坏基线上继续叠加修改。基线通过后,再让 Codex 开始重构。

5. 第三步:用 Codex 实施重构并补充测试

现在可以给 Codex 下发正式的修改任务。提示词必须包含:任务目标、允许修改范围、禁止修改内容、测试要求和验证方式。

text 复制代码
请基于当前项目实施以下重构,并补充相应测试。

任务目标:
1. 在 app/auth.py 中新增函数 parse_bearer_token(authorization: str) -> dict。
   - 输入为 Authorization 请求头原始字符串。
   - 如果格式不是 "Bearer <token>",抛出 HTTPException(401)。
   - 如果 token 无效或过期,抛出 HTTPException(401)。
   - 返回 payload 字典。
2. 修改 app/main.py 中的 get_current_user,改用 parse_bearer_token。
3. 删除 main.py 中重复的 token 解析逻辑。

允许修改范围:
- app/auth.py
- app/main.py
- tests/test_auth.py
- tests/test_api.py

禁止修改内容:
- app/models.py
- app/storage.py
- 接口路径、请求参数、响应结构
- verify_token 和 create_token 的签名

测试要求:
1. 在 tests/test_auth.py 中为 parse_bearer_token 补充单元测试。
2. 覆盖:合法 token、缺失 Bearer 前缀、空 token、无效 token。
3. 运行 python -m pytest -q,确保全部测试通过。

验证方式:
- 修改完成后,运行 pytest 并报告结果。
- 输出 git diff --stat 摘要,列出修改了哪些文件。

这个提示词把边界划得很清楚。Codex 只能动四个文件,不能碰模型和存储层。测试要求覆盖了正常和异常路径,验证方式要求 AI 自己跑测试并报告结果。

Codex 完成修改后,我得到如下 diff 摘要:

text 复制代码
app/auth.py      | +18
app/main.py      | -12
tests/test_auth.py | +24
tests/test_api.py  | +6

新增的 parse_bearer_token 函数如下:

python 复制代码
from fastapi import HTTPException

def parse_bearer_token(authorization: str) -> dict:
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid authorization header")
    token = authorization.removeprefix("Bearer ").strip()
    if not token:
        raise HTTPException(status_code=401, detail="Missing token")
    payload = verify_token(token)
    if payload is None:
        raise HTTPException(status_code=401, detail="Invalid or expired token")
    return payload

重构后的 main.py 变得非常干净:

python 复制代码
from fastapi import FastAPI, Header
from app.auth import parse_bearer_token

app = FastAPI()

@app.get("/users/me")
def get_current_user(authorization: str = Header(...)):
    payload = parse_bearer_token(authorization)
    return {"user_id": payload["user_id"], "name": payload["name"]}

注意:parse_bearer_token 内部直接抛 HTTPException,这意味着 auth.py 现在依赖 FastAPI。如果未来想复用这个函数到非 Web 场景,需要再解耦。这是 Codex 方案的一个潜在问题,我在人工审查阶段会重点检查。

6. 第四步:运行测试并检查 git diff

Codex 报告测试通过,但我不会直接相信。我会自己再跑一遍,并逐行检查 diff。

bash 复制代码
# 运行全部测试
python -m pytest -q

# 查看详细 diff
git diff

# 查看统计摘要
git diff --stat

测试输出:

text 复制代码
============================= test session starts =============================
collected 8 items

tests/test_auth.py ....                                                  [ 50%]
tests/test_api.py ....                                                   [100%]

============================== 8 passed in 0.42s ==============================

8 个测试全部通过。接下来逐行检查 git diff,重点看三件事:

  1. 是否修改了禁止范围的文件 ------用 git diff --stat 确认只有四个文件变动;
  2. 是否引入了无关改动------例如格式化、变量重命名、注释删除;
  3. 是否改变了接口行为------例如响应结构、状态码、错误信息。

我注意到 Codex 在 auth.py 中新增的 parse_bearer_token 直接依赖 FastAPI 的 HTTPException。这不算错误,但值得记录。如果项目后续要拆分纯 Python 层和 Web 层,这个函数需要再抽象。我在代码审查时给 Codex 提了这个问题,它给出了一个不依赖 FastAPI 的版本:

python 复制代码
class TokenParseError(Exception):
    pass

def parse_bearer_token(authorization: str) -> dict:
    if not authorization.startswith("Bearer "):
        raise TokenParseError("Invalid authorization header")
    token = authorization.removeprefix("Bearer ").strip()
    if not token:
        raise TokenParseError("Missing token")
    payload = verify_token(token)
    if payload is None:
        raise TokenParseError("Invalid or expired token")
    return payload

然后在 main.py 中捕获 TokenParseError 并转换为 HTTPException。这样 auth.py 就不依赖 FastAPI 了。这个改进是我在人工审查阶段提出的,Codex 负责实现------这正是「AI 辅助开发」的正确姿势:AI 提方案、写代码,人做判断和决策。

7. 第五步:人工代码审查清单

AI 生成的代码必须经过人工审查。我总结了一份适用于 Codex 修改的审查清单:

检查项 说明
修改范围 是否只动了允许的文件?用 git diff --stat 确认
接口兼容性 请求参数、响应结构、状态码是否变化?
异常处理 是否覆盖了空值、非法格式、过期 token?
依赖方向 是否引入了不合理的依赖(如业务层依赖 Web 框架)?
敏感信息 是否有 API Key、密码、Token 被硬编码或打印?
测试覆盖 新增代码是否有对应测试?异常路径是否覆盖?
无关改动 是否顺手改了格式化、变量名、注释?

针对本次重构,我重点确认了异常处理。Codex 的初版方案把 HTTPException 直接抛在 auth.py 里,虽然功能正确,但让业务层依赖了 Web 框架。经过一轮提示词迭代,改为自定义异常 + 接口层转换,职责更清晰。

8. 第六步:合并与回滚策略

测试通过、审查无误后,合并分支:

bash 复制代码
# 切回主分支
git checkout main

# 合并功能分支
git merge refactor/token-parsing

# 推送远程
git push origin main

如果合并后发现线上问题,回滚也很简单:

bash 复制代码
# 回滚到上一个稳定版本
git revert HEAD

# 或者直接重置到合并前
git reset --hard HEAD~1

git revert 会生成一个新的反向提交,适合已经推送到远程的情况;git reset --hard 会丢弃提交历史,只适合本地未推送的分支。生产环境优先使用 git revert

9. 完整工作流总结

下面是本次实战的完整流程图:
#mermaid-svg-TcqxLonIELdTBSwN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TcqxLonIELdTBSwN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TcqxLonIELdTBSwN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TcqxLonIELdTBSwN .error-icon{fill:#552222;}#mermaid-svg-TcqxLonIELdTBSwN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TcqxLonIELdTBSwN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TcqxLonIELdTBSwN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TcqxLonIELdTBSwN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TcqxLonIELdTBSwN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TcqxLonIELdTBSwN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TcqxLonIELdTBSwN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TcqxLonIELdTBSwN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TcqxLonIELdTBSwN .marker.cross{stroke:#333333;}#mermaid-svg-TcqxLonIELdTBSwN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TcqxLonIELdTBSwN p{margin:0;}#mermaid-svg-TcqxLonIELdTBSwN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TcqxLonIELdTBSwN .cluster-label text{fill:#333;}#mermaid-svg-TcqxLonIELdTBSwN .cluster-label span{color:#333;}#mermaid-svg-TcqxLonIELdTBSwN .cluster-label span p{background-color:transparent;}#mermaid-svg-TcqxLonIELdTBSwN .label text,#mermaid-svg-TcqxLonIELdTBSwN span{fill:#333;color:#333;}#mermaid-svg-TcqxLonIELdTBSwN .node rect,#mermaid-svg-TcqxLonIELdTBSwN .node circle,#mermaid-svg-TcqxLonIELdTBSwN .node ellipse,#mermaid-svg-TcqxLonIELdTBSwN .node polygon,#mermaid-svg-TcqxLonIELdTBSwN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TcqxLonIELdTBSwN .rough-node .label text,#mermaid-svg-TcqxLonIELdTBSwN .node .label text,#mermaid-svg-TcqxLonIELdTBSwN .image-shape .label,#mermaid-svg-TcqxLonIELdTBSwN .icon-shape .label{text-anchor:middle;}#mermaid-svg-TcqxLonIELdTBSwN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TcqxLonIELdTBSwN .rough-node .label,#mermaid-svg-TcqxLonIELdTBSwN .node .label,#mermaid-svg-TcqxLonIELdTBSwN .image-shape .label,#mermaid-svg-TcqxLonIELdTBSwN .icon-shape .label{text-align:center;}#mermaid-svg-TcqxLonIELdTBSwN .node.clickable{cursor:pointer;}#mermaid-svg-TcqxLonIELdTBSwN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TcqxLonIELdTBSwN .arrowheadPath{fill:#333333;}#mermaid-svg-TcqxLonIELdTBSwN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TcqxLonIELdTBSwN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TcqxLonIELdTBSwN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TcqxLonIELdTBSwN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TcqxLonIELdTBSwN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TcqxLonIELdTBSwN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TcqxLonIELdTBSwN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TcqxLonIELdTBSwN .cluster text{fill:#333;}#mermaid-svg-TcqxLonIELdTBSwN .cluster span{color:#333;}#mermaid-svg-TcqxLonIELdTBSwN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TcqxLonIELdTBSwN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TcqxLonIELdTBSwN rect.text{fill:none;stroke-width:0;}#mermaid-svg-TcqxLonIELdTBSwN .icon-shape,#mermaid-svg-TcqxLonIELdTBSwN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TcqxLonIELdTBSwN .icon-shape p,#mermaid-svg-TcqxLonIELdTBSwN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TcqxLonIELdTBSwN .icon-shape .label rect,#mermaid-svg-TcqxLonIELdTBSwN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TcqxLonIELdTBSwN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TcqxLonIELdTBSwN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TcqxLonIELdTBSwN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否



需求说明
Codex 阅读项目
输出修改计划
人工确认计划
创建 Git 分支
运行基线测试
Codex 修改代码
Codex 补充测试
运行 pytest
测试通过?
检查 git diff
人工代码审查
审查通过?
合并分支
推送并监控

这套流程的核心原则是:AI 负责执行,人负责决策。Codex 可以快速阅读项目、生成代码、补充测试,但修改计划、范围边界、最终审查必须由人来把控。

10. 常见错误与解决方法

错误 1:没有先让 AI 读项目就直接改

症状:Codex 修改了错误的文件,或者重复实现了已有功能。

解决:先发「只读分析」提示词,让 AI 输出项目结构和修改计划,确认后再动手。

错误 2:没有限制修改范围

症状:Codex 顺手格式化了整个项目,diff 里出现大量无关改动。

解决:在提示词中明确「允许修改范围」和「禁止修改内容」,用 git diff --stat 检查。

错误 3:只信 AI 的测试结果

症状:Codex 报告「测试通过」,但实际没跑或只跑了部分测试。

解决:自己运行 python -m pytest -q,并检查测试数量是否合理。

错误 4:在主分支上直接让 AI 改

症状:AI 改坏了,无法快速回滚。

解决:始终在独立分支上操作,合并前确保基线测试通过。

11. 安全注意事项

使用 Codex 辅助开发时,必须注意以下几点:

  • 不要把 API Key、密码、Token 写进代码。Codex 生成的代码如果包含敏感信息,审查时必须删除。
  • 使用环境变量保存敏感配置。例如数据库密码、第三方服务密钥,一律从环境变量读取。
  • 不向 AI 提交生产环境密码。在提示词中不要粘贴真实密钥,用占位符代替。
  • 检查日志和配置文件中的敏感信息。Codex 可能在调试代码中加入打印语句,合并前检查。
  • 限制 AI 可以修改的文件范围。在提示词中明确允许和禁止的文件列表。
  • 使用最小权限原则。本地开发账号不要使用生产环境权限。
  • AI 生成代码必须经过测试和安全检查。不要因为测试通过就跳过人工审查。
  • 不要直接让 AI 修改生产环境。所有修改先在本地分支验证,再走合并流程。

12. FAQ

Q1:Codex 和 ChatGPT Plus / ChatGPT Pro 是什么关系?

Codex 是 OpenAI 面向开发者的 AI 编程工具,可以理解为「能直接操作代码仓库的 AI 助手」。ChatGPT Plus 和 ChatGPT Pro 是 ChatGPT 的订阅套餐,具体包含哪些功能、是否包含 Codex 使用权限,会随版本和地区变化,请以 OpenAI 当前官方页面为准。

Q2:每次都要让 AI 先输出计划吗?

对于涉及多个文件、可能影响接口行为的修改,强烈建议先输出计划。对于单文件的小改动(如修一个变量名),可以直接修改,但仍要检查 diff。

Q3:Codex 生成的测试可信吗?

Codex 生成的测试可以作为基础,但需要人工补充边界条件和异常路径。本次实战中,Codex 自动生成的测试覆盖了合法 token 和无效 token,但「空 token」「缺失 Bearer 前缀」是我在提示词中明确要求的。

Q4:如果 Codex 修改了禁止范围的文件怎么办?

git checkout -- <文件> 恢复该文件,然后在提示词中再次强调范围限制。如果反复出现,说明提示词不够清晰,需要更明确地列出允许修改的文件清单。

Q5:如何确认 Codex 没有引入安全漏洞?

人工审查时重点检查:敏感信息是否硬编码、输入是否校验、异常是否被吞掉、依赖是否引入不安全的版本。必要时用 pip-auditnpm audit 检查依赖安全。

13. 总结

Codex 是强大的 AI 编程助手,但它不是「自动驾驶」。真正可靠的 AI 开发流程,是把 Codex 放进一个受控的 Git 工作流里:先读项目、再定计划、然后开分支、改代码、补测试、查 diff、人工审查、最后合并。

本次实战中,Codex 完成了 token 解析逻辑的重构,并生成了对应的单元测试。但真正让这次重构安全落地的,是分支隔离、基线测试、diff 审查和人工决策。这套流程适用于 FastAPI、Spring Boot、React 等任何技术栈,核心思想是一致的:AI 提效,人控质量

具体功能和可用范围可能随版本、账号和地区变化,请以 OpenAI 当前官方页面为准。

相关推荐
科技新芯14 分钟前
2026年国内可用AI生图网站实测:ImageGood、ChatGPT、Gemini、Firefly怎么选?
人工智能·chatgpt
jkyy201416 分钟前
从商品售卖到健康服务,数字化重构大健康品牌会员运营底层逻辑
大数据·人工智能·信息可视化·健康医疗
yangshuo128119 分钟前
用AI开发一个公网IP查询小工具:精准查询IDE/终端的真实出口IP(Python实战)
ide·人工智能·tcp/ip
架构技术专栏19 分钟前
Skill 写了几十条规则,Codex 为什么还是不按你的方式干活?
人工智能
judezh34 分钟前
MCP 接进生产环境之前,先回答这五个问题
人工智能
星火102437 分钟前
【从 0 到 1 动手造 Agent】02、确定性铁笼 LangGraph
人工智能·后端·agent
Csvn39 分钟前
第 8 章 RAG 工程
人工智能
秋风点枝42 分钟前
第一篇:认识 Argo CD —— 从传统发布到云原生 GitOps
git·云原生·argocd
CyberwayTech43 分钟前
从自然语言到结构化活动方案:Cyber TPM AI智能活动推荐如何嵌入TPM业务流程
人工智能·tpm·赛博威·营销费用管理·快消