GitHire

Case 01 · Real production incident ·

从一条 prompt
到生产事故,
只用了 20 分钟.

一段 22 行的代码,由 AI 自动写出、通过本地测试、合入主干、部署上线,把生产 Redis 打到 503。 这是一次真实事故的复盘,沿着 GitHire 的六步 workflow 走一遍,看哪一步没被认真对待—— 事故就在哪一步埋下。

  1. 10:19 用户发出 prompt
  2. 10:24 AI 写出 SCAN+HGETALL
  3. 10:39 commit · 进入 PR
  4. 21:35 PR 合并 · 部署
  5. 23:54 生产事故 · 第一次救火
  6. +1d 11:34 Redis SET 彻底替代

事故代码:22 行 · 救火 commit:5 次 · 真正根治用时:25 小时

Why the workflow exists · 方法论

AI 写代码的速度,
已经远快于人类 review 的速度.

过去的工程范式默认"人在写代码,AI 是辅助",所以 review 是顺序里最后一步—— 人写完 → 测试 → review。这条线在 AI-native 时代被反过来了: AI 在写,人在审;人在 frame,AI 在执行

六步 workflow 不是给 AI 装护栏,是给人类留决策点。 Issue 是 framing 的决策点;架构师评审是方向的决策点; sandbox 与 AI review 是两次"还来得及反悔"的决策点。 一旦哪个决策点形同虚设,AI 的速度就会变成事故的速度。

下面这个 case,会沿着六步走读:
每一步在理想中应该是什么样,在这次事故里又实际是什么样。

先看完整 workflow ↗

STEP 01 / 06 · ISSUE

理想:把"为谁解决什么"写清楚
实际:一条聊天框 prompt.

Real prompt · 2026-05-14 10:19 +0800 · Codex CLI · gpt-5.5

当前这个国产模型的判断走的是前缀匹配。我想把它做成 model detail 里面的字段,我记得原本就有一个 made_in_china 的字段。

我们要把这个字段利用起来,使其更加灵活、可配置。此外,在做 CI 的时候,需要针对国内站和国际站分别做 search 检查,确保:

  1. 国内站:对应的 search 接口只返回所有 made_in_china 的模型。
  2. 国际站:不作限制,正常返回。

建议在 smoke test 那边增加这样一个 E2E 的 CI 流程小步骤进行测试。

STEP 02 / 06 · SANDBOX

理想:长期沙盒,带真实数据
实际:本地仓库,pytest 全绿.

Codex 在本地仓库直接修改文件、跑 pytest tests/test_site_mode.py -q, 103 个用例全通过。 但 pytest 用的是 fakeredis 或 in-memory mock,里面只有几条测试数据。 真实生产环境里 model_detail::* 有几十到上百个 key, 而且 /api/site/config 在前端启动时每个用户都会打一次。

沙盒的价值,不在于"环境能跑",在于"环境像生产"。 缺数据规模、缺 QPS 量级、缺并发——这三样缺一个,AI 写出的实现就可能在量级跳变时塌掉。

STEP 03 / 06 · EXECUTE

理想:AI 在沙盒里完成实现
实际:5 分钟后,22 行 patch 出炉.

wowchat/site_config.py · 由 AI 生成 · 10:24:27
def get_domestic_model_ids() -> List[str]:
    model_ids = []
    cursor = 0
    while True:
        cursor, keys = r.scan(cursor, match='model_detail::*', count=200)
        if keys:
            pipe = r.pipeline()
            for key in keys:
                pipe.hgetall(key)
            for key, detail in zip(keys, pipe.execute()):
                if not is_made_in_china_model_detail(detail):
                    continue
                model_id = detail.get('engine') or key.split('::', 1)[1]
                if model_id:
                    model_ids.append(model_id)
        if cursor == 0:
            break
    return sorted(set(model_ids))

@router.get('/config')
async def get_site_config(request: Request):
    return ResponseWrapper(result=SiteConfigModel(
        site_mode=resolve_site_mode(request),
        domestic_model_ids=get_domestic_model_ids(),  # ← 每次请求都跑一遍
    ))

STEP 04 / 06 · AI REVIEW

理想:另一位 AI 读一遍 PR
实际:这一步没发生.

从 prompt 到 commit,整个流程在同一个 Codex session 里走完, 没有第二个 agent 读过这个 diff。 本地测试通过就成了唯一的"通过"信号。

如果当时有一个独立 review agent,给它的 system prompt 里写一句 "Flag any new Redis SCAN / KEYS / HGETALL across full keyspace in request-path code", 这次事故大概率就在这一步被拦下。

AI review 的本质

不是"再跑一遍测试",而是用另一组先验看同一段代码—— review agent 该懂的是性能模式、安全模式、可维护性模式, 和生成 agent 互补,不重叠。

STEP 05 / 06 · ARCHITECT · 关键一步

理想:人类架构师判断方向
实际:这一步被跳过了.

从 prompt 到 commit,总共 20 分钟
没有任何一刻,让一个懂这个系统的人看一眼: "每次 /api/site/config 都 scan 全量 model_detail::*,意味着什么?"

