先聊点实在的:AI 编程智能体是这两年最容易被高估,也最容易被低估的东西。被高估,是因为总有人把它想象成“一句话生成整个项目”的魔法;被低估,是因为只要理解了它的核心能力边界,把它当成一个有手有脚但偶尔犯迷糊的实习生来带,它在实际工程里的提效幅度会非常夸张。这篇文章我想把“智能体”这个概念从神话里拽回来,围绕 AI 编程智能体的核心能力做一个系统拆解,再把那些天天被混着用的词——大模型、RAG、工作流、多智能体、平台搭建和代码自建——放在同一张桌子上做一次概念辨析。适合三类人看:正在用 AI 辅助写代码的程序员、计划把智能体接进业务系统的工程师、以及面试前想理清 Agent 脉络的候选人。
1. 智能体到底“智能”在哪:先拆掉迷思
1.1 从一次性问答到闭环执行,核心差异是“有反馈回路”
普通的大模型对话,本质是一个“输入-输出”的一次性过程:你问它一个问题,它给一个回答,这件事就结束了。就算你让它写一段代码,它也是生成一段文本交给你,然后由你自己去运行、报错、粘贴回来、再重新生成。整个链路中并不存在模型主动观察结果、调整策略的机制。
智能体改变的正是这个链路。它的最小工作单元是一个带反馈的循环:感知当前状态,做出决策,调用工具行动,然后观察行动结果,再基于新状态做下一轮决策。这个循环持续进行,直到任务完成或达到终止条件。我用一个生活化类比解释:传统大模型像是给你一本菜谱,写得再详细,菜也得你自己做;智能体则是请了一个帮厨,它自己买菜、洗菜、切菜、下锅、尝咸淡,发现咸了还会自己加点水再煮一轮。
这个“反馈回路”看起来只是工程结构上的小改动,实际影响非常大。一次问答是静态的,一个循环是动态的。动态意味着模型可以处理过程中出现的意外——测试挂了、文件不存在了、接口返回格式变了——它有机会重新规划路径,而不是把所有情况都押在第一次输出上。这也是为什么很多公司宁可牺牲一点首次生成的准确率,也要把产品从“聊天框”重构成“智能体循环”,因为真实任务从来不是一次调用能完成的。
1.2 四项核心能力:规划、记忆、工具使用与反思
如果把智能体拆到不能再拆,几乎所有有价值的实现都绕不开四项能力:规划、记忆、工具使用、反思。
规划能力决定了智能体能不能把一个大目标拆成有序的小步骤。比如“给这个仓库加上登录功能”这种任务,直接让模型写代码,它容易生成一堆没有落点的文件;但一个有规划能力的智能体会先列出:读现有目录结构、找路由配置文件、设计用户表、写鉴权中间件、生成登录页、跑测试。规划不是生成一段大纲就完了,它要在执行中根据反馈动态调整顺序。目前主流的实现方式还是 ReAct 循环的变体,也就是“思考-行动-观察”交替进行,每一步都由模型决定下一步干什么。
记忆能力解决的是“干着干着忘了初衷”的问题。它分好几层:上下文窗口内的短期记忆,负责存放当前任务的对话历史;长期记忆通常用向量库或结构化存储,负责跨会话的项目知识、用户偏好、既往决策;还有一层工作记忆,负责当前任务中哪些文件改过、哪些测试还没跑、哪些假设已经被推翻。很多翻车现场不是模型不聪明,而是记忆层没设计好,导致它每一步都在重新发明轮子。
工具使用能力是智能体从“只会说”变成“会做”的关键。代码解释器、shell 命令、文件读写、API 请求、数据库查询,这些都是智能体的“手”。工具的本质上是一个带有明确输入输出契约的函数:模型负责决定调用哪个工具、传什么参数,系统负责执行并返回结果。这个契约设计得好不好,直接决定任务成功率,后面我会专门写示例。
反思能力则是最容易被忽略的一项。优秀的智能体在失败后不会原样重试,它会先解释失败原因,再提出一个不同的方案。比如测试失败了,它不会把同一段代码改个格式再交上去,而是会去看报错栈里的真正原因,是空指针、类型不匹配还是断言写错了。反思机制在工程上通常表现为自我批判指令,或者让一个单独的审查模型对主模型的输出做交叉检查。
1.3 会调用工具不等于会干活,能力边界比想象中更清晰
这里我必须泼一盆冷水:一个智能体看起来能调用工具,离真正能干活还有很长的路。我见过不少 Demo,演示时让智能体写一个斐波那契函数并执行成功,台下掌声一片;可一旦把它丢进一个真实的多模块项目里,让它修改一个跨文件功能,它就彻底失灵了。问题出在哪里?不是工具调用能力不行,而是任务分解能力和长程一致性撑不住。
写一个斐波那契函数,本质上是一次小规模生成任务,上下文里塞下题目和期望输出就够了。但“在多模块项目中加一个功能”这个任务,涉及先理解现有架构、找到正确的修改点、评估影响面、同步修改多个文件、补齐测试、确认没有破坏已有功能。这中间每一步都不是特别难,难的是把信息在长流程里保持正确:第一步读到的目录结构,要能支撑到最后一步的决定;第二次改的文件,不能和第一次改的逻辑冲突。
所以我对编程智能体的能力边界有这样一个判断:它擅长的是有明确验收标准的、步骤可以被反复验证的任务;它不擅长的是那种“说了个大概、验收也靠感觉”的开放式任务。你让它“优化一下性能”,它大概率会做一些花拳绣腿;你让它“把 /api/users 接口的响应时间从 800ms 降到 200ms,并跑完回归测试”,它反而能给你一套像样的工程操作。把任务描述得越接近验收单,智能体的表现就越稳定。
2. 编程智能体的核心能力:从“写代码”到“做工程”
2.1 代码生成只是基本功,真正的分水岭是代码库理解
如果只看代码生成质量,今天很多大模型之间的差距已经在缩小。真正拉开档次的是代码库理解能力。一个只接受零散代码片段的工具,和一个能理解整个仓库结构、知道哪些文件互相依赖、能顺着函数调用链追根溯源的智能体,使用体验完全不同。
代码库理解这件事,工程上并不是把整个仓库塞进上下文。以常见做法为例:系统先对代码库做索引,包括文件路径、目录结构、函数与类定义、import 关系、关键符号位置;检索时结合向量检索和结构化符号索引,找到与当前任务相关的代码片段。设计检索参数时有一个经验值可以参考:快照切块大小设在 400 到 800 token 之间,切块之间保留 50 到 100 token 的 overlap,这样能避免函数定义恰好被拦腰切断;检索返回的 topK 控制在 5 到 15 个片段之间,太少不够信息,太多会稀释模型的注意力。
为什么不能整库灌入?因为 token 是成本和性能的共同瓶颈。一个中型仓库动辄几十万行代码,就算模型窗口支持几十万 token,真正把它全部放进去后,模型的注意力会被大量无关文件分散,检索效率反而下降。更合理的做法是按需取用:任务开始先给智能体一个目录树和项目说明,它觉得需要看哪个文件,再调用 read_file 具体读取。这和我们人写代码时的习惯一样,没有人会一口气读完整个项目的源码再动手。
2.2 上下文管理:窗口是物理限制,能力靠“管”出来
模型上下文窗口再大,也是物理限制。而且窗口越大,token 成本越高,响应延迟越高。所以智能体系统的设计重心,很大程度不是在“扩展窗口”,而是在“管理窗口”。
我常用的管理手段有三种。第一种是摘要压缩,把早期的对话历史定期压缩成一段结构化摘要,保留关键决策和未完成事项,丢弃无关细节。第二种是分层记忆,把项目级知识、任务级进度、当前步骤的代码片段分成不同优先级,按需载入。第三种是分文件载入,大型文件只读取相关函数或类,而不是整个文件。
举个具体的计算例子:假设你的模型上下文窗口是 200K token,听起来很宽裕,但一个包含依赖信息、项目说明、工具定义、历史对话的智能体系统,基础开销可能在 8K 到 15K token。再加上一次检索返回的 10 个代码片段,以及随后多轮工具调用的输入输出记录,几轮下来就容易逼近 40K 到 50K。如果任务规模更大,不加管理的对话很容易在两三步之内把窗口塞满。所以我在搭智能体时,会在循环的每个关键节点都做一次 token 盘点:当前消息数多少、历史里哪些消息已经不再需要、哪些摘要可以替代原文。
2.3 执行与反馈闭环:能跑起来看结果,才算闭环
编程智能体和通用问答智能体的最大不同,在于它有一个确定的执行环境:代码必须真的能跑。这意味着智能体不能只生成“看起来对”的代码,它要通过执行命令、观察退出码、解析报错信息来验证自己的产出。
一个像样的闭环长这样:生成代码 -> 写入文件 -> 运行测试 -> 收到失败输出 -> 解析错误位置 -> 修改代码 -> 重跑测试。这个循环的终止条件是测试通过或达到最大步数。执行环境通常是沙箱容器,命令需要设置超时时间,避免某一命令卡死整个任务。
针对反复失败的情况,我推荐在重试机制上加点指数退避。比如同一操作连续失败时,第一次失败等待 1 秒,第二次 2 秒,第三次 4 秒,最多到 5 次就停下来让智能体换一个策略。这个设计的逻辑是,如果同一个方案连续失败,说明问题不是偶然的,继续快速重试只会浪费 token,不如强制冷静一下,强制它重新思考。另外,测试驱动在这种闭环里特别好用:让智能体先根据需求写测试,再写实现,之后每一次修改都由测试结果来评判。测试就是验收标准,有了测试,智能体的自我反思才有客观依据。
2.4 多文件修改与一致性维护:项目级编辑的暗坑
单文件编辑,智能体很少出大问题;一旦涉及多文件修改,各种一致性灾难就来了。最常见的场景是重构:一个函数改名,调用它的十几个地方都要同步更新。很多智能体只会修改你明确指出的那个文件,剩下十几个文件里仍然引用旧函数名,测试一跑报错一大片。
要解决这个问题,第一步是变更影响面分析。在动手之前,智能体应该先检索所有引用了目标符号的位置,评估改动涉及哪些文件。这可以通过代码搜索工具实现,类似 grep 或者基于语法树的引用查找。第二步是全局性修改策略,同一逻辑的修正必须一次性应用到所有相关位置,而不是逐个文件碰运气。第三步是独立验证,修改完成后不仅要跑整体测试,还要针对改动涉及的文件单独确认。
我在实践中发现,给智能体一个“操作手册”类文档能显著降低这类问题。手册里写明项目的文件组织方式、常用命令、编码规范、关键模块之间的依赖关系。智能体每次开工前先读一遍手册,再开始检索代码,它的行为会明显更有章法。这背后的原因很简单:模型需要先建立全局心智模型,才能做出局部修改;没有这个模型,它就是一叶障目。
3. 概念辨析:把这几个高频混合词一次性理清
3.1 智能体 vs 大模型:模型是大脑,智能体是“大脑加身体”
这个区别听起来简单,但实践中大量产品把二者混为一谈。大模型是一个静态的推理引擎,它接收文本输入,输出文本或结构化数据;智能体是一个包含模型、工具、记忆、循环控制的完整系统。模型是智能体的决策核心,但不是智能体本身。
怎么判断你用的系统是不是智能体?标准很简单:看到一次模型输出后,是否有外部工具被调用并影响下一轮决策。如果你只是调用一次 API 并展示返回结果,那是模型,不是智能体。如果你让模型决定是否调用一个搜索工具,并且根据搜索结果继续推理,那才是智能体。这个区分能解释很多争议:有人说“大模型没有推理能力”,那是在说模型本身;但智能体系统可以通过工具验证、多轮反思弥补模型的弱点,整体表现远超裸模型。
3.2 智能体 vs RAG:一个偏行动,一个偏记忆
RAG(检索增强生成)的核心思路是:在模型生成之前,先从外部知识库检索相关内容,把检索结果拼进上下文,再让模型生成。它解决的是“模型不知道”的问题,本质上是一个记忆增强装置。智能体的核心是“行动”,是决定调用什么工具、怎么处理结果。
现实中二者经常组合使用,所以更容易被混为一谈。一个带 RAG 的客服问答系统,是典型的知识库系统,用户问题进来、检索、拼接、回答就结束了;它不需要规划子任务,不需要调用外部工具,也不需要根据反馈修正。一个智能体系统则可能是这样:先检索代码库理解现有实现,然后规划修改,调用执行工具,看到报错再检索新的信息,这个过程里 RAG 只是其中一个记忆模块,而不是系统的全部。
辨别方法同样直接:整个过程是否只有一次“检索-生成”链路?如果是,就是 RAG;如果检索只是多项决策中的一项,并且执行结果会反馈回系统引发新的检索或操作,那就是智能体。
3.3 智能体 vs 工作流:固定流水线还是动态路由
工作流是预先画好的流程图:步骤 A 完成后必做步骤 B,分支条件也提前定义好。智能体是动态路由:每一步该做什么,由模型在执行时根据当前状态决定。一个形象的对比是工厂流水线和外卖骑手:流水线的工序固定不变,骑手会根据实时路况决定先送哪单、走哪条路。
这二者不是替代关系,而是成本与可控性的权衡。如果你的业务流程非常固定,比如“接收工单 -> 提取关键词 -> 查数据库 -> 生成回复模板”,用工作流就足够,而且结果可预期、成本低、容易排查。如果业务场景多变,比如“把一段含糊的需求转化为可运行的代码并自行验证”,固定流程根本写不全面,这时候智能体的动态决策才有价值。
实践中很多团队犯的错,是把简单固定的事做成智能体,白白增加不稳定性和成本;反过来又把真正需要动态决策的任务硬编码成工作流,结果模型步骤错一次就全盘崩掉。我的建议是先画流程:能明确画出所有分支的,用工作流;分支多到画不完的,才交给智能体。
3.4 单智能体 vs 多智能体:什么时候需要团队作战
多智能体系统这几年非常热门,概念上也很性感:一个负责写代码,一个负责审查,一个负责跑测试,模拟一个真实团队。但我想先强调一句:多智能体的成本是爆炸式增长的,通信开销、上下文复制、协调难度都会上涨,且模型之间的互相“审核”有时候只是把错误换了一个方向。
什么时候值得上多智能体?我认为是任务内部存在天然的角色隔离,并且每个角色需要不同的工具权限、不同的上下文聚焦点时。比如一个大型代码重构任务,研究型智能体负责梳理调用链,编码型智能体负责修改,审查型智能体负责检查风格与逻辑一致性。分工能减少单一智能体的上下文污染,每个角色维护自己关心的那部分信息。
如果任务规模不大,比如改一个函数、写一个脚本,单智能体永远是更优选择。多智能体不是银弹,它是一个在特定复杂度区间内才划算的架构选项。判断标准也很简单:单智能体处理时,上下文是否频繁互斥?如果它既要读很多资料又要频繁执行命令,还要同时记住修改结果,确实有点吃不消;但如果只是读读写写,就折腾多智能体纯粹是给自己加戏。
3.5 平台搭建 vs Python自建:选型对照与决策逻辑
现在市面上一堆低代码/无代码智能体平台,也有不少人坚持用 Python 手搓智能体。这两条路线不是谁取代谁,而是约束条件完全不同。我做了一张选型对照表,方便直接抄:
| 维度 | 平台搭建 | Python 自建 |
|---|---|---|
| 上手速度 | 快,几小时能出原型 | 慢,需要搭建基础循环和工具链 |
| 可控性 | 受平台规则限制 | 完全可控,想怎么改怎么改 |
| 扩展性 | 依赖平台生态,自定义工具有限 | 可接入任意工具、任意数据源 |
| 私有化部署 | 多数平台不支持或需企业版 | 可完全私有化 |
| 调试便利性 | 平台自带日志与可视化看板 | 需要自己打日志、做监控 |
| 成本 | 按量付费,省去运维 | 需要自己管理模型成本与资源 |
| 适合场景 | 客服、知识问答、快速原型 | 深度集成、私有化、复杂业务系统 |
我的决策逻辑是这样的:如果你要解决的问题是一个标准化的场景,比如知识库客服、信息整理类、内容生成类,先看平台能不能满足。平台的价值在于省掉了基础设施和运维。但如果你要的智能体需要深度依赖企业内部系统、需要精细控制每一步的工具调用和权限边界、需要离线部署,那就老老实实走 Python 自建。记住一个原则:平台搭建是买一辆货拉拉,Python 自建是自己造皮卡,货物和路线决定工具,而不是工具决定路线。
4. 从零搭一个最小可用的编程智能体:实操记录
4.1 技术选型:模型、框架和循环结构怎么定
模型层的选择首先要考虑工具调用能力。当前能稳定支持 function calling 的模型并不算特别多,选模型时要看它的工具调用格式是否规范、是否支持并行工具调用、在长流程中的指令遵循能力如何。商用模型和开源模型各有取舍,核心看你的数据敏感性和预算。
框架层我的态度比较克制。新项目上手,先别急着套大型框架。一个最小智能体循环的本质就是一个 while 循环加工具映射表,几百行代码就能实现。当你发现手写循环解决不了复杂状态管理、多智能体协作、人工介入控制等问题时,再考虑上 LangGraph、CrewAI 这类框架。框架解决的问题是标准化的状态机和编排,但它也会带来学习成本和抽象泄漏。简单任务用框架,常常是背着抱着一样沉。
循环结构上,我建议先实现一个最朴素的版本:模型决定是否调用工具,系统执行工具并把结果返回给模型,然后循环。不要一开始就搞复杂的规划器、反思模块、记忆系统。先把主循环跑通,后面一项项加能力,每加一项都做一次评测对比,确保它真的是在改进而不是给你增加排查负担。
4.2 一个最小的Agent循环:代码骨架与执行细节
下面这个骨架是我在多个项目里验证过的最小实现,核心逻辑集中在 run_agent 函数中:
def run_agent(task, tools, max_steps=10): messages = [{"role": "user", "content": task}] for step in range(max_steps): resp = llm.chat(messages=messages, tools=tools) messages.append({"role": "assistant", "content": resp.content, "tool_calls": resp.tool_calls}) if not resp.tool_calls: # 模型没有请求工具,说明它认为任务已经完成 return resp.content for call in resp.tool_calls: result = execute_tool(call["name"], call["arguments"]) # 关键点:把工具执行结果作为 role=tool 的消息放回对话 messages.append({ "role": "tool", "tool_call_id": call["id"], "name": call["name"], "content": format_result(result) }) return "已达到最大步数,任务未完成"这段代码看起来简单,但有几个细节直接决定成败。第一个细节是,工具执行结果必须原样返回对话,而不是只把模型决定的工具调用参数塞回去。模型需要看到真实的世界反馈:命令执行成功没、输出是什么、退出码是多少,这些信息才是下一轮决策的依据。第二个细节是返回结果时要带上元信息:工具名称、执行耗时、退出码。别小看这些,模型在判断“这个方案是否已经反复失败”时,非常依赖这些元数据。第三个细节是 max_steps 一定要给上限,宁可让任务失败,也别让它陷入无休止的循环。
举个具体的场景,让智能体自动修复单元测试失败。给它一个任务描述:“运行 pytest,解析失败原因,修复源码,直到测试全部通过。每次修改后都要重跑测试。”然后循环里的行为应该是:第一步调用 run_command 执行 pytest --tb=short -q,拿到失败信息;第二步调用 read_file 读取测试文件和被测源码;第三步调用 write_file 修改代码;第四步再次调用 run_command 执行 pytest。这一步一步的行动不是预写死的,而是模型根据步骤 1 的失败信息自己决定下一步该读哪个文件、改哪个位置。你只负责提供工具和终止条件。
4.3 工具定义与权限边界:把“手”的安全绳绑好
编程智能体的工具通常包括三类:文件操作、命令执行、代码检索。工具定义是一门手艺活,尤其要重视 JSON Schema 里的 description 字段。模型靠它来判断该调用哪个工具、怎么填参数,写得太草率,模型的工具选择就会飘。
拿命令执行工具举例,一份可用的定义长这样:
{ "type": "function", "function": { "name": "run_command", "description": "在项目沙箱中执行shell命令,返回stdout、stderr和退出码。仅用于构建、测试和代码 check 类命令,禁止执行删除、重置等破坏性操作。", "parameters": { "type": "object", "properties": { "command": { "type": "string", "description": "需要执行的完整命令,例如 pytest --tb=short -q" }, "timeout": { "type": "integer", "description": "超时秒数,默认30,最大120" } }, "required": ["command"] } } }这份描述里我特别加了“禁止执行删除、重置等破坏性操作”,这是安全边界的第一个闸门。第二个闸门是执行环境,所有命令都要在沙箱或容器内运行,容器的文件系统是临时的,宿主机只通过挂载卷暴露项目目录。第三个闸门是权限校验,工具执行函数里先做命令白名单检查,发现危险命令直接拒绝并返回错误信息。第四个闸门是写文件先备份,智能体要修改文件时,系统先把原文件复制到 .backup 目录,一旦后面发现改了不该改的,能立即回滚。
4.4 评测与回归:怎么判断智能体真的在变好
没有评测的智能体迭代,都是在原地折腾。我的做法是准备一个小型基准任务集,包含 15 到 20 个任务,覆盖几类典型场景:生成新函数、修改已有逻辑、修复测试失败、重构变量名、跨文件调整。每个任务都提前写明验收标准,比如“有正确输出”“测试通过”“没有破坏其他测试”。
每次改动智能体系统后,把整个基准集完整跑一遍,记录几个指标:任务通过率、平均步数、平均 token 消耗、工具调用失败率。这些指标的变化比任何主观感觉都有说服力。比如你刚给系统加了记忆模块,通过率从 60% 升到 75%,说明方向是对的;如果通过率没变,token 消耗涨了 30%,那这个模块可能只带来了成本没带来能力。
记录表格我一般长这样:
| 任务 | 通过 | 步数 | token数 | 失败原因 |
|---|---|---|---|---|
| 修复test_login失败 | 是 | 5 | 12340 | - |
| 实现分页函数 | 否 | 10 | 22800 | 超过最大步数 |
| 重构User类字段名 | 是 | 8 | 18500 | - |
这套评测机制还有一个额外好处:它本身就是最好的回归测试。有时候你以为改了一个无关紧要的提示词,结果某些任务的表现反而下降,有了基准集能及时发现这种隐性退化。
5. 高频翻车点与排查思路:真实踩坑记录
5.1 上下文漂移:干到一半忘了初始需求
症状非常典型:任务开始时智能体目标明确,步骤一多它就开始跑偏。比如用户要求“只改 /api 下的接口,不要动数据库表结构”,跑了五步之后,模型开始给你改模型层代码了。原因很简单,早期指令被越来越多的工具输出挤出了注意力中心,或者被摘要压缩丢了细节。
排查方向有三个:先看历史消息长度是否已经过长,如果超过窗口一半就应当做压缩;再看摘要压缩逻辑是否正确保留了硬性约束;最后检查每一轮开始时是否重新灌入了“当前目标”。我自己在循环里注入的清单是这样:当前目标是什么、已完成哪些关键步骤、下一步待办有哪些、不可触碰的红线是什么。每轮开始前用一小段结构化文本把这些信息压进上下文,上下文漂移的概率会明显下降。
5.2 工具调用参数错乱:不是模型笨,是契约没写清
我见过很多智能体失败,表面上是“模型不会调用工具”,点开日志发现工具定义写得极其潦草。比如有个项目里,read_file 工具的参数只写了“file_path”,没有说明路径是相对于项目根目录还是绝对路径;execute_command 工具不写超时上限,模型就频繁默认传一个很短的超时导致命令被掐断。
这类问题的排查顺序是先检查工具定义有没有歧义,再检查参数名是否和模型习惯一致,再看是否给了足够的调用示例。工具定义里多写一句“路径相对于项目根目录,不要包含前导斜杠”就能省掉大量返工。更根治的办法是让模型在调用工具前先复述一遍自己的调用计划,虽然多消耗一些 token,但经常能拦住低级错误。
5.3 死循环与重试风暴:别让智能体原地打转
死循环是编程智能体最耗钱的失败模式。常见表现有两种:一种是对同一命令反复执行,每次都收到同样的失败,然后继续执行;另一种是修改代码后测试失败,修改后再失败,循环十几次直到撞上最大步数。前一种是“重试风暴”,后一种是“无效修改循环”。
解决思路是给循环加多层保险。第一层是最大步数限制,这个前面已经说过。第二层是相同错误去重:如果模型在连续 N 轮内收到完全相同的错误输出,直接中断并提示“你已经尝试过相同方案,换一个思路”。第三层是强制反思:连续失败两次后,在下一轮开始前注入一段系统消息,要求模型先总结失败模式,再提出与之前不同的方案。这里的关键词“不同”很重要,模型如果不被强制,很容易在同一个方案上打转。
5.4 幻觉式改代码:在“空文件”上空转
模型幻觉在编程场景下的表现特别坑:它会在没有读取文件的情况下,笃定地说“我已经修改了 config.py”,实际上那个文件根本不存在;或者它引用了一个从未定义过的函数,然后写下调用它的代码。这种错误常发生在检索没有真正被约束、或者模型跳过信息收集直接生成结果的场景。
要压制幻觉,一个有效手段是流程约束:在工具权限上,write_file 执行前必须先执行一次 read_file 或 search_symbols,如果系统检测到当前会话没有该文件的有效读取记录,直接拒绝写入。另一个手段是在修改后接入静态分析工具,让语法检查器或 linter 跑一遍,把真实错误返回给模型。这两个手段配合起来,能大幅减少“对着空气改代码”的情况。
5.5 成本失控:每一步都在烧token
编程智能体的 token 消耗远远超出普通对话。原因也很直白:一次任务往往要经过“读文件、写文件、跑命令、看报错”多个环节,而每个环节的输入输出都要完整走一遍模型。最贵的一类循环是频繁让模型重新读取大文件,一个 5000 行的文件读一遍就是几千 token,同一个文件读三遍,成本直接翻三倍。
省钱的基本功是控制检索范围:检索 topK 别贪多,返回的代码片段限制行数;读取大文件时只读相关函数区间,而不是整个文件;历史消息里的旧工具结果及时压缩成摘要。更进阶的做法是按工具结果大小做裁剪:命令输出只保留最后 2000 字符,报错栈只截取与本次修改相关的部分。这些裁剪本质上没有损害模型需要的信息,但省下的 token 量很可观。
5.6 问题速查表:从症状到解法的快速路径
| 症状 | 可能原因 | 快速解法 |
|---|---|---|
| 任务中途忘记原始要求 | 早期指令被挤出注意力 | 每轮注入目标清单,压缩历史时保留硬约束 |
| 同一命令反复失败 | 缺少去重机制 | 对相同错误计数,连续N次后强制换策略 |
| 工具参数格式错误 | JSON Schema描述不清 | 补充参数说明与调用示例 |
| 代码引用了不存在的符号 | 跳过了检索直接生成 | 强制先检索再生成,写入前做语法检查 |
| 修改一个文件破坏了另一个 | 缺少全局影响面分析 | 增加引用搜索,修改前枚举所有依赖点 |
| 上下文被工具输出塞满 | 工具结果未做裁剪 | 限制输出长度,旧结果转摘要 |
| 任务卡在最大步数 | 任务拆分不合理或规划能力弱 | 拆小任务,补充中间验收点 |
6. 实操体会与两个小建议
我个人实际用下来最深的体会是,判断一个编程智能体强不强,不要看它能不能写出漂亮的算法,而要看它连续受挫五次之后还能不能定位问题、重新规划路径。第一次成功是模型能力,第五次成功才是系统能力,后者才是工程上真正值钱的东西。
最后分享一个我一直在用的小技巧:给自己的智能体准备一份“项目操作手册”文档,内容不用长,几百字就够,写上构建命令、测试命令、目录结构、编码规范、关键模块依赖关系。每次新任务开始时,让智能体先读一遍这个文档,再开始干活。我对比测过,加了这一步之后,智能体在多文件仓库里的任务成功率明显提升,因为它在动手之前就建立了一个基本的全局心智模型。这比任何花哨的框架配置都管用。