ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

编程尚未被解决:LLM、AI Agent与Harness工程的能力边界

编程尚未被解决:LLM、AI Agent与Harness工程的能力边界

1. 为什么“编程尚未被解决”是个值得认真对待的判断

先把话说直白一点:编程这件事,到今天为止,依然没有被真正解决。我们有了更快的编译器、更聪明的编辑器、能补全整段函数的模型,甚至能根据一句自然语言描述生成一个可运行的小项目,但如果你真的在一线写过几年代码,就会知道——写代码从来不是最难的,难的是把模糊的需求变成精确的规格,再把精确的规格变成可维护、可验证、可演进的系统。这个链条上的每一环,目前都还依赖人的判断。

我最近在几个项目里密集使用 Claude Code、DeepSeek Harness 这类工具,也在团队内部推动过 LLM 辅助单元测试、AI 编程提示词规范、多 AI 协作流程。实测下来,效率提升是真实的,但“编程被解决”这个说法,至少现在还站不住脚。原因不复杂:LLM 擅长的是局部模式补全,而编程的本质是全局约束满足。这两者之间的鸿沟,不是靠堆参数就能填平的。

这篇文章想聊的,就是围绕“编程尚未被解决”这个判断,把 LLM、AI Agent、Harness 工程、Claude Code 这类工具的真实能力边界拆开来看。我会从整体设计思路、核心细节、实操过程、常见问题四个层面展开,尽量把每个“为什么”讲清楚。适合谁看?如果你正在用 AI 编程工具,或者正在团队里推 AI 辅助开发流程,又或者你只是好奇“大模型到底能不能替代程序员”,这篇内容应该能给你一些来自一线的、不粉饰的观察。

提示:本文提到的所有工具和流程,都是基于公开可获取的通用实践总结,不涉及任何特定平台或敏感场景。具体配置请以你本地环境为准。

2. 整体设计思路:LLM 编程能力的真实边界在哪里

2.1 从“补全”到“约束满足”:两种完全不同的任务

要理解为什么编程还没被解决,先要理解 LLM 到底在做什么。大模型的核心能力是基于上下文的概率性 token 预测。你给它一段代码前缀,它预测下一个最可能出现的 token;你给它一段自然语言描述,它预测最可能对应的代码片段。这个过程本质上是模式匹配和局部补全。

但编程任务是什么?是给定一组约束——输入输出规格、性能要求、内存限制、并发安全、错误处理、向后兼容、团队代码规范——然后找到一个满足所有约束的解。这是一个约束满足问题,而且约束之间经常互相冲突。比如你为了性能用了缓存,但缓存带来了数据一致性问题;你为了可读性拆了函数,但拆完之后调用链变长、调试变难。

LLM 在局部补全上很强,因为它见过海量代码模式。但它在全局约束满足上很弱,因为它没有真正的“全局状态”概念。它看到的只是你喂给它的上下文窗口,窗口之外的东西它一概不知。这就是为什么你让 AI 写一个函数,它写得很好;你让它改一个跨十个文件的 bug,它经常改着改着就引入新问题。

2.2 Harness 工程:把 LLM 装进“脚手架”里

正因为 LLM 有这个短板,才出现了 Harness 这个概念。Harness 和 Agent 的区别,很多人搞不清楚。简单说,Agent 是“谁来做”,Harness 是“怎么做”。Agent 是一个能感知环境、做决策、执行动作的实体;Harness 是包裹在 LLM 外面的那层工程脚手架,负责给 LLM 提供上下文、约束输出格式、验证执行结果、处理错误重试。

我自己的理解是:Harness 就是给 LLM 修的“护栏”和“传送带”。护栏保证它不跑偏,传送带保证它能把任务一步步往前推。DeepSeek Harness 这类工具的核心价值,不在于模型本身多聪明,而在于它把任务分解、上下文管理、结果验证、错误恢复这些工程环节做成了可复用的流程。

为什么这个思路重要?因为单靠 LLM 的原始能力,你只能做“一问一答”式的代码生成。但真实编程任务是多步骤的、有状态的、需要反复验证的。Harness 工程把这些步骤串起来,让 LLM 在一个受控的循环里工作。这就像给一个很有天赋但没耐心的实习生配了一个项目经理,项目经理负责拆任务、检查进度、处理异常,实习生只管写代码。

2.3 Claude Code 的定位:编辑器里的“结对伙伴”

