拆解 Dex Horthy:No Vibes Allowed 背后的上下文工程方法论

拆解 Dex Horthy:No Vibes Allowed 背后的上下文工程方法论

不靠“vibe”写代码:如何让 AI Agent 攻克复杂代码库

写在开始

笔者平时使用 Codex、Claude Code、Pi 等编码 Agent 工具时总会遇到同一个问题:简单场景可以胜任,但在复杂业务背景下,Agent 编码很难达到想要的结果——经常出现失忆、幻觉、注意力不集中、过度设计等问题,导致代码不得不返工。因此想参考技术大佬面对这些问题时是怎么决策的。

视频来自:YouTube AI Engineer的 Vibes Allowed: Solving Hard Problems in Complex Codebases

演讲者:Dexter Horthy是一位美国科技创业者、软件工程师,主要活跃在 AI Agent和开发者工具领域。 他的主要身份:

  • HumanLayer 创始人兼 CEO
  • 曾任 Replicated 高管/工程负责人相关职位
  • AI Agent 社区的活跃人物

文章概要

  • AI 在新项目中往往表现不错,但进入历史悠久、关系复杂的代码库后,很容易制造返工与技术债。
  • 问题不只在模型能力,更在于上下文:错误、缺失、噪声和失败对话都会影响 Agent 的下一步选择。
  • Dex 提出用 intentional compaction(有意压缩)、subagent(子代理)和 Research–Plan–Implement 工作流,让 Agent 尽可能始终处于高质量上下文中。

一、背景:AI 让我们写得更多,还是返工得更多?

AI 确实能快速生成代码:搭一个新页面、写一段脚本、补一个简单接口,通常都能很快交付看起来不错的结果。但一旦放进真实且复杂的工程环境,情况可能迅速恶化:

  • 没有找到真正应该修改的文件;
  • 理解了局部代码,却误判了系统整体的数据流;
  • 重复实现代码库中已有的能力;
  • 为了完成当前任务,绕开既有架构;
  • 生成的代码暂时能运行,却增加了后续维护成本;
  • 开发者不得不反复解释、纠正和返工。

slop:看似交付,实则返工

演讲开头引用了一项针对约 10 万名开发者的调查,结论是:AI 让团队交付了更多代码,但其中相当一部分工作是在重做前一周交付的低质量内容。这类产出被称为slop,它不只是“写得不好看的代码”,还包括:

  • 没有充分理解代码库便生成的修改;
  • 看似完成任务、实际上需要持续返工的实现;
  • 导致 codebase churn(代码库反复变动)的低质量产出;
  • 不符合既有架构与约束的代码。

Greenfield 与 Brownfield

GreenfieldBrownfield codebase
定义从零开始的新项目持续演进多年的既有系统
特点历史约束少、代码规模小,模型容易掌握全局大量历史设计、分散在多模块的业务规则、不明显的调用关系、兼容性约束、隐含约定与技术债

AI 可以很好地完成一个新的小型项目;但如果让它进入一个已有十年历史的 Java 代码库,结果可能完全不同。

本次分享要解决的核心问题:如何让今天的模型在复杂棕地代码库中解决真正困难的问题,同时减少 slop 和返工?


二、结果:从“不好用”到 2~3 倍吞吐量

Dex 坦言,他第一次使用 Claude Code 时并没有留下特别深刻的印象,认可产品体验、也能看出进步,但不认为足以改变软件开发方式。后来他所在的三人团队花了八周时间重新设计工作流,带来了约2~3 倍的吞吐量提升——交付速度快到不得不改变协作方式,甚至重新设计整个软件开发流程。

团队逐渐收敛出的目标:

  1. 让 AI 能在 brownfield codebase 中工作;
  2. 让 AI 能解决复杂问题;
  3. 尽量避免 slop;
  4. 维持团队的 mental alignment(心智对齐);
  5. 尽可能把有意义的工作交给 AI,发挥 token 的杠杆价值。

整场分享把整套实践概括为:

Advanced context engineering for coding agents(面向编码代理的高级上下文工程)


三、分析:真正的痛点不是模型不会写,而是上下文一团乱

1. 最典型的情况:纠正循环

大多数人使用 coding agent 时都会经历下面的循环:

提出需求 ↓ Agent 给出错误实现 ↓ 指出错误并补充说明 ↓ Agent 再次修改 ↓ 发现新的错误 ↓ 继续纠正,直到上下文耗尽或开发者放弃

看起来是在“逐步逼近正确答案”,但实际只会越来越乱。问题在于:它能否记得自己搜过哪些文件、运行过哪些命令、采用过哪些错误假设、用户如何否定了这些假设?

答案往往是不能——上下文已经脏了,只能重新开个窗口:保留原任务和已确认的事实,丢弃无效探索,再从正确方向重新开始。但直接重启也有代价:新的 Agent 必须重新搜索代码、理解调用关系、定位文件。于是提出了intentional compaction(有意压缩)

