刚接手一个跑了六年多的老仓库时,图形化插件基本帮不上忙——它能补全当前光标下的一行代码,却没法帮我搞清楚OrderService在三个模块里到底被谁引用过、改动会不会牵连到那条没人敢碰的结算链路。真正的转折点是我把工作流挪到了终端里,用终端 AI 编码助手去做跨文件检索、批量改法和脚本化批处理。这篇文章记录的就是从安装配置、模型接入到实际跑起来之后的完整经历。前半段关于环境和权限的坑,属于装完就想卸载的那种;后半段关于模型后端替换、上下文管理和工具调用的坑,则是跑通 Demo 之后才真正开始难受的部分。适合两类人看:一类是刚准备在终端里搭起 AI 编码工作流、还在纠结运行时和权限报错的,另一类是想把默认模型后端换掉、又不想被兼容性细节反复折磨的。下面所有内容都来自实际配置过程,参数和配置项我会给出可直接照抄的样例,但具体取值请按自己环境调整。
1. 终端编码助手真正解决的是哪类问题
1.1 图形化补全和终端代理式编码的分界线
很多人对 AI 编码工具的认知停留在“写完一行按 Tab”,这其实是补全模型的能力边界。终端里的编码助手是另一种东西:你给它一句自然语言任务,它自己去读文件、grep 关键字、拼上下文、改代码、跑测试,最后把 diff 摆在你面前。两者的差别不在模型大小,而在能动性——补全工具只能看见光标附近几百个 token,终端助手能主动决定去看哪个文件、看多少行。
这个差别决定了它们擅长的任务完全不同。补全型工具在“我知道要写什么,只是懒得敲”的时候效率极高;终端助手在“我知道要什么结果,但不知道从哪下手”的时候才有价值。举个具体例子:要把一个散落在 12 个文件里的硬编码超时时间统一抽成配置项,补全工具只能一个一个文件帮你敲,终端助手可以一次性 grep 出所有timeout=的字面量,读一遍上下文判断哪些是 HTTP 超时、哪些是数据库连接超时,然后分批给出改法。
理解这条分界线很重要,因为它决定了你对工具的预期。我见过不少人装完终端助手之后第一件事是让它“帮我写个 React 组件”,然后觉得不如编辑器插件好用——这属于用错了场景。真正能把这类工具发挥出价值的,是那些需要跨文件理解、需要重复执行同样改法、需要命令行反馈闭环的任务。
1.2 三个我实际用得最多的场景
场景一是老仓库的定向重构。接手一个没有文档、测试覆盖率不到 20% 的项目,最痛苦的从来不是写新代码,而是判断“我改这个方法会不会炸”。终端助手的价值在于它可以先把调用链梳理出来,用 grep 加文件读取拼出一张调用图,再基于这张图给建议。这个过程比我手动翻快三到五倍,而且它不会漏掉那些藏在测试文件、脚本文件里的调用。
场景二是批量机械改法。比如把项目里所有moment()的调用换成原生Date,或者把回调式 API 全改成 Promise。这类任务的特点是模式高度重复、单点判断简单、但文件数量多到人会烦躁。让助手先生成三五个文件的改法给我审,确认模式没问题后再让它批量跑,这个节奏最稳。
场景三是脚本化的一次性任务。像“扫一遍日志目录,把昨天所有 5xx 请求的 request id 提出来,按接口分组统计”,这种任务写脚本要十分钟,描述一遍只要三十秒。终端助手的优势是它能直接调用 shell,把grep、awk、jq这些串起来,中间出错了还能自己调整。
这三个场景有个共同点:任务的正确性可以被快速验证。改完能跑测试,脚本跑完能看输出,重构完能看 grep 结果。这也是我判断什么时候该用它、什么时候该自己动手的核心标准——如果一件事的结果我无法在五分钟内验证对错,我就不太敢让助手全自动跑。
1.3 选择前先想清楚的三件事
第一件是数据边界。终端助手默认会把文件内容发给模型后端,如果仓库里有密钥、用户数据、内部协议文本,你必须提前把排除规则配好,而不是等出事再补。这件事在后面第 5 节我会给出具体的排除文件写法。
第二件是模型后端的可控性。默认后端通常按调用计费,长任务一次跑下来可能几十上百次请求,成本曲线不线性。所以我在配置阶段就把“哪些任务用强模型、哪些任务用便宜模型”想清楚了,并且把这个策略固化进配置文件,避免每次都靠自觉。
第三件是失败时的降级路径。助手卡在工具调用死循环里出不来,你有没有办法立刻中断?会话上下文被污染了,能不能回滚到干净状态重新开?这些在 Demo 阶段不会暴露,真正在改生产相关的代码时才知道多重要。我后来养成的习惯是:任何超过十个文件改动的任务,先让助手输出计划、我审完再让它执行,中间每完成一批就打个 commit,出问题直接 reset。
2. 安装阶段的三个卡点:运行时、权限与命令可见性
2.1 运行时版本这道坎,比想象中硬
绝大多数终端编码助手是基于 Node 生态分发的,包本身可能不大,但它依赖的东西不少:内置的fetch、AbortController、结构化克隆,还有一堆 ESM 相关的特性。这些都意味着运行时有硬性下限。我第一台机器上是 Node 16,装完之后clx --version能跑,但一发起请求就报fetch is not defined,排查了半小时才意识到是版本问题。
提示:安装前先跑
node -v,低于 18 的不要抱侥幸心理,直接升到 20 LTS。中间版本看似能装,实际会在某个你想不到的环节炸。
升级方式我推荐用版本管理器而不是系统包管理器。系统包管理器装的 Node 通常会被其他项目依赖,升级一次可能把别的工具链带崩。用 nvm 之类的版本管理器切版本,是成本最低的方案:
# 安装版本管理器后 nvm install 20 nvm use 20 nvm alias default 20 node -v # 应该输出 v20.x.x最后那行nvm alias default很关键。我踩过一次:当前 shell 里nvm use生效了,安装也成功了,第二天开新终端又找不到命令,因为没设默认版本。这种问题没有报错信息,纯粹是环境没配好,最耗人心态。
2.2 全局安装的权限报错,别用 sudo 硬顶
安装全局包时最常见的一条报错长这样:
npm ERR! code EACCES npm ERR! syscall mkdir npm ERR! path /usr/local/lib/node_modules/xxx npm ERR! errno -13直觉反应是加sudo,命令行确实会立刻成功。但这是我在整个配置过程中最后悔的一个操作。sudo npm i -g装出来的目录归 root 所有,之后每次升级、每次装同前缀下的其他包都要 sudo,而且某次升级失败后残留的 root 权限文件会让后续所有非 sudo 安装全部报错,清理起来极其麻烦。
正确做法有两个方向。一是让全局目录归当前用户所有,二是把全局 prefix 指向用户目录。我选了后者,因为它不依赖系统目录结构,迁移机器时只改一行配置:
mkdir -p ~/.npm-global npm config set prefix ~/.npm-global # 然后把下面这行加进 shell 的 rc 文件 export PATH="$HOME/.npm-global/bin:$PATH"改完source一下配置文件,再装就不会有权限问题了。已经用过 sudo 装坏的同学,先执行sudo chown -R $(whoami) /usr/local/lib/node_modules /usr/local/bin把所有权拿回来,再按上面的方式改前缀。
2.3 装完提示 command not found 的三种成因
这类问题表面上症状一样,根因完全不同,排查顺序建议按下面来:
| 现象 | 可能根因 | 验证方式 | 处理 |
|---|---|---|---|
| 新开终端找不到命令 | PATH 未写入 rc 文件 | echo $PATH看有没有全局 bin 目录 | 补 export 语句到 rc |
| 当前终端找不到但新终端可以 | 未 source 新配置 | source ~/.zshrc后重试 | 重新 source |
| 配置文件改了仍不生效 | 改错了 rc 文件 | echo $SHELL确认 shell 类型 | 改对应 rc 文件 |
第三条是最容易翻车的。macOS 默认 shell 是 zsh,但很多人习惯性把配置写到.bash_profile里,然后对着一个完全不读这个文件的环境反复排查。echo $SHELL一秒钟就能确认,别跳过这步。
还有一类特殊情况和权限相关:包已经装到~/.npm-global/bin,PATH 也配了,但执行时报Permission denied。这通常是之前的 sudo 安装残留了一个同名但权限异常的可执行文件,用which clx看下实际解析到哪个路径,删掉旧的那个再重装。
2.4 依赖拉取慢:镜像源怎么配才不出新的坑
安装慢基本是包源解析的问题。换成国内镜像源是标准操作,但这里有个容易踩的细节:不要全局替换所有 registry。有些内部私有包挂在私有 scope 下,全局替换之后npm i @internal/xxx直接 404,而且报错信息不会告诉你是源的问题,只说不存在。
我的做法是只改默认源,私有 scope 单独保留:
# 只改默认源 npm config set registry https://registry.npmmirror.com # 私有 scope 指回内部源(按实际地址填) npm config set @your-scope:registry https://your-internal-registry/另外,安装失败时先别急着反复重试,先清一下缓存。有些时候是包元数据拿到了但 tarball 下载中断,缓存里留了半截文件,重试一百次都是同一个错误:
npm cache verify # 仍然失败再考虑 npm cache clean --forcecache clean --force要慎用,全量清理之后下次安装所有依赖都得重新下载,在慢网络下是灾难。先verify是更稳妥的顺序。
3. 换后端模型:协议、鉴权与能力对齐
3.1 为什么优先走兼容协议而不是私有 SDK
把默认模型后端换成国产模型,第一件事是决定接入方式。厂商通常会提供两套东西:自家的原生 SDK,和一套兼容主流协议的 HTTP 接口。我强烈建议走兼容协议这条路,哪怕它有些细节不那么完美。
理由很实际。走兼容协议,你换模型供应商时只需要改三个环境变量,配置文件、提示词、项目约定文件全部不用动。走原生 SDK,意味着你要改代码、重新编译、重新测试整套工具调用链路,换一次供应商的成本可能抵得上半个月的调试时间。而且兼容协议的边界是清晰的——它只承诺某几个接口的字段结构,不承诺额外特性,你反而知道自己的依赖面有多大。
代价是有些模型独有能力用不上,比如某些特殊的思考模式参数、自定义的工具描述格式。但就我实际的编码场景来说,这些能力的收益远小于供应商锁定的成本。我更愿意把不同模型当成可替换的算力单元,用配置层做路由。
3.2 配置的优先级:环境变量、配置文件、命令行参数
这类助手工具的配置通常有三个来源,优先级从高到低大致是:命令行参数 > 环境变量 > 用户配置文件 > 项目配置文件。这个顺序本身没什么问题,问题在于很多人只配了一层,然后被另一层覆盖了还不知道。
我遇到过最典型的状况:在配置文件里写了模型名,命令行跑起来却一直用默认值。排查半天发现环境变量里有一行旧的三月前导出的CLX_MODEL,因为写在 rc 文件里,每次开终端都自动设置,把配置文件的值顶掉了。这类问题的排查方式是打印所有相关环境变量:
env | grep -i clx看到不该存在的就unset掉,并且去 rc 文件里把那一行删干净。别只在当前会话 unset,下次开终端又回来了。
我现在的习惯是把所有配置集中在一个地方——用户级配置文件,环境变量只用来做临时覆盖(比如临时切一个便宜模型跑批量任务),不用来做持久配置。这样"配置文件是唯一事实来源",排查时不用猜。
3.3 模型名、上下文长度与 max_tokens 的对齐
这三个参数之间的约束关系,是接入阶段最容易出错的地方。
先从模型名说起。国产模型普遍存在“对外展示名”和“API 调用名”不一致的情况,配置文件里必须填 API 调用名,填错了就是 404 或者model not found。有些平台还区分聊天模型和推理模型,名字只差一个后缀,复制粘贴时特别容易漏。
然后是上下文长度和输出长度的关系。假设某模型标称上下文 128K,这不代表你能让它一次输出 128K 的内容。绝大多数实现里,输入加输出共享这个总预算,而单次输出还有独立上限(常见的 4K、8K)。如果你把max_tokens设成和上下文一样大,很可能直接收到一个参数校验错误。
注意:判断一个模型是否够用,别看标称上下文,看“单次输出上限”和“实际可用的输入预算”。一个 64K 上下文、8K 输出的模型,处理一个两万行文件时仍然会截断,因为输入这一侧还要给对话历史、系统提示、工具定义留位置。
我的经验值是这样:改单个函数,输入预算 8K 足够;跨文件重构,至少留 32K 给输入;全仓库分析这种任务,别指望一次做完,拆成多轮。max_tokens我一般设成任务预期输出的两倍,宁可有富余,因为输出被截断时返回的是半截 JSON,解析失败之后排查成本远高于多花的那点 token。
温度参数在编码场景里建议压低,0.1 到 0.3 之间比较稳。代码改动需要确定性,温度高了同一个任务跑两次给出不同方案,你会很难判断到底哪次是对的。
3.4 工具调用能力的真实水位
终端助手和普通聊天模型的本质区别在于工具调用(tool use / function calling)。助手能不能自己读文件、跑命令,完全依赖模型输出结构化的工具调用请求,工具执行完再把结果塞回对话。这条链路上任何一环不合规,整个 agent 循环就会卡住。
实际接入时我遇到过三类兼容性问题,按出现频率排序:
第一类是工具调用的 JSON 不严格。模型想调用read_file,正常应该输出{"path": "./src/a.js"},但它有时会输出带注释的伪 JSON、或者把参数写成自然语言描述。助手工具的解析器一旦严格校验,就会认为这不是有效调用,转而把这段文本当普通回复展示给用户——你看到的现象是助手“只说不做”。
第二类是思考型模型的字段处理。带推理能力的模型会额外返回一个思考内容字段,和最终回答分开。有些实现要求你在后续对话里把这个字段原样带回去,有些则明确禁止回传,还有的只在流式响应里给。处理错了的表现是:第一轮正常,第二轮开始报 400 参数错误。这个坑我在第 4 节会展开。
第三类是流式响应的结束标记。标准实现会在流末尾发一个明确的终止事件,但不是所有后端都遵循。缺少终止事件时,客户端会一直等,直到超时。表现是每个请求都要卡满超时时间才返回,看着像网络慢,实际是协议没对齐。
3.5 一份能直接跑的最小配置
绕开上面这些问题的最简做法,是先做一个最小闭环验证:单轮对话能通、工具调用能回、流式能结束。配置文件长这样(路径和字段名按你实际工具调整,这里用通用命名):
{ "apiBase": "https://api.example.com/v1", "apiKey": "sk-xxxxxxxx", "model": "chat-model-name", "maxTokens": 8192, "temperature": 0.2, "requestTimeout": 120, "streaming": true }对应的环境变量覆盖方式:
export CLX_API_BASE="https://api.example.com/v1" export CLX_API_KEY="sk-xxxxxxxx" export CLX_MODEL="chat-model-name"验证顺序建议严格按这个来,不要跳步:
curl直接打一次/chat/completions,确认基础对话通- 带一个极简的
tools定义再打一次,确认返回里有结构化的工具调用 - 加上流式参数,确认最后能正常结束
- 回到助手工具里跑一个“读一个文件并总结”的任务
第 1 步不通就调第 2 步,纯属浪费时间。我见过有人协议字段都没对齐就开始调工具调用,最后在错误方向上耗了一整个下午。curl那一步虽然笨,但它把变量压到只剩三个:地址、密钥、模型名,任何一个不对都能立刻定位。
4. 跑通之后的坑才是真坑:会话、工具与成本
4.1 上下文超限的四种表现
上下文超限这件事之所以难排查,是因为它不总是抛一个清晰的错误。我记录过四种不同表现:
第一种是明确的 400 错误,报context length exceeded,这种最好处理,系统会告诉你要砍多少。第二种是静默截断——请求成功返回,但模型显然没看到后半段内容,给出的答案基于不完整的输入。第三种是助手自己把旧消息丢掉,你觉得它应该记得前面说过的约束,它却忘得一干二净。第四种最阴险:请求成功,但响应时间异常长,因为你把接近上限的输入发给了一个排队严重的后端。
应对方式分两层。工具层要把会话历史做压缩,长对话超过一定轮次自动摘要;使用层要养成“一个任务一个会话”的习惯。我现在的原则是:切换任务类型就开新会话,不要在同一个会话里既改代码又问部署问题。历史里那些无关的上下文会持续消耗预算,还会干扰模型判断。
具体到重构场景,我用的策略是分段喂。先让助手列出所有需要改的文件清单(这一步只要 grep 结果,开销小),然后按文件逐个处理,每次只把当前文件和相关的那几个调用点放进上下文。这样单轮输入控制在可控范围内,也方便中途出问题只回滚一小块。
4.2 工具调用格式不符导致的死循环
这是最消耗时间和额度的一类问题。表现是助手反复尝试同一个工具调用,每次失败,然后重试,UI 上看起来一直在“思考中”。
我的排查链路是这样的。第一步,把助手的详细日志打开,看每次工具调用的原始返回。第二步,找到失败的那次调用,看模型的输出和工具期望的入参结构差在哪。第三步,判断是模型能力问题还是参数描述问题。
多数情况属于第三类。你给工具写的参数描述太模糊,模型不知道该填什么格式。比如一个edit_file工具,参数只有path和content,模型就会自己发明一个old_string和new_string的结构,然后因为字段不匹配被拒。解决办法是把工具描述写得极其具体,把必填字段、格式样例、常见错误都写进去。
还有一种情况是模型输出了多段 JSON,解析器只取了第一段。表现是工具调用成功了,但改的位置不对。这类问题在批量修改时特别危险,因为它不报错,只是改错地方。我的做法是:任何批量修改前先看 diff,别直接接受。养成这个习惯之后,我至少拦下过三次“把测试文件里的字符串也一并替换了”的事故。
提示:工具调用的参数描述里,把“不要做什么”也写清楚。模型对否定式指令的遵守程度,比你想象中好。比如明确写“一次只调用一个工具,不要并行输出多个调用”。
4.3 文件写入与 diff 应用失败
改代码改不动,报的错却是“文件写入失败”或者“patch 应用失败”,这中间隔着一堆可能。
最常见的是行尾符问题。仓库在 Windows 上克隆下来是 CRLF,助手的 diff 基于 LF 生成,应用时对不上行,整段 patch 被拒。这个问题的排查方式是看失败文件的行尾:
file ./src/problem.js # 或者用 od 看具体字节 od -c ./src/problem.js | head -5处理方式不是让助手去适配,而是统一仓库配置。加一个.gitattributes明确文本文件的换行规范,让所有人拉下来都是同一套行尾。这类环境层面的问题,用工具层面去绕,绕不完的。
第二常见的是编码问题。带 BOM 的 UTF-8 文件,助手按纯 UTF-8 读取,第一个字符就会变成乱码,后续所有偏移全错。表现是 patch 应用成功但文件开头多了几个奇怪字符,或者第一行被改坏。批量处理前先扫一遍:
find . -name "*.js" -exec sh -c 'head -c 3 "$1" | od -An -tx1 | grep -q "ef bb bf" && echo "$1"' _ {} \;第三类是文件权限和只读挂载。容器里跑的助手对挂载目录只有读权限,所有写入都会失败,但错误信息可能只说“操作失败”。ls -l看一眼目录权限,一秒钟的事。
4.4 token 消耗失控的排查链路
有一次跑批量重构,我以为几十块钱够了,结果账单是预估的五倍。复盘下来,问题出在三个地方叠加。
第一,没有排除目录。助手在做全文搜索时把node_modules、构建产物目录、日志目录全扫了一遍,每次搜索的结果都整个塞进上下文。一个仓库光dist目录就有十几万行,白白烧掉大量输入 token。
第二,工具调用结果没有做摘要。读文件的工具每次返回全文,一个两千行文件读十次就是两万行历史。后来我把读文件工具改成支持行范围参数,并且默认只返回前若干行,需要再读再取。
第三,失败重试没有上限。工具调用失败后助手会重试,如果失败原因是环境问题,这个重试会一直持续到会话预算耗尽。
对应的处置是三件事,都在配置层解决:
# .clxignore 与 .gitignore 语法一致 node_modules/ dist/ build/ coverage/ *.log *.min.js package-lock.json再加一个项目级约定文件说明“搜索时要排除的目录”,以及在工具配置里把单文件读取上限和重试次数写死。这三条做完之后,同类任务的消耗降到了原来的三分之一左右。
我后来还加了一个习惯:跑长任务时先做个估算。让助手先只执行“列出涉及的文件”,看它返回多少个,乘以单文件平均消耗,心里有个数再决定要不要让它继续。这个动作花不了半分钟,但能避免很多失控。
5. 让它在团队里真正可用:项目约定与任务分流
5.1 项目根目录的约定文件该写什么
助手每次启动都是“失忆”状态,对项目一无所知。项目根目录放一个约定文件,是把这个“入职培训”写下来,让它每次进来先读。
内容不要贪多,写四类就够,多了反而稀释重点:
第一类是构建和测试命令。npm run test还是pnpm test、单元测试怎么跑单个文件、集成测试需要不需要先起依赖服务。不写这些,助手会去猜,猜错的概率很高。
第二类是代码风格里无法从文件推断的部分。比如“新增文件必须加类型标注”“异步函数统一用 async/await,不要用回调”“工具函数放在src/utils下而不是src/lib”。
第三类是禁止触碰的目录和文件。迁移脚本、生成的协议文件、有历史包袱不能动的老模块,列出来。这是整个文件里最有价值的部分,因为它直接避免事故。
第四类是常见任务的执行方式。比如“改完代码后必须跑一次 lint”“涉及数据库 schema 的改动要在 PR 描述里注明”。
我的经验是这份文件控制在 100 行以内,超过这个长度,模型对后半段的遵守度会下降,而且维护成本高。定期清理比一次写全更重要,我基本上每两周删一批已经固化进 lint 规则的条目。
5.2 按任务难度做模型分流
把所有任务都交给最强的模型,成本会失控;都用便宜模型,复杂任务的质量又不够。我的做法是配置两套模型,手动切或者写简单脚本自动切。
| 任务类型 | 模型选择 | 理由 |
|---|---|---|
| 文件定位、关键字搜索、格式转换 | 轻量模型 | 不需要推理,只要输出准确 |
| 单函数改写、单测生成 | 中等模型 | 需要理解上下文但范围有限 |
| 跨文件重构、架构分析 | 强模型 | 需要长链路推理 |
| 长文档摘要、日志分析 | 中等模型,分批 | 上下文消耗大,靠拆批控制 |
切换方式我倾向用环境变量临时覆盖,不改配置文件。批量任务前 export 一个便宜模型,跑完 unset 掉,配置文件里的默认值不动。这样不会出现“忘了改回来”的问题。
还有一个反直觉的观察:轻量模型配合好的提示词,在很多任务上能追上强模型。比如“把这段代码里的 console.log 全部改成统一的 logger 调用”,这种任务的关键不是推理能力,是你把 logger 的导入路径、调用签名、参数格式写清楚了没有。提示词里给一个完整的改前改后示例,轻量模型的准确率会明显上升。
5.3 权限边界:哪些操作不能给它自动执行
功能越强的工具越要设边界。我的红线上有三条,都是实打实踩出来的。
第一条,不允许自动执行任何有副作用且不可逆的命令。删除文件、重置 git 状态、推送远程、改数据库,这些必须由我确认。实现方式是在配置里把危险命令加入拒绝列表,让助手发起调用时直接拒绝,而不是执行完再问我。
第二条,不允许跨出仓库目录。默认工作目录限制在当前项目根目录下,避免它读取到用户主目录下的配置文件、密钥文件。有些工具支持配置“可访问根目录”,一定要显式设成项目路径,不要用默认的家目录。
第三条,不允许把敏感文件内容发出去。除了.clxignore排除之外,我还会在约定文件里明写“.env、证书、密钥文件一律不读不写”。双保险,因为排除规则总有配错的时候。
关于自动执行的程度,我建议从最保守的做起:所有写操作先展示 diff,我确认后才落盘。用一段时间,把那些稳定不出错的场景逐步放开,比如单元测试文件的新增。这个渐进过程大概花了我两周,之后才敢让它在改完代码后自动跑 lint。
6. 上线前的自检清单和几个私藏技巧
6.1 一份照着打勾的配置检查表
每次换机器或者换项目,我都按这个顺序过一遍,能省掉大量重复排查:
| 检查项 | 命令 / 位置 | 期望结果 |
|---|---|---|
| 运行时版本 | node -v | 20.x 或以上 |
| 全局目录归属 | ls -ld ~/.npm-global | 当前用户可写 |
| 命令可解析 | which clx | 指向用户目录下的 bin |
| 环境变量无残留 | env | grep -i clx | 只有预期的几项 |
| 后端连通 | curl打一次对话接口 | 返回正常结构 |
| 流式终止 | 观察请求耗时 | 不卡满超时 |
| 排除规则生效 | 让它搜node_modules | 搜不到 |
| 危险命令拦截 | 让它删个测试文件 | 被拒绝 |
表格里最后两项最容易被跳过,但恰恰是它们决定了用起来稳不稳。排除规则没生效,你会在某次账单上看到答案;危险命令没拦截,答案可能在git reflog里。
6.2 会话管理的两个土办法
第一个办法是给会话起名。多数工具支持会话列表,功能都一样但名字默认是时间戳,找起来费劲。我改成按任务命名,比如refactor-order-timeout、debug-ci-flaky-test。一个项目做下来有十几个会话,靠名字能三秒钟找到当时怎么想的。
第二个办法是重要结论落盘。会话里讨论出来的方案、试过但失败的思路,我会让助手直接写进仓库根目录的一个notes文件里。会话会丢,文件不会。尤其是“这条路走不通,原因是 X”这类信息,下次遇到同样问题时能省一整轮试错。
6.3 我踩过最亏的一次:以为改完了
有次让它批量把一个模块里的同步 IO 改成异步,三十多个文件,它跑完之后我看 git status 一堆改动,直接提交了。CI 挂了才发现,有四个文件的改动只改了一半——调用点改成await了,但函数定义没加async。助手当时报告的是“已完成”,因为它把这些文件当作成功处理,而实际上写入时遇到了语法错误被它自己跳过,日志里有但被正常输出淹没了。
教训是两条。一是批量改动必须跑验证,哪怕只是一个语法检查,node --check逐文件过一遍成本极低。二是不要只看助手的完成报告,要看它实际改了什么,git diff --stat一眼就能看出改动的文件数是否符合预期。
后来我把这条固化成了流程:批量任务分三批跑,每批完成后跑一次 lint 加单元测试,通过再跑下一批。慢一点,但不会有“提交完才发现”的惊吓。
6.4 提示词上的一个小改动,效果比换模型明显
最后分享一个投入产出比很高的调整。我在给助手的任务描述里,固定加了一句:“先输出你的执行计划,等我确认后再动手。”
这一句话带来三个变化。第一,它不再一上来就乱改文件,而是先列出打算读哪些文件、改哪些位置。我能在这一步拦下方向错误。第二,计划本身暴露了它对任务的理解是否准确,理解偏了一眼就能看出来。第三,有个明确的确认节点,长任务不会一口气跑到失控。
代价是每个任务多一轮交互。但对比之前那些“跑完发现方向错了、全部回滚重来”的经历,这一轮的性价比高得离谱。尤其在新项目上,我宁愿多花这一分钟。
关于成本,我还发现一个细节:在计划阶段用便宜模型做,确认之后再切到强模型执行,效果和全程强模型差别不大,但费用能压下来一截。计划阶段本质上是结构化输出,不需要多强的推理;执行阶段才需要。这个组合我用了几个月,是目前最满意的一套配置。
这套工作流跑顺之后,它在我这里的定位慢慢从“代码生成工具”变成了“能自己去看代码的执行者”。真正的门槛从来不在模型能力上,而在你有没有把环境、边界、验证这三件事配好。配置这一步花的时间,会在后面每一次任务里省回来。
最后再提一句配置维护的事:把整套配置连同排除规则、约定文件一起放进版本控制,换机器的时候直接拉下来,能省掉重走一遍上面所有坑的时间。这个仓库我现在已经攒了快两百行注释,每一行背后都是一次排查。