3067 字
约 10 分钟
18
S24-X16 · AI 编程工具的边界与失效模式

用一段时间 Claude Code / Codex CLI 之后,多数人会形成一套模糊直觉:有些活能用它,有些不能用。这篇把那套直觉拆成可判断的东西——边界在哪四个维度上、失效模式有哪几类、任务开始前怎么预判。


1. 能力边界的四个维度

维度 边界在哪 触发失效的典型场景
上下文能装多少 窗口有限,且有效注意力低于名义容量 让它改一个 3000 行的巨型类
训练数据时间与领域 截止日期之后的 API 变更不在知识内 用新版本框架的推荐写法改写老代码
能否执行与观察 能跑命令不等于能看懂结果 跑通了构建,但没看出告警里的关键信息
能否长期记忆 跨会话无记忆,靠文件承载 上周定下的命名约定,今天它又忘了

前两个是硬边界,提问技巧突破不了;后两个是软边界,能用工程手段补(约定写进 AGENTS.md、要求贴出命令输出)。

一个常见误解:以为"窗口够大就没问题"。实际上窗口越大,位置靠中的材料被利用的效率越低(典型量级,按所用模型核对)。把 5000 行代码塞进 1M 窗口,效果不如精心挑出的 300 行。


2. 六类失效模式

2.1 幻觉 API 与配置项

// 看着像标准库,实际不存在
List<Order> first = orders.stream().filterIf(true, ...).toListOrEmpty();
# 键名编造:看起来对,启动时报 Unknown property
spring:
  datasource:
    hikari:
      maximum-pool-size: 20   # 真实键名是 max-pool-size

共同特征是出错前无法从语法上判断,唯一可靠检验是编译与启动。

2.2 把旧版本写法当现行

领域 旧写法 现行写法
Spring Security WebSecurityConfigurerAdapter SecurityFilterChain Bean
HTTP 客户端 RestTemplate 裸用 RestClient / WebClient
Java 集合 Collections.unmodifiableList 手动包装 List.copyOf / Stream.toList()
前端构建 webpack.config.js 手写 Vite / 框架内置

默认输出倾向语料里频次最高的写法,而语料里老代码永远比新代码多。纠正方式:明确要求"按 OrderService 的现有写法来"。

2.3 忽略项目特定约定

它知道"Java 里怎么判空",不知道"这个项目禁止返回 null 集合"。这类失效不产生编译错误,只产生评审返工。

2.4 过度重构

让它改一个方法,它顺手改了命名、抽了接口、注入了两个新依赖。diff 从 20 行变成 400 行。

# 生成代码的"顺手优化"
- 加了 OrderMapperFactory 抽象层
- 把 3 处重复逻辑抽成 AbstractOrderHandler
- 引入 MapStruct 做 DTO 转换

每处单看都"更好",合起来是这次任务不该承担的风险。

2.5 只处理主路径

正常输入走通,异常、空值、超时、并发全缺。这是生成代码里最稳定的缺陷类型(见 X10 的检查点二)。

2.6 大文件与长链路的局部最优

让它改一个跨 8 个文件的调用链,它往往把每个文件都改得"局部合理",但整体契约对不上:上层传了 null,中层没判断,底层抛异常。这不是它不努力,是它一次只能看到一个断面的取舍。


3. "能编译"不等于"能用"

编译是一个极弱的正确性证明。它只排除语法错误和类型不匹配,剩下的全都不管。

检查 能发现 发现不了
编译 语法、类型、符号不存在 逻辑、边界、配置键名、并发
单测 被断言的路径 未覆盖的分支、真实装配
启动 配置加载、Bean 装配 运行期行为、数据形态
真实请求 端到端主路径 并发、大数据量、下游故障
灰度观察 真实流量下的异常分布 低频边界

所以"能编译"连前三档都算不上,它只是最低前提。验收至少要跨到"真实请求"这一档。


4. 知识时效性

三个具体表现:

  1. 废弃 API:仍生成已标记 @Deprecated 的调用;
  2. 版本行为变更:同一配置键在旧版是默认值,新版改了默认;
  3. 新语法缺失:不熟悉 record、模式匹配,回退到冗长写法。
# 确认项目实际版本,再决定"新写法"是否可用
$ grep -A2 "<parent>" pom.xml | head -5
$ ./mvnw -v

处理方式是把版本作为上下文显式给出:

项目使用 Spring Boot 3.3(Java 17)。禁止使用 WebSecurityConfigurerAdapter
与 javax.* 包;允许使用 record、switch 模式匹配。

不给版本信息时,它会按"最通用的写法"输出,而最通用往往等于最旧。


5. 安全与合规相关的失效

这一节不是道德讨论,是失效模式分类:这些地方出错的方式和其他地方不一样,因为错误不一定表现为功能异常。

失效 具体形态 后果
凭据处理 把连接串、密钥直接写进源码或测试 随代码进仓库
日志脱敏 打印整个请求体、完整手机号/卡号 敏感数据进日志系统
供应链 为解决小问题引入一个新的第三方包 依赖来源未评估
输入校验 信任前端参数,直接拼进 SQL 或路径 注入
错误信息 把异常栈、内部路径返回给调用方 信息泄露