2. 上下文压缩的核心概念:Context Engineering

LLM 是stateless(无状态)的。Dex 曾把 LLM 类比为 pure function——虽然输出具有非确定性,严格来说不是纯函数,但确实可以视为无状态的。

对 coding agent 来说,每一步都可能面临很多选择:接下来读哪个文件?是否继续搜索?应该调用哪个工具?其中可能同时存在数百个合理选择和数百个错误选择。影响模型下一步输出的核心信息,完全来自当前对话中已有的内容。因此:

更高质量的上下文 Token ⟶ 更高概率的正确输出 Token \text{更高质量的上下文 Token} \longrightarrow \text{更高概率的正确输出 Token}更高质量的上下文Token更高概率的正确输出Token

上下文工程,就是优化输入的 token,让结果更接近正确。

怎么定义“正确的输入”?从 4 个方面切入:

维度含义
Correctness(正确性)上下文中的事实必须正确
Completeness(完整性)必须包含解决任务所需的关键文件、调用关系和约束
Size(大小)上下文应尽可能精简,避免噪声占用有限空间
Trajectory(轨迹)对话历史应该尽量呈现正确、稳定的推进方向

这 4 点不可能同时满足——正确性、完整性、轨迹都满足时,上下文就不可能短了。所以要尽可能舍弃部分上下文、保留关键正确的信息,但不能压缩得太短。

短而错误的上下文并不好;正确但略长的上下文,也可能优于精简却缺失关键约束的上下文。

3. 不要在错误的轨迹上继续争论

假设对话一直是:

Agent:做出一个错误修改 → 用户:指出错误 Agent:再次做出错误修改 → 用户:再次指出错误 Agent:又一次做出错误修改 → 用户:继续指出错误

从人类视角看,我们是在耐心地纠正模型;但从模型视角看,当前上下文展示的连续模式是:模型犯错 → 用户批评 → 模型犯错 → 用户批评。模型可能继续生成最符合这段对话轨迹的内容——再做错一件事,让用户继续纠正。

这并不意味着模型真的拥有这种意图,而是说明:对话历史不仅记录事实,也在塑造后续输出的统计轨迹。因此,一旦对话长期陷入“生成—否定—再生成—再否定”,最合理的处理方式可能不是继续争论,而是:

  1. 提取已确认的正确事实;
  2. 删除错误假设与无效探索;
  3. 启动新的上下文;
  4. 从正确轨迹重新开始。

4. 模型会随着上下文变长而变得愚蠢

以 Claude Code 为例,上下文窗口大约为 168,000 tokens,其中一部分还要为输出和压缩预留。问题是:模型智力并不会等到上下文完全耗尽才下降,注意力会被不断分散。Dex 给出的经验性判断:

对某些复杂任务而言,上下文使用到约 40% 附近时,就可能开始出现 diminishing returns(边际收益递减)。

为什么模型智商会下降?

上下文不断增长 ↓ 噪声和历史信息不断累积 ↓ 模型定位关键事实的难度提高 ↓ 复杂任务更早出现性能衰减

这是最常见的上下文杀手:

  • 搜索和定位文件;
  • 理解代码流;
  • 编辑文件的过程;
  • 测试输出;
  • 构建输出;
  • MCP 工具返回的大量 JSON。

如果 coding agent 配置了太多 MCP 工具,并让它们持续向上下文中注入垃圾,Agent 甚至可能从任务一开始就站在垃圾堆里。所以:工具越多不一定越强——不能转化为有效决策的信息,只是在消耗上下文预算。


四、解决:三个方法

方法一:Intentional Compaction(有意压缩)

什么是有意压缩?无论当前任务是否已经跑偏,都主动让 Agent 将现有上下文压缩成一份 Markdown 文档,随后拿着这份上下文开始新的工作区域——相当于给 Agent 一个清晰的背景、目标、规范。

旧上下文 ├── 文件搜索记录 ├── 完整文件内容 ├── 构建与测试输出 ├── 错误尝试 └── 已确认事实 │ ▼ Intentional Compaction │ ▼ 压缩后的 Markdown ├── 当前任务 ├── 已确认结论 ├── 关键文件与行号 ├── 相关代码流 └── 下一步工作 │ ▼ 新 Agent 直接继续

这样,新 Agent 不必重新进行所有搜索,也不必继承旧上下文中的大量噪声。

一份好的压缩结果应该包含什么?

  • 当前究竟在解决什么问题;
  • 哪些文件与问题直接相关;
  • 相关代码位于哪些行;
  • 哪些代码流与当前任务有关。

可直接使用的模板:

# 当前任务 描述需要解决的问题。 ## 已确认事实 - 已经通过代码验证的事实 - 当前系统的相关行为 - 不能违反的现有约束 ## 关键位置 - `path/to/file-a`: 与问题相关的代码位置 - `path/to/file-b`: 调用方或依赖位置 ## 相关代码流 说明请求、数据或状态如何经过相关模块。 ## 当前进度 - 已完成: - 待完成: ## 下一步 给出下一阶段应处理的具体事项。