Claude Code 这类工具,定位很明确:它不是要替代你,而是要做一个随时在线的结对编程伙伴。你在 VS Code 里写代码,它在旁边看着,你问它问题它回答,你让它改代码它改,你让它解释一段逻辑它解释。它的优势是上下文感知——它能读你当前打开的文件、你项目里的其他文件、你的终端输出,甚至你的 git 历史。

但它的局限也很明显:它不知道你脑子里想的那个“最终目标”是什么。你让它优化一个函数,它可能优化得很好,但这个函数所在的模块下周就要被删掉了。你让它加一个功能,它加得很漂亮,但这个功能和你们团队正在做的架构重构方向是冲突的。这些信息不在代码里,在人的脑子里。所以 Claude Code 再强,它也只能做“局部最优”,做不了“全局最优”。

注意:不要把 AI 编程工具当成“自动程序员”。它更像是一个反应很快、知识面很广、但缺乏项目全局视野的助手。你的角色是架构师和决策者,它的角色是执行者和建议者。

3. 核心细节解析:LLM 编程的四个关键环节

3.1 上下文管理:为什么“喂对信息”比“模型多大”更重要

用 LLM 编程,第一个要解决的问题就是:给它看什么。上下文窗口再大,也是有限的。你把整个项目几万行代码全塞进去,模型反而会迷失在细节里,抓不住重点。我试过把整个模块的代码一次性喂给模型让它找 bug,结果它花了大量 token 在无关的注释和空行上,真正的问题反而没找到。

正确的做法是分层喂入。第一层是任务描述:你要它做什么,输入是什么,输出是什么,有什么约束。第二层是相关代码:只给和任务直接相关的文件和函数,不要给整个项目。第三层是示例:如果团队有代码规范,给一两个符合规范的例子。第四层是验证方式:告诉它你会怎么验证结果,比如跑哪些测试、检查哪些边界条件。

这个分层策略背后的逻辑是:LLM 的注意力是稀缺资源。你给它的信息越精准,它用在关键推理上的注意力就越多。这就像你给一个新同事交代任务,你不会把公司所有文档都扔给他,你会告诉他“看这三个文件,改这个函数,注意这个边界条件”。

3.2 提示词工程:AI 编程提示词到底该怎么写

网上有很多“AI 编程提示词模板”,但大部分都太泛了。我实测下来,真正有效的提示词要包含五个要素:角色、任务、约束、示例、验证。

角色不是“你是一个资深程序员”这种空话,而是更具体的定位,比如“你是一个熟悉 Python 异步编程和 FastAPI 的后端工程师”。任务要精确到函数级别,不要写“优化这个模块”,要写“把process_data函数的执行时间从 O(n²) 降到 O(n log n),保持输入输出不变”。约束要明确列出,比如“不能引入新的第三方依赖”“必须保持线程安全”“错误处理要沿用现有的AppError异常体系”。示例给一两个符合团队规范的代码片段。验证方式告诉它你会怎么检查,比如“我会跑test_process_data.py里的所有用例”。

为什么这五个要素缺一不可?因为 LLM 没有“常识”意义上的项目上下文。你不告诉它约束,它就会按自己的偏好来,可能引入一个你根本不用的库。你不告诉它验证方式,它就无法自我检查,生成的结果可能连语法都不对。

3.3 结果验证:LLM 说“完成了”不等于真的完成了

这是我最想强调的一点:永远不要相信 LLM 的自我报告。它说“我已经修复了所有问题”,你要当成“它认为自己修复了所有问题”。这两者之间可能差着十万八千里。

我踩过的最大的坑,就是让 AI 改一个并发相关的 bug,它改完之后信誓旦旦说“已确保线程安全”,结果我跑压力测试,直接死锁。后来我学乖了,所有 AI 生成的代码,必须过三道关:第一道是静态检查,跑 linter 和类型检查;第二道是单元测试,跑现有测试用例,看有没有回归;第三道是人工审查,重点看边界条件和错误处理。

这三道关里,最容易被忽略的是第一道。很多人觉得 AI 生成的代码“看起来没问题”就直接用了,结果 linter 一跑,一堆未使用变量和类型不匹配。静态检查能在几秒钟内发现的问题,不要留到运行时。

3.4 多 AI 协作:什么时候需要,什么时候是过度设计

“多 AI 协作”是最近很热的概念,但我个人的经验是:大部分场景下,单模型加好的 Harness 就够了。多 AI 协作适合的是那种需要不同视角的任务,比如一个 AI 写代码,另一个 AI 专门找 bug,第三个 AI 专门写测试。这种“红蓝对抗”模式在安全审计和代码审查场景下确实有效。