共同点是不会让程序报错或变慢,测试永远发现不了,防线只能设在约定与评审里:

## AGENTS.md 片段
- 禁止在源码、测试、配置模板中写入真实密钥;用占位符 `${DB_PASSWORD}`
- 日志禁止打印完整身份证号、手机号、卡号;需脱敏到前 3 后 4
- 新增依赖需在 PR 说明:为什么不使用已有能力
- 对外错误响应只返回 code + 通用文案,异常栈只进日志

6. 不确定性表达的价值

合格的输出会标注"这里我不确定",不合格的输出会把不确定的地方硬编成确定的代码。

表现 价值 处理
"假设 status 只有 PENDING/PAID/CANCELLED 三种取值" 高 直接确认这条假设
"这里用了 @Transactional 默认传播行为,若需新事务请说明" 高 明确需求
"根据常见实现推断该类存在 findById 方法" 高 立刻核对真实签名
什么都没说,代码里写死了判断 低 逐处追问"你的依据是什么"

把它变成固定要求:

实现时请在文档注释里用 `// ASSUME:` 标出所有未经确认的假设,
并在结尾汇总成清单。不要为了代码完整而自行补齐你没看到的信息。

ASSUME 标记的价值在于它把隐藏的猜测变成可搜索的文本:rg "ASSUME:" 就能拿到这次改动里所有的未验证前提。


7. 哪些任务适合、哪些不适合

判断维度 适合 不适合
是否可验证 有测试、有命令能判定对错 只有"看起来对"一个标准
是否本地化 改动集中在 1–3 个文件 跨 10+ 文件的链路改造
是否有明确契约 接口、数据结构、错误码已定 契约本身待设计
是否可逆 能 git reset 回退 数据迁移、发布、删除

四维全绿:放心交给它。有一项为红:人来主导,它做辅助。

# 典型适合
- 按已有 Controller 模式新增一个接口
- 补单元测试、补参数校验、补日志字段
- 机械式重构:重命名、抽常量、替换已废弃 API
- 写脚本:日志分析、数据核对、批量校验

# 典型不适合
- 决定模块边界与分层方案
- 一次性且不可逆的数据迁移
- 没有测试覆盖的重构
- 排查"偶发、只在生产出现"的问题

最后一条值得展开:偶发问题依赖对真实系统的观察,它看不到你的监控、日志与流量分布,假设常常覆盖不到真正原因。


8. 工具选择的三问

不要按"哪个工具更强"选,按任务形态选。

问题 回答 指向
要不要改文件? 不改,只想问 普通对话
要不要跑命令、看输出? 要跑测试/构建/脚本 CLI 工具(Claude Code / Codex CLI)
要不要长期上下文? 跨多次任务记住约定 CLI + AGENTS.md 落盘

三个"否":随便一个聊天窗口就够,不必引入工具链。
第二问"是"而第三问"否":单次任务用 CLI,不沉淀文件。
三问全"是":需要约定文件 + 分步工作流的完整配置。

编辑器补全类工具(Cursor 的 tab)与 agent 类工具不是替代关系:前者解决"这一行怎么写",后者解决"这个任务怎么做"。用 agent 做逐行补全,成本高且慢。


9. 排错:判断失误的复盘

现象 真实原因 该怎么改
它把一个简单需求做成了大改造 把执行任务当成了设计任务 明确"只做最小改动,不要抽新抽象"
输出的架构方案看着合理但用不了 让它做架构决策,缺项目历史 架构由人定,只让它给候选与代价
数据迁移出了不可逆错误 把不可逆操作交出去了 生成脚本,人审阅后执行
重构后行为变了 没有测试覆盖就重构 先补特征测试(characterization test)再动
约定每次都忘 只在对话里说过 落进 AGENTS.md,不要靠记忆
越改越偏 前提错误还在加提示 回退到上一个验证点,重写任务描述

倒数第二条最值得投入:凡纠正过两次以上的问题,都该变成一行约定。


10. 边界:这篇讲的范围

话题 归属
怎么组织上下文 本系列 X09
生成代码怎么验收 本系列 X10
工作流怎么编排 本系列 X11
具体工具的配置差异 按所用版本核对官方文档

三篇合起来是一个闭环:把事实讲清楚(X09)→ 把产出验明白(X10)→ 把流程排对(X11)→ 知道哪里不该用它(X12)。


11. 自测

  1. 四个边界维度中,哪些是硬边界、哪些能用工程手段补?
  2. "能编译"在验证链条上处于什么位置?至少要跨到哪一档才算验收?
  3. 为什么它会倾向输出旧版本写法?怎么纠正?
  4. 安全类失效为什么测试发现不了,防线该设在哪?
  5. 让模型标注 // ASSUME: 的实际作用是什么?
  6. 工具选择三问分别指向什么配置选择?
S24-X16 · AI 编程工具的边界与失效模式
https://www.clxhxhhr.top/posts/5973/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。