人工审核:压缩最关键的不在于短,而在于正确。压缩并不会自动保证正确——如果旧上下文中已经存在误解,模型可能把误解一并写入摘要,未经审核便交给新 Agent,只是把错误从长上下文迁移到了短上下文。所以必须人工审核,才能拿到正确的交接文档。

方法二:Subagent——它是上下文隔离器

提到 subagent(子代理),我们会下意识地按岗位创建角色来并行执行、加快开发效率,比如前端/后端/QA/数据科学子代理。Dex 纠正了这一点:

Subagents are not for anthropomorphizing roles. They are for controlling context.
子代理不是为了把角色拟人化,而是为了控制上下文。

正确的用途:假设主 Agent 正在解决一个复杂问题,但需要先了解某个功能在大型代码库中如何工作,主 Agent 可以创建一个独立的子上下文:

主 Agent │ ├── 任务:实现当前功能 │ └── 派生子 Agent: "查清楚这个功能在代码库中是如何实现的"

子 Agent 可以在自己的上下文中完成高消耗工作:搜索大量文件、阅读完整代码、追踪调用链、理解代码库结构、排除不相关模块。然后它不把所有过程原样传回主 Agent,只返回一个精简结论:

  • 目标逻辑位于某文件;
  • 关键入口是某函数;
  • 相关调用关系如下;
  • 主 Agent 接下来只需阅读这个文件。

主 Agent 因此不必承担搜索过程中产生的上下文负担,可以直接读取最相关的文件并开始工作。

子代理的本质:从上下文工程角度看,子代理是一个“信息漏斗”:

大量代码与搜索结果 │ ▼ 子 Agent 隔离处理 │ ▼ 少量、高密度、任务相关的信息 │ ▼ 主 Agent

它就是一个上下文隔离器,防止主 Agent 被垃圾污染。但使用子代理也需要注意返回结果的正确定性。

方法三:Frequent Intentional Compaction(频繁有意压缩)

单次压缩只能解决某一个节点上的上下文膨胀。Dex 进一步提出:不要等上下文被垃圾堆满了才开始压缩

压缩不再是异常处理动作,而是开发流程的一部分,开发者要有意且主动地压缩。与其让一个 Agent 在同一个对话中完成理解代码库 → 设计方案 → 修改代码 → 运行测试,不如在不同阶段之间主动建立边界,每个阶段只保留下一阶段需要的信息。

这就引出了整场演讲最重要的工作流:

Research → Plan → Implement


五、实践:三阶段工作流 Research、Plan、Implement

阶段一:Research

目标:

  • 理解系统如何工作;
  • 找到真正相关的文件;
  • 理清代码流;
  • 保持客观,不急于提出修改方案。

这一阶段可能需要大量搜索和阅读,也因此很容易产生大量上下文。合理的做法是让研究过程独立运行,最后输出一份高密度的研究文档。

可直接使用的模板:

# Research:目标问题 ## 系统当前行为 说明相关功能目前如何运行。 ## 关键文件 - `path/to/file-a` - `path/to/file-b` ## 代码流 入口 → 中间处理 → 数据写入或输出 ## 与问题直接相关的事实 - 事实一 - 事实二 ## 尚未确认的问题 - 待确认事项一 - 待确认事项二

阶段二:Plan

在理解代码结构后就可以开始规划怎么开发了,务必澄清所有需求。

plan = Outline the exact steps —— 列出准确的步骤。

阶段三:Implement

规划清楚之后,就可以开发,然后再验收。

三个阶段是怎么连接的

每个阶段都拿到正确的输入、工作并产生正确的结果:

Research 上下文 ├── 大量搜索 ├── 阅读代码 └── 输出压缩后的研究结论 │ ▼ Plan 上下文 ├── 使用研究结论 └── 输出明确实施步骤 │ ▼ Implement 上下文 ├── 使用研究结论与计划 └── 聚焦代码修改

每一次阶段切换,都可以成为一次intentional compaction


六、总结:长期任务或复杂项目中怎么用 AI Coding

为什么 AI 在复杂代码库里容易制造 slop?因为它经常需要同时承担四类工作:

  1. 搜索代码;
  2. 理解系统;
  3. 设计方案;
  4. 编写并验证代码。

如果所有过程都发生在同一个上下文里,模型需要一边处理当前任务,一边背负所有历史搜索、错误输出和工具结果。随着上下文增长,它做出错误下一步选择的概率也会提高。

Dex 的方案可以概括为:

复杂任务 │ ├── 用 subagent 隔离高消耗搜索 ├── 用 intentional compaction 提炼有效信息 ├── 用 Research 建立客观理解 ├── 用 Plan 固化下一步路径 └── 用 Implement 聚焦执行

其本质很简单:在开发中不断保持输入的正确和简洁。