在存量项目中做增量开发,AI 最难解决的通常不是“不会写代码”,而是“不知道该如何理解这个项目”。它可能误读已有约定,遗漏隐含依赖,直接修改不该碰的文件,甚至在执行完成后无法证明结果可靠。开发者于是不断补充上下文、纠正方向、检查差异,AI 反而变成了一个需要持续监督的实习生。
一种更稳妥的做法,是把 AI 协作拆成两层:用 OpenSpec 规范层描述目标、边界和验收条件,用 AI Workflows 执行层安排调查、实现、验证和交付。再配合“规范 + 技能 + 钩子”机制,把 AI 的理解、执行和校验变成可重复的流程。
为什么存量项目需要规范层
新项目可以依赖清晰的目录结构和完整的设计文档,存量项目却往往同时存在历史约定、局部例外和未记录的业务规则。单次对话中的自然语言指令,很难覆盖这些信息。
规范层的作用不是增加文档数量,而是把一次开发任务拆成几个可检查的部分:
- 目标:这次变更要解决什么问题。
- 范围:允许修改哪些模块,明确不应触碰哪些区域。
- 约束:必须遵循哪些 API、命名、兼容性或安全规则。
- 验收:什么结果才算完成,应该运行哪些测试或检查。
- 风险:哪些行为可能影响已有用户、数据或部署流程。
可以把任务规范写成一个轻量文件。下面的示例假设项目使用 openspec/changes/ 保存变更说明,具体目录和命令可以按实际工具调整:
# openspec/changes/add-order-filter.md
## Goal
为订单列表增加按状态和创建时间筛选的能力。
## Scope
- 修改 `src/orders/query.ts` 和对应测试。
- 保持现有分页参数和响应字段不变。
- 不修改数据库迁移和后台管理页面。
## Constraints
- 状态只能使用 `pending`、`paid`、`cancelled`。
- 时间参数使用 ISO 8601 格式。
- 缺少筛选条件时,查询结果必须与当前行为一致。
## Acceptance Criteria
- 状态筛选返回结果中的订单状态全部匹配。
- 时间范围使用左闭右开区间:`createdAt >= from && createdAt < to`。
- 现有订单查询测试全部通过。
- 新增非法状态和非法日期格式的测试。
这里最重要的不是 Markdown 格式,而是让 AI 在动手前拥有一份可以逐条核对的“工作合同”。当需求变化时,先更新规范,再重新评估实现范围,也比直接在聊天窗口里追加几句要求更容易追踪。
执行层:把一次对话变成工作流
规范只能说明“做什么”和“做到什么程度”,还需要执行层回答“按什么顺序做”。一个适合存量项目的 AI Workflow,可以分为四个阶段:
- Inspect:读取规范、项目规则、相关模块和测试,先建立事实清单。
- Plan:列出拟修改文件、调用链、兼容性影响和验证命令。
- Implement:按计划修改代码,每完成一个独立步骤就检查差异。
- Verify:运行格式化、静态检查、单元测试和与需求对应的验收检查。
可以将这套流程固化为一个简单的任务文件,作为 AI 每次工作的入口:
# .ai/workflows/change.yaml
name: implement-change
inputs:
- openspec/changes/add-order-filter.md
- AGENTS.md
- package.json
steps:
- id: inspect
instruction: |
阅读输入文件和相关源码。
输出:现状、相关调用链、风险点,以及需要确认的未知信息。
require_output: investigation.md
- id: plan
instruction: |
根据规范和调查结果生成实现计划。
每个步骤必须包含文件路径、修改目的和验证方式。
require_output: plan.md
- id: implement
instruction: |
只修改计划中允许的文件。
保持现有接口兼容,并为新增行为补充测试。
require_output: git-diff.patch
- id: verify
instruction: |
运行项目已有的检查命令,并逐条对照 Acceptance Criteria。
任何失败都必须说明原因,不得用“应该没问题”代替结果。
commands:
- npm test
- npm run lint
如果项目使用脚本来驱动流程,可以先从一个可运行的本地检查器开始。下面的 Bash 示例会检查变更规范是否存在,并阻止在没有验收条件时进入实现阶段:
#!/usr/bin/env bash
set -euo pipefail
SPEC="${1:-openspec/changes/add-order-filter.md}"
if [[ ! -f "$SPEC" ]]; then
echo "Missing change spec: $SPEC" >&2
exit 1
fi
for section in "## Goal" "## Scope" "## Constraints" "## Acceptance Criteria"; do
if ! rg -q "^${section}$" "$SPEC"; then
echo "Missing required section: ${section}" >&2
exit 1
fi
done
echo "Spec is ready: $SPEC"
运行方式:
chmod +x .ai/check-spec.sh
.ai/check-spec.sh openspec/changes/add-order-filter.md
这个检查器不会替代 AI,也不会判断业务设计是否正确。它只负责把最容易遗漏的流程前置,减少“还没有明确验收标准就开始改代码”的情况。
技能与钩子:分别约束能力和边界
“规范 + 技能 + 钩子”可以理解为三个不同层次的控制点。
规范定义任务契约,解决目标不清和范围漂移;技能定义 AI 如何完成某类工作,解决执行方式不稳定;钩子定义进入下一阶段前必须满足的条件,解决结果无人校验。
一个数据库变更技能可以明确要求 AI:先检查现有迁移,再确认回滚策略,最后运行 schema 校验。一个 API 变更技能则可以要求:先定位路由和 DTO,再检查鉴权、错误码和兼容性测试。
钩子适合放置硬性规则,例如:
- 提交前检查是否修改了规范之外的文件。
- 代码生成后自动执行格式化和类型检查。
- 测试失败时禁止标记任务完成。
- 发现高风险文件时要求人工确认。
- 交付摘要必须列出实际运行过的命令和结果。
可以用一个最小的 Git hook 检查暂存区是否包含未经允许的文件。示例假设规范中的 Scope 只列出了文件路径,实际项目中可以改成更严格的 manifest:
#!/usr/bin/env bash
set -euo pipefail
allowed=(
"src/orders/query.ts"
"src/orders/query.test.ts"
)
is_allowed() {
local file="$1"
for item in "${allowed[@]}"; do
[[ "$file" == "$item" ]] && return 0
done
return 1
}
while IFS= read -r file; do
if ! is_allowed "$file"; then
echo "Blocked: file is outside the approved change scope: $file" >&2
exit 1
fi
done < <(git diff --cached --name-only)
echo "Staged files are within the approved scope."
这类钩子不能保证代码没有缺陷,但能有效阻止一部分高成本错误:误改配置、意外提交生成文件,或者让一次小需求扩散成无法审查的大范围修改。
如何落地而不增加新的负担
规范驱动的协作体系不应该一开始就变成复杂平台。可以按下面的顺序逐步采用:
- 为高频且容易出错的任务建立规范模板,例如 API、数据库、权限和配置变更。
- 先记录项目级规则文件,明确测试命令、目录边界、禁止修改区域和提交要求。
- 把重复出现的执行方式沉淀为技能,而不是每次重新写一遍提示词。
- 只为高风险节点增加钩子,避免用大量机械检查阻塞低风险修改。
- 观察返工次数、越界修改次数、测试失败后的修复轮次,再调整规范和流程。
需要注意的是,规范越多不等于效果越好。过度详细的流程会让 AI 花大量时间维护文档,也会让开发者绕过系统。规范应当靠近真实风险,验收条件应当能够执行或观察,钩子则应该优先拦截不可接受的错误。
结语:让 AI 的可靠性来自系统,而不是运气
AI 在存量项目中的表现,取决于它是否能持续获得准确上下文、明确边界和可验证反馈。OpenSpec 规范层负责把需求变成可检查的契约,AI Workflows 执行层负责把任务变成有顺序的行动,技能和钩子则分别稳定执行方式、守住质量边界。
落地时可以用这份清单自查:
- 任务是否有明确目标和修改范围?
- AI 是否先调查再实现?
- 每个关键行为是否有可执行的验收条件?
- 失败结果是否会阻止任务进入完成状态?
- 是否记录了真实修改文件和实际运行命令?
- 哪些环节仍然必须由人工做最终判断?
当这些信息从一次性聊天内容变成项目中的持久化规范和自动化检查,AI 才会从“会写代码的对话助手”逐步变成可以纳入工程流程的协作工具。