但如果你只是写一个 CRUD 接口,搞三个 AI 互相 review,纯属浪费时间。多 AI 协作的代价是通信开销和一致性维护。两个 AI 对同一个问题的理解可能不一致,你需要额外的工作来协调它们。所以我的建议是:先用单模型加 Harness 跑通流程,遇到明确的瓶颈——比如模型总是忽略某类错误——再考虑引入第二个 AI 专门补这个短板。

4. 实操过程:从零搭建一个 LLM 辅助编程工作流

4.1 环境准备:Claude Code 安装与 VS Code 配置

先讲最基础的:怎么把工具跑起来。Claude Code 的安装方式取决于你的操作系统。在 Ubuntu 上,通常是通过包管理器或者官方提供的安装脚本。安装完成后,你需要在 VS Code 里装对应的扩展,然后在设置里配置 API 密钥或者本地服务的地址。

这里有个细节很多人会忽略:工作目录的隔离。不要让 AI 工具直接在你的主项目目录里跑,最好给它一个独立的沙箱目录,把需要它处理的文件复制进去。这样做的好处是,万一它执行了破坏性操作——比如误删文件或者改了不该改的配置——你的主项目不受影响。我一般会在项目根目录下建一个.ai-workspace文件夹,专门给 AI 工具用。

配置完成后,先跑一个最简单的任务验证链路:让它读一个文件,然后回答一个关于这个文件的问题。如果这一步都跑不通,后面的复杂流程就不用试了。

4.2 任务分解:把大需求拆成 LLM 能处理的小块

这是整个工作流里最考验人的一步。LLM 不擅长处理大而模糊的需求,所以你必须先把需求拆成原子任务。什么叫原子任务?就是输入明确、输出明确、验证方式明确的任务。

举个例子,需求是“给用户模块加一个批量导入功能”。这个需求对 LLM 来说太大了。拆解之后可能是:第一,定义导入数据的格式和校验规则;第二,写一个解析 CSV 的函数;第三,写一个批量插入数据库的函数,要处理重复数据和事务;第四,写对应的单元测试;第五,写 API 接口。每个子任务都可以单独交给 LLM,每个子任务都有明确的验证方式。

拆解的原则是:每个子任务的输出不超过 200 行代码,且能独立测试。超过 200 行,LLM 的出错率会明显上升;不能独立测试,你就无法验证它做对了没有。

4.3 执行与迭代:一个完整的 AI 辅助编码循环

有了原子任务,就可以进入执行循环了。我的标准循环是五步:描述、生成、检查、修正、提交。

描述阶段,把任务描述、相关代码、约束、示例、验证方式一起喂给 LLM。生成阶段,让它输出代码。检查阶段,先跑静态检查,再跑单元测试。修正阶段,如果检查不通过,把错误信息反馈给 LLM,让它修正。提交阶段,人工审查通过后,提交到版本控制。

这个循环里,最关键的是修正阶段的反馈质量。你不能只说“报错了,改一下”,你要把完整的错误堆栈、你期望的行为、实际的行为都告诉它。反馈越具体,它修正得越快。我试过只给一句“测试没过”,它改了三次都没改对;后来我把完整的 pytest 输出贴给它,它一次就改对了。

4.4 参数选择:温度、上下文长度、重试次数的经验值

这些参数没有绝对的最优值,但有一些经验范围。温度方面,写代码建议用低温度,0.1 到 0.3 之间,保证输出的确定性。温度太高,它每次生成的代码风格都不一样,你没法维护。上下文长度方面,不要贪多,够用就行。我一般控制在任务相关代码的 2 到 3 倍长度,留出空间给任务描述和示例。

重试次数方面,我建议最多 3 次。如果 3 次修正之后还是不对,说明要么任务拆得不够细,要么这个任务超出了模型的能力范围,需要人工介入。无限重试只会浪费 token 和时间。

参数推荐值说明
温度0.1 - 0.3代码生成需要确定性
上下文长度任务代码的 2-3 倍留空间给描述和示例
最大重试次数3超过则人工介入
单次输出行数< 200 行超过则出错率上升

5. 常见问题与排查技巧实录

5.1 LLM 生成的代码跑不通,怎么排查

这是最常见的问题。排查顺序应该是:先看语法,再看类型,再看逻辑,最后看环境。语法错误最容易发现,linter 一跑就出来了。类型错误次之,类型检查器能抓到大部分。逻辑错误最难,需要单元测试来暴露。环境问题最隐蔽,比如依赖版本不匹配、环境变量没设置。

