AI编程“越写越慢”?破解程序化停滞的关键在反馈闭环
如果你身边有正在大量使用 AI 编程工具的开发者你可能会观察到一种奇怪的现象代码量产出明显变快了但项目的真实进度却不升反降。功能分支越开越多AI 生成的代码块堆积在仓库里却没人能说清楚它们之间是如何协作的。半年过去Demo 还是那个 Demo离上线始终差着一层窗户纸。这个现象在英文语境里有一个非常准确的描述Programmatic Stagnation程序化停滞。它描述的不是“写不出代码”而是一种更隐蔽的困境——明明每一行代码都来得比以前更快整个系统却停在了原地。这篇文章我想认真拆解一下LLM 到底是怎么把开发者推向这种停滞的它和传统意义上的“技术债”有什么区别更重要的是那些没有被 AI 编程困住的团队到底做对了什么。文章会先从现象和机制讲起然后落到一个可运行的最小 LLM Agent 项目用代码演示“有反馈闭环”和“没有反馈闭环”两种开发方式的本质区别。最后给出工程层面的最佳实践。读完你应该能判断出自己的项目到底是处于真正的快速迭代还是正在经历一场高质量的原地踏步。1. 什么是 Programmatic StagnationProgrammatic Stagnation 不是一个教科书概念而是过去两年 LLM 辅助开发大规模普及后浮现的真实场景。它最核心的表现是开发活动的“产量”和“进展”发生了分离。在传统开发里代码量虽然不能完全代表进度但两者高度相关。你写了一个模块测试通过了接进来了这个功能就真的前进了一步。但在 LLM 辅助开发的环境里这个相关性被打破了。开发者可以在一小时内生成几百行逻辑完整、风格规范的代码但这些代码如果没有被正确地理解、验证、集成它们就只是“看起来像进度”的文本而不是“系统能力的一部分”。我把编程停滞的特征归纳为三个典型状态特征具体表现传统开发中的对应物产出高、交付低代码提交量增加可上线功能没有同比增加需求蔓延、返工重复修改同一段逻辑LLM 反复生成相似代码却总在边缘问题上绕圈缺乏设计的应急修补集成失败常态化每个模块单独看都能跑合在一起无法运行接口契约混乱这三种状态叠加起来就会产生一个让团队非常沮丧的结果大家都在加班加点地“生产代码”但系统没有在变好。真正让人警惕的是这种停滞是温柔的它不会像线上事故那样一下子爆发而是以“每天都在忙、每周都没有里程碑”的方式慢慢消耗团队。为什么传统开发里的老问题在 LLM 环境下变得格外尖锐因为 AI 让“产生代码”这个动作的成本趋近于零但它并没有让“理解代码”“验证代码”“集成代码”的成本下降。瓶颈被转移了而大多数团队的管理方式还停留在“以代码产出衡量进度”的旧模式里。这正是停滞的种子。1.1 一个真实的开发困局假设你接到了一个任务给内部知识库做一个 AI 问答助手。传统做法是设计检索方案定义接口写代码联调测试上线。每一步都有明确的产出物。如果用 LLM Agent 来做很多人会这样开始先把 LangChain 或 Spring AI 的依赖加上然后让 AI 生成一个 RAG 流程再让 AI 补充 Web 接口接着让 AI 修复编译错误。这个过程的前两天非常爽代码量以每天几千行的速度增长。但到了第三天问题开始出现向量库的召回结果不稳定Prompt 稍微改一下回答风格就漂移Agent 调工具时偶尔会传错参数。开发者开始陷入“让 AI 继续改 Prompt——修改代码——重新运行——发现问题——再改 Prompt”的循环。这个循环就是编程停滞的微观体现。表面看每一步都在推进实际上是在一个坏味道的架构上反复涂抹。等到发现问题根源是“检索模块和生成模块之间缺少结构化中间层”时已经过去了整整一周。这个例子说明LLM 不是把开发者的工作难度降低了而是把难度从“写代码”转移到了“定义边界和验证行为”。那些边界清晰、验证手段完善的团队能快速受益那些只会让 AI 不断生成代码的团队会发现自己被代码淹没。1.2 停滞和传统技术债的区别很多人会问这不就是技术债吗不完全是。传统技术债通常来自刻意的妥协比如为了赶上线先写一段硬编码你知道这里以后要重构债是被记录的。而编程停滞更像一种“隐性的复杂度膨胀”。代码看起来都很合理没有明显的坏味道但系统整体的不可预测性在增加。因为你无法通过阅读代码判断 AI 当时为什么这么写也无法预判它会在哪个边界条件下失败。这种不可预测性不表现为某一段代码很烂而表现为整个系统很难被信任。理解这个区别非常重要因为应对方法完全不同。技术债可以通过重构来偿还编程停滞则需要重新建立“生成、验证、反馈”的闭环否则重构本身也会变成一场新的 AI 生成狂欢。2. LLM 为什么会导致编程停滞要解决问题先要理解机制。LLM 导致编程停滞的原因不是单一的技术缺陷而是多个因素叠加。2.1 瓶颈转移从“写代码”到“理解与决策”在 LLM 辅助开发之前程序员的核心瓶颈是打字速度、语法记忆和框架熟悉度这些恰好是 LLM 最擅长补齐的。但当这些低层瓶颈消失后更高层的瓶颈暴露了出来你是否能清晰描述需求边界你是否能设计出可验证的接口契约你是否能判断 AI 生成的方案是不是在正确方向上前进这些能力在传统开发中也很重要但被“写代码”这个动作掩盖了。以前一个需求理解有偏差至少你要花几个小时把错误代码写出来才会发现现在AI 会在几秒内把偏差放大成几百行“看起来正确”的代码。等你发现方向错了沉没成本已经很高。这不是 LLM 的错而是使用方式的问题。把 LLM 当成一个无限快速的打字员它只会加速你冲向错误方向的速度。要避免停滞必须把重点从“让 AI 生成更多代码”转移到“在生成之前定义标准在生成之后验证结果”。2.2 上下文窗口的物理限制LLM 的核心限制之一是有限的上下文窗口。即使是最新一代模型窗口达到几十万 token也无法完整容纳一个中型项目的所有代码、文档、需求和历史决策。这意味着两种情况一种是开发者手动挑选上下文喂给模型这要求开发者对项目足够熟悉另一种是让 Agent 自主检索上下文这引入了检索质量的不确定性。无论哪种都不可避免会丢失信息。丢失信息的直接后果是AI 生成的代码可能违反了某个模块的隐含约束或者重新实现了已经存在的功能。更隐蔽的是上下文丢失会导致 Agent 的“短期记忆”问题。在长任务中Agent 可能在第 10 步时已经忘记了第 3 步做出的关键决策从而生成与之前逻辑冲突的代码。如果你没有完善的测试和文档这种冲突不会第一时间暴露而是会在未来的某个时刻引爆。2.3 表面正确与集成失败LLM 生成的代码有一个显著的特点单点正确率高集成成功率低。这是因为模型的训练目标决定它擅长生成“看起来合理的局部代码”但它并不具备对整体系统的运行期理解。我见过很多这样的场景一个 AI 生成的函数单独测试完全正常但放进生产代码里它会因为异常处理方式不一致、日志格式不统一、依赖注入方式不同而引发问题。这些问题在代码审查中很难被发现因为每一段代码单独看都是合格的。真正能发现集成问题的只有自动化测试、端到端验证和灰度发布。如果一个团队过度依赖 AI 生成代码却没有配套的验证体系那么集成失败就会变成家常便饭项目自然就陷入停滞。2.4 Agent 循环的误差放大当你把多个 LLM 调用串成一个 Agent 循环时问题会进一步放大。每个节点都有一定的出错概率而这些概率是指数级累积的。假设一个 Agent 流程有 5 个环节每个环节的成功率是 90%整体成功率只有 59%。这还只是理想情况。实际上Agent 的某个环节产生错误输出后如果错误反馈机制不完善错误会被当作“正常输入”传给下一个环节导致偏离越来越大。这也是为什么很多团队发现Agent 在 Demo 场景下表现惊艳但在真实任务中频繁失败。不是模型变笨了而是循环缺少“检查点”缺少对中间结果的验证和纠偏。要解决这个问题光靠换更强的模型是不够的关键是在 Agent 架构中增加验证节点和回退机制。3. 从“生成代码”到“推进项目”LLM 应用的正确打开方式理解了停滞的成因我们就能顺理成章地推导出解法把注意力从“生成”转移到“反馈闭环”。这一节先梳理几个关键概念然后给出一个核心判断。3.1 LLM Agent 到底是什么Agent 不是一个神秘的概念。简单说它是一个让 LLM 能够循环执行“思考、调用工具、观察结果、再思考”的软件架构。传统的单次 Prompt 调用是无状态的你问一句它答一句而 Agent 在循环中维护状态可以根据工具返回的结果调整下一步行动。同一个 LLM 模型既可以做成一个简单的聊天机器人也可以做成一个能够自主完成多步任务的 Agent。区别不在于模型本身而在于外围的循环设计。这个循环通常包含任务拆解、工具调用、结果观察、记忆管理和终止条件。Agent 真正有价值的地方是处理那些无法预先穷举步骤的任务比如“帮我调研这个开源项目的文档并整理出接入指南”。但如果任务的每一步结果都无法验证Agent 的不确定性就会变成灾难。所以 Agent 越强大你越需要一个严格的验证层。3.2 RAG 能解决什么问题RAGRetrieval-Augmented Generation把检索和生成结合起来让 LLM 在回答问题时先从一个知识库中检索相关内容再基于这些内容生成答案。它的意义在于在不重新训练模型的情况下把外部知识注入生成过程。RAG 对编程停滞的对抗体现在两个层面。第一它让 Agent 可以访问项目的完整文档和历史决策缓解上下文窗口的物理限制。第二它把“我知道什么”和“我能检索到什么”分离开来让系统表现更可控。但 RAG 不是银弹。它的效果严重依赖检索质量——如果向量化不好、切片不合理、召回率低那么生成端拿到的就是无关信息结果可能比不做 RAG 还差。很多项目刚接上 RAG 时效果不错等知识库膨胀到一定规模后开始退化这也是停滞的一种形态。3.3 编排框架为什么有必要2025 年的 LLM 应用开发已经不再是“调用一个 API”那么简单。一个典型的应用可能需要串联多个模型、工具、记忆模块和外部系统。这时候你需要一个编排框架来解决谁来调用模型、工具调用的参数怎么传、失败后怎么重试、上下文分段如何管理。开源的 LangChain、LangGraphJava 生态的 Spring AI以及越来越受关注的 MCPModel Context Protocol都是这类问题的答案。它们解决的不是“模型能不能回答”而是“多步骤逻辑如何稳定执行”。没有编排框架你当然也能写但到最后你会发现你其实是在手写一个不完整的编排框架。当然编排框架本身也在制造新的复杂度。框架抽象层太多会让问题定位变得困难。我的建议是小项目优先用最少的依赖把 Agent 循环自己写清楚当任务复杂度超过一定阈值再引入框架。3.4 核心判断LLM 应用的本质是反馈闭环现在可以给出这篇文章最核心的判断LLM 应用开发的真正内核不是模型选择也不是 Prompt 技巧而是反馈闭环的设计。一个没有反馈闭环的 LLM 应用本质上是一次性的文本生成有了反馈闭环它才变成值得依赖的软件系统。反馈闭环至少包含三个要素验证机制如何判断 LLM 的输出是否正确。错误传播发现错误后如何把错误信息反馈给模型或系统。人工介入当自动反馈无法纠正时如何干净地暂停并交给人类处理。这三个要素缺一不可。没有验证机制你不知道系统在犯错没有错误传播模型会在同一个坑里反复踩没有人工介入系统会在错误轨道上越走越远。理解了这一点你就会明白为什么很多 LLM 项目会停滞——因为它们只有“生成”和“展示”没有“验证”和“纠偏”。4. 最小可验证的 LLM Agent 项目从停滞到收敛下面用一个最小示例演示反馈闭环的威力。这个例子不依赖重型框架只使用 Python 和 OpenAI 兼容接口重点展示“生成—验证—反馈—重试”的循环结构。4.1 环境与依赖建议使用 Python 3.10 及以上版本。需要安装 openai 客户端库pip install openai pyyaml这里使用 OpenAI 兼容接口是为了兼容 vLLM、Ollama 等本地推理服务。如果你使用的是在线模型的官方接口同样适用只是把base_url和api_key改成自己的配置。4.2 项目结构llm_agent_demo/ ├── agent_demo.py ├── retriever.py ├── config.yaml ├── docs.json └── requirements.txtagent_demo.py是 Agent 主循环retriever.py是简单的本地检索模块config.yaml是配置文件docs.json是知识库文档示例。4.3 配置文件创建config.yamlllm: base_url: http://localhost:8000/v1 api_key: EMPTY model: qwen2.5-7b-instruct temperature: 0.2 retriever: doc_path: ./docs.json top_k: 3 agent: max_attempts: 5 enable_validation: true enable_trace: true配置说明这里的base_url是一个本地推理服务的通用地址对应 vLLM、Ollama 或 Xinference 等工具默认的 OpenAI 兼容端点。如果你使用在线模型可以直接配置官方接口地址。temperature调低是为了让代码生成更稳定减少随机性。4.4 Agent 主循环创建agent_demo.py# 文件路径llm_agent_demo/agent_demo.py import yaml from openai import OpenAI def load_config(path: str) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def create_client(cfg: dict) - OpenAI: return OpenAI( base_urlcfg[llm][base_url], api_keycfg[llm][api_key], ) def llm_call(client: OpenAI, messages: list, model: str, temperature: float) - str: resp client.chat.completions.create( modelmodel, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content def validate_code(code: str) - str: 验证生成的代码是否可以执行并返回执行结果或错误信息。 namespace {} try: exec(code, namespace) return OK except Exception as e: return f{type(e).__name__}: {e} def run_agent(task: str, cfg: dict) - None: client create_client(cfg) model cfg[llm][model] temperature cfg[llm][temperature] max_attempts cfg[agent][max_attempts] history [{role: system, content: 你是代码生成助手每次只输出一个可执行的 Python 代码块。}] history.append({role: user, content: task}) for attempt in range(1, max_attempts 1): code llm_call(client, history, model, temperature) if cfg[agent][enable_trace]: print(f--- 第 {attempt} 次生成结果 ---) print(code) print(--- 验证 ---) result validate_code(code) if result OK: print(f验证通过共尝试 {attempt} 次) print(code) return print(f验证失败{result}) history.append({role: assistant, content: code}) history.append({ role: user, content: f上面的代码执行失败错误信息{result}。请修复后重新输出完整代码。, }) print(已超过最大尝试次数需要人工介入) if __name__ __main__: cfg load_config(config.yaml) task ( 写一个 Python 函数 gcd(a, b)计算两个非负整数的最大公约数 并用 print 输出 gcd(48, 36) 的结果。 ) run_agent(task, cfg)这段代码的核心逻辑是每次让 LLM 生成代码后立即用 Python 的exec在隔离的命名空间中执行验证。如果执行失败把错误信息反馈给模型让它基于错误信息修复。这就是一个最小的反馈闭环。4.5 RAG 检索模块创建retriever.py# 文件路径llm_agent_demo/retriever.py import json from pathlib import Path class SimpleRetriever: def __init__(self, doc_path: str): self.docs self._load(doc_path) def _load(self, path: str) - list[dict]: data json.loads(Path(path).read_text(encodingutf-8)) return data def search(self, query: str, top_k: int 3) - list[str]: scored [(self._score(query, doc[text]), doc[text]) for doc in self.docs] scored.sort(keylambda x: x[0], reverseTrue) return [text for _, text in scored[:top_k]] def _score(self, query: str, text: str) - int: q_tokens set(query.lower().split()) d_tokens set(text.lower().split()) return len(q_tokens d_tokens) if __name__ __main__: r SimpleRetriever(docs.json) for snippet in r.search(Agent validation loop): print(snippet) print(---)这里的检索实现非常基础只做关键词重叠度计算目的是演示“检索—生成”的结构不追求检索效果。真实项目中会替换为向量检索或 BM25。4.6 知识库示例创建docs.json[ { id: 1, text: Agent validation loop is the core of reliable LLM applications. }, { id: 2, text: RAG combines retrieval and generation to inject external knowledge. }, { id: 3, text: Without feedback, LLM applications may drift from the target. }, { id: 4, text: Context window limits the amount of information a model can process. } ]5. 运行结果与效果验证5.1 启动命令如果你有本地推理服务启动服务后执行cd llm_agent_demo python agent_demo.py如果没有本地服务也可以用在线模型的兼容接口只改config.yaml即可。我没有在一台具体机器上跑通并给出输出截图这里给出的是代码在不同配置下都能出现的典型行为。5.2 预期输出解读正常运行时你会看到类似下面的输出节奏--- 第 1 次生成结果 --- def gcd(a, b): while b: a, b b, a % b return a print(gcd(48, 36)) --- 验证 --- 验证通过共尝试 1 次 def gcd(a, b): while b: a, b b, a % b return a print(gcd(48, 36))如果第一次生成的代码有语法错误或逻辑错误会看到错误信息被反馈给模型第二次生成时通常能修复。这就是反馈闭环在起作用。5.3 如何判断系统没有陷入停滞这个最小示例的验证方式很粗糙只能验证代码能否执行不能验证逻辑是否正确但它已经具备了闭环的雏形。判断一个 LLM 应用是否健康可以看三个指标重试是否收敛同样的任务是否随着重试次数增加而接近成功。错误是否可诊断失败时系统能否给出可读的错误信息而不是静默失败。是否有人工兜底超过最大尝试次数后系统能否安全停止并标记为需要人工处理。如果这三个指标都满足了说明这个 LLM 应用有基本的生产可用性如果都不满足那它大概率还停留在“文本生成工具”阶段离“软件系统”还有距离。6. 常见问题与排查思路问题现象可能原因排查方式解决方案Agent 反复生成相同错误代码历史消息没有正确拼接到请求中模型看不到上一次的错误反馈打印发送给模型的完整消息列表检查 assistant 和 user 的反馈是否成对追加在每次重试后把上一次的输出和错误信息一起追加到历史消息中LLM 返回的内容不是纯代码模型被系统提示词要求输出解释文字或者温度过高导致输出漂移查看实际响应内容确认是否混有 Markdown 标记或解释文本在系统提示词中强调“只输出代码”使用较高 top_p 或降低温度必要时用正则提取代码块本地推理服务不存在或地址错误config.yaml 中 base_url 配置错误或服务未启动用 curl 测试接口是否可访问检查服务日志修正 base_url确认服务监听地址和端口验证阶段 exec 执行了危险代码代码验证使用 exec 执行任意生成代码存在安全风险检查验证环境是否与生产隔离生产环境使用 Docker 或沙箱执行验证不要在本机直接 execRAG 检索结果与问题无关关键词重叠度过低或文档切片不合理打印检索到的片段人工检查相关性改用向量检索调整切片长度增加词表或同义词扩展重试次数用尽但问题仍未解决任务本身超出模型能力或错误信息表述不够清晰人工介入查看生成历史判断卡在哪个环节拆分子任务或把问题进一步约束为更小的代码片段这里最容易被忽视的是最后一行。反馈闭环不是万能的如果模型能力确实不够闭环只会耗光重试次数。这时候正确的做法是缩小任务粒度而不是让 Agent 在一个错误方向上反复尝试。7. 工程最佳实践如何避免 LLM 项目停滞前面几节讲完了概念和最小示例这一节给出一套可以落地的建议。这些建议来自对多个 LLM 应用项目的观察不一定每条都适用于你的场景但值得作为检查清单。7.1 写代码前先定义验收标准这不是口号而是硬性要求。在让 LLM 生成任何代码之前先回答三个问题这段代码的输入是什么输出是什么怎样算正确把答案写进任务的 Prompt 里或者写进代码仓库里的验收标准文档中。缺少验收标准会导致 LLM 生成的结果“看起来合理但无法判定对错”反馈闭环就无从建立。有了明确的输入输出和验证条件哪怕验证方式很简单也能让生成过程从随机搜索变成定向收敛。7.2 为 Agent 添加轨迹和审计日志LLM 应用的一个常见问题是“不可解释”。Agent 做了一连串操作最后失败了但你不清楚是哪个环节出的错。轨迹trace就是解决这个问题的。在每个 Agent 循环中记录下模型输入、模型输出、工具调用参数、工具返回值、验证结果。不要只记成功链路失败的中间结果更重要。这些日志是排查问题的第一手资料也是未来优化 Prompt 和检索策略的依据。7.3 用单元测试约束生成代码如果你用 LLM 生成业务代码最好的约束不是更好的 Prompt而是测试。把测试写成明确的代码让 LLM 生成“能通过测试的实现”而不是“看起来正确的实现”。这样做的好处是测试既定义了验收标准又提供了错误反馈。当 LLM 生成的代码失败时测试的失败信息比 LLM 自己判断“哪里不对”要可靠得多。这也意味着逆向的反馈闭环内建在开发流程中。7.4 控制上下文文档化与检索上下文窗口有限这是物理限制不能靠“更长的窗口”根治。最优策略是让模型只获取它当前步骤真正需要的信息。这就需要一个维护良好的项目文档库以及一个好用的检索模块。很多团队忽视文档建设以为有 AI 就不需要写文档了。实际上在 LLM 时代文档变得更加重要。因为 AI 不能像资深工程师那样在脑子里保存整个项目的隐含知识它必须依赖外部化的知识库。这也是 Karpathy 提到的 LLM Wiki 范式背后的逻辑把知识固化到可检索的文档中让 LLM 在需要时能快速拿到。7.5 数值精度与推理稳定性关于模型推理的精度问题值得单独提醒。在本地部署模型时FP32、FP16、BF16 各有适用场景FP32 最稳定但显存占用高FP16 速度快但可能出现精度丢失BF16 在动态范围和显存占用之间相对平衡。如果 Agent 在特定输入上行为异常可以先检查推理服务使用的数据类型尝试切换精度验证是否为数值问题。不过精度问题通常不是 LLM 应用停滞的首要原因。真正需要警惕的是把精度问题当成一个筐什么异常都往里装。大部分停滞来自系统设计和反馈闭环的缺失而不是模型数值不稳定。7.6 生产环境的回滚与灰度LLM 应用天然具有不确定性所以生产环境必须有过硬的安全措施。对于 Agent 生成的代码或内容一定要在隔离环境验证后再应用到生产对于 Agent 的自动操作要设置权限边界不允许默认拥有最高权限对于涉及外部系统变更的操作先走审批流程。回滚能力也同样重要。当一次 Prompt 修改导致线上质量下降时要能快速切回上一版本。这意味着模型配置、Prompt 版本、检索策略都应该纳入版本管理而不能只存在于聊天记录或某个开发者的脑海里。7.7 承认人工介入的必要性最后一条建议也是最容易被忽视的一个成熟的 LLM 应用必须设计人工介入的入口。不要把 Agent 设计成“全自动直到成功”的闭环那只会让错误在无人知晓的情况下累积。优秀的 Agent 设计应该在以下时刻主动停下来连续重试超过阈值、验证结果置信度过低、工具调用出现异常、或者任务涉及高风险操作。停下来并向人类报告比硬着头皮继续尝试更安全也更专业。8. 总结与后续学习方向这篇文章的核心判断可以浓缩为一句话LLM 时代真正的分水岭不是你能不能生成代码而是你能不能验证代码并形成反馈闭环。Programmatic Stagnation 描述的不是工具的无能而是使用工具的人和组织在代码产出剧增时丢失了“推进项目”的能力。从实践角度看你需要做三件事第一给每个 LLM 生成任务定义明确的验收标准第二在 Agent 循环中增加验证、错误反馈和人工介入机制第三把项目知识文档化、检索化而不是依赖模型记住一切。这三件事都不是模型能力问题而是工程问题。后续值得深入的方向包括Agent 编排框架的选型与取舍、RAG 检索质量评估、MCP 协议下工具调用的标准化、长上下文场景下的记忆管理以及模型精度对应用稳定性的实际影响。你可以从本文的最小示例出发先给它加上单元测试验证再替换为向量检索最后接入真正的 Agent 编排框架一步步在实践中理解反馈闭环的价值。如果你的项目已经连续几周没有可量化的进展不要急着换模型也不要把所有时间花在调 Prompt 上。先停下来盘点一下你的系统有没有验证机制错误能不能被看见失败能不能回退这些问题的答案往往比“换个更聪明的模型”更能决定项目能否从停滞中走出来。