架构师应该问的问题

  1. 这个 endpoint 每分钟会被调用多少次?
  2. model_detail::* 的 key 数量当前多少?预计涨到多少?
  3. 能不能维护一个 set,把"国产模型 id"显式存起来,避免运行时扫描?
  4. 这次改动是 hot path 还是 cold path?是不是该走 cache?
  5. 如果 Redis 抖动 / 慢,这个调用会不会把上游 API 拖垮?

为什么这些问题 AI 不会自己问

AI 看到的是当前仓库的代码上下文,看不到生产 QPS、 看不到 Redis 容量曲线、看不到团队历史上踩过哪些坑。 这些是架构师才有的"系统侧上下文", 也是为什么这一步必须是人。

AI 能在 5 分钟写出正确实现;架构师只需要 30 秒就能识别"不该用这个路径"。 这 30 秒,就是工作流里最贵的一段时间。

STEP 06 / 06 · PRODUCTION

理想:合入主干,平稳上线
实际:当晚 23:54,生产报警.

部署后约两小时,监控开始报:/api/site/config p99 飙到秒级, Redis 慢日志被 SCAN + 大量 HGETALL 塞满, CPU 占用打高,连带其他依赖 Redis 的接口一起被拖慢。 首屏阻塞、新用户进不来。

上线那一刻,没人觉得这次有问题—— CI 全绿、PR 描述清楚、AI 写的代码看起来"工整"。 事故的特征就是:它在所有传统门禁里都看起来合规。

救火链 · 5 个 commit · 25 小时

救火链没有一击即中
是一连串 不够根治 的尝试.

  1. 事故 +5 分钟

    Cache 结果 + 把 SCAN 挪到 threadpool

    第一反应:让 SCAN 不要堵主线程。问题被推后,没解决——cache 失效那一刻还是会打满。

  2. +30 分钟

    改成 in-memory cache + 60s 后台刷新

    把 SCAN 从"每次请求"降到"每分钟"。事故面积变小,但启动期还是会一次性扫满。

  3. +90 分钟

    Drop 后台刷新 · 改成 startup-only warmup

    承认在线刷新有风险,退回到只在启动时扫一次。但服务每次重启时还是要承压

  4. +6 小时

    Restore handler · 调好 caching contract

    把 endpoint 行为修对,确保 fallback 不会引入新 bug。

  5. +25 小时

    用 Redis SET 维护 domestic ids

    这才是根治:模型上下线时显式写入一个 set, 读取时直接 SMEMBERS,从 O(N) 扫描降到 O(1) 查询。 SCAN 这条路径在生产代码里彻底消失。

每一步救火都"看起来更好了一点",但根治用了 25 小时。 如果架构师在 step 05 多花 30 秒,这 25 小时就不会发生。

Rewrite · 同一条需求

一条 architect-readable 的 issue
长成什么样.

原 prompt · 缺四样东西

BAD

当前这个国产模型的判断走的是前缀匹配。我想把它做成 model detail 里面的字段……

需要针对国内站和国际站分别做 search 检查……

建议在 smoke test 那边增加 E2E 测试。

  • ✗ Constraints
  • ✗ Non-goals
  • ✗ Verification
  • ✗ Architecture notes

同一需求 · 重写

GOOD

Goal 把"国产模型"判定从 engine 前缀改成 model_detail.made_in_china 字段,让上下线只需改一个字段。

Constraints /api/site/config 是高频接口,前端启动时每个用户都会打。不允许每次请求扫 Redis keyspace。model_detail 当前 ~80 条,可能涨到 ~500。

Non-goals 不要用 SCAN / KEYS 实现。不要在 request-path 里做 O(N) 计算。本次不改前端缓存策略。

Architecture notes 建议显式维护一个 Redis SET(如 domestic_model_ids),模型上下线时写入;读路径 O(1)。

Verification 国内站 search 只返回 made_in_china=1;国际站不变;线上 /api/site/config p99 不增。Smoke 新增 E2E 断言。

  • ✓ Constraints
  • ✓ Non-goals
  • ✓ Verification
  • ✓ Architecture notes

右边这版多了 ~150 字。这 150 字,能换 25 小时的救火 + 一段生产事故。

把这套六段式 issue 模板装成 Prompt Spec Skill ↗ — 直接读 SKILL.md,或 npx skills add realRoc/skills --skill prompt-spec

Takeaways

这个 case 教会我们三件事.

  1. AI coding 的速度,要求 review 前置。

    从 prompt 到 prod-bound commit 只用了 20 分钟。等"PR review"再发现问题,已经晚了—— 真正的 review 应该发生在 prompt 阶段,由 issue framing 完成。

  2. 没有 constraints / non-goals 的 prompt,是空白支票。

    AI 不会自己问"调用频率多少"、"数据规模多大"。 这些数字必须由人塞进 issue。需求里每一处空白,都是事故的入口。

  3. 架构师评审是工作流里最贵的 30 秒。

    AI 写代码 5 分钟,架构师识别"不该用这个路径"30 秒。 这一步必须是人——因为只有人有"系统侧上下文"(QPS 曲线、历史事故、容量规划)。