我遇到过一个典型案例:AI 生成的代码在本地跑得好好的,一到 CI 就挂。排查了半天,发现是它用了一个本地有但 CI 环境没有的库。这种问题,静态检查抓不到,单元测试也抓不到,只能靠依赖清单审查。所以我现在养成了一个习惯:AI 生成的代码,只要引入了新依赖,必须人工确认这个依赖在目标环境里可用。

5.2 AI 改代码引入回归,怎么快速定位

AI 改代码引入回归,通常是因为它只看了局部,没看全局。比如它改了一个函数的返回值类型,但没改所有调用这个函数的地方。定位这种问题,最有效的工具是版本控制。每次让 AI 改代码之前,先 commit 一次;改完之后,用git diff看它到底改了什么。如果测试挂了,用git bisect或者直接看 diff,很快就能定位到问题。

另一个技巧是让 AI 自己解释改动。改完之后,问它“你改了哪些文件,每个文件改了什么,为什么这么改”。它的解释不一定完全准确,但能帮你快速建立改动的心理模型。

5.3 模型“幻觉”出不存在 API 怎么办

这是 LLM 编程的经典问题。它会调用一个根本不存在的函数,或者用一个库的 API 但参数完全不对。应对方法有三个:第一,在提示词里明确列出可用的库和版本,不要让它自由发挥。第二,要求它只使用你提供的代码里出现过的 API。第三,静态检查加类型检查,不存在的函数和错误的参数类型,这两道关基本都能拦住。

如果它反复幻觉出同一个不存在的 API,说明这个 API 在它的训练数据里很常见,但在你的项目里不存在。这时候你要做的不是反复纠正它,而是在提示词里显式声明“本项目不使用 XXX 库”。

5.4 常见问题速查表

问题现象可能原因排查方法解决技巧
代码语法错误模型输出截断或格式错乱跑 linter降低温度,增加输出长度限制
类型不匹配模型不了解项目类型定义跑类型检查提示词里附上类型定义文件
测试回归改动影响未覆盖的调用方git diff + 跑全量测试改前 commit,改后对比
幻觉 API模型用了训练数据里的常见 API静态检查提示词里显式声明可用库
死锁/并发问题模型对并发场景理解不足压力测试人工审查并发逻辑
依赖缺失引入了目标环境没有的库依赖清单审查提示词里限定依赖范围

5.5 几个我踩过的坑和对应的经验

第一个坑:让 AI 一次性改太多文件。我试过让它同时改五个文件,结果它改到第三个就忘了第一个改了什么,最后五个文件互相不兼容。后来我改成一次只改一个文件,改完验证通过再改下一个,问题就没了。

第二个坑:不给 AI 看测试文件。我以为它不需要看测试,结果它生成的代码和现有测试的假设完全不符。后来我每次都把相关测试文件一起喂给它,它生成的代码通过率明显提高。

第三个坑:忽略 AI 的“解释”。AI 改完代码后经常会附一段解释,我以前不看,直接跑测试。后来发现,它的解释里经常藏着重要的假设,比如“我假设输入数据已经去重了”。这些假设如果不验证,就是定时炸弹。

提示:AI 编程工具的输出,永远要当成“草稿”而不是“成品”。你的审查和验证,是保证质量的最后一道防线。

6. 我对“编程尚未被解决”的最终判断

回到标题。编程尚未被解决,不是因为工具不够强,而是因为编程的本质是人的意图和机器执行之间的翻译,而人的意图往往是模糊的、变化的、充满隐含假设的。LLM 能帮你把清晰的意图翻译成代码,但它没法帮你澄清模糊的意图。它能在局部做出很好的决策,但它没法替你做全局的权衡。

我现在的做法是:把 LLM 当成一个高吞吐量的草稿生成器和不知疲倦的代码审查员。它帮我写第一版,帮我找明显的错误,帮我补测试用例。但架构决策、边界条件判断、跨模块协调,这些还是我自己来。这个分工下,我的效率大概提升了百分之三四十,但离“编程被解决”还差得远。

如果你也在用这些工具,我的建议是:先把工作流搭好,再追求模型能力。一个好的 Harness 加一个中等能力的模型,比一个裸的强大模型好用得多。另外,永远保留人工审查环节,不要因为 AI 说“没问题”就跳过验证。这个习惯,能帮你省下大量排查回归的时间。

最后分享一个小技巧:每次让 AI 改代码之前,先让它复述一遍你的需求。如果它复述得不对,说明你的描述有问题,先改描述再让它动手。这个简单的步骤,能避免至少一半的返工。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进