上一篇文章把软件开发中的知识拆成了六个层次:代码、业务逻辑、架构设计、历史决策、需求、基础知识
拆完之后,一个很自然的问题就摆在了面前:六层知识浩瀚如海,但一次具体的开发任务——比如「在退款流程里加一个超 30 天拒退的校验」——只需要其中的极少一部分。剩下的全是噪音,不仅没用,还会干扰判断
怎么从知识的海洋中,捞出刚好需要的那一滴?
这就是 Context 要解决的问题。在整个 AI Native 软件开发的链路中:
1 | Knowledge → Context → Decision → Specification → Execution |
Context 是第二环,承接 Knowledge,支撑 Decision。但我越来越觉得,它可能是整个链路中最难的一环
不是因为其他环节简单,而是因为这一环要解决的问题,本质上没有一个完美解
一个看似简单的问题
先别急着讨论理论。回到上一篇那个退款场景:
任务:在退款流程中增加一个校验——超过 30 天的订单不支持退款。
这个需求只有一句话。但为了正确地实现它,你需要知道:
- 退款的主流程在哪里?(代码)
- 退款之后有哪些异步链路——MQ?Event?定时任务?(架构)
- 有没有现成的校验逻辑可以参考?(代码中的模式)
- 有没有幂等要求?(历史决策 + 基础知识)
- 这个改动会不会影响积分、优惠券、通知?(架构 + 业务逻辑)
- 有没有类似的历史改动可以参考?(历史决策)
- 测试需要覆盖哪些边界场景?(需求 + 历史事故)
这些信息确实都存在于系统中——在代码里、在文档里、在某个人的脑子里。但它们不会自动跑到你面前。你必须主动去找
而且这里有一个很微妙的困境:在开始找之前,你并不知道上面这个清单。你只知道「我要改退款流程」。至于退款流程涉及什么、需要小心什么——这些是你边找边发现的
你需要上下文来发现你需要什么上下文
两种上下文
在讨论具体机制之前,先做一个区分。上下文不是一次性出现的——它有一个从少到多的构建过程:
1 | 初始上下文(Seed Context):在探索系统之前,你手里已经有的信息 |
这个区分的重要之处在于:Seed Context 的质量,直接决定了整个构建过程的效率
举个例子:如果你的 Seed Context 里已经有了一条信息——「退款流程涉及 MQ 消费者和定时任务补偿」——你第一轮就会主动去读那些代码。如果没有这条信息,你只能打开 RefundService.java,顺着调用链往下追,追到一半才发现原来还有 MQ,然后再去查 MQ 消费者
前者是主动检索,后者是被动发现。Seed Context 的作用不是直接给你答案,而是让你知道应该问什么问题
这直接解释了为什么 CLAUDE.md、ADR、架构文档在 AI 时代变得如此重要。它们不是在替代推理,而是在降低探索的盲目性
Context 的五重约束
Context 构建之所以没有一个完美解,是因为它同时面对五重互相冲突的约束:
1. 完备性——不能漏
任何一个相关的知识点漏掉了,对应的决策就会出错。漏掉了「MQ 消费者也会处理退款事件」→ 你改了主流程但没改消费者 → 上线后积分扣回逻辑行为不一致
2. 最小性——不能多
不相关的信息进入上下文,不只是浪费空间——它会主动稀释注意力。人类的工作记忆只有 4±1 个组块,AI 的上下文窗口虽然有 token 上限但更大一些,但注意力稀释的规律是相似的:噪音越多,信号越难被抓住
3. 容量限制——不能全要
无论是人还是 AI,上下文窗口有上限。你不可能把整个代码仓库、全部文档、所有历史 Issue 都装进去。你必须做取舍
4. 可获取性——有些知识就是不在那
基础知识在模型里,不需要获取。代码在仓库里,读就行了。但历史决策——三年前的那次事故复盘、那个设计决策的讨论——可能散落在工作聊天记录、已离职员工的脑子里、一个没人维护的 Wiki 页面。你知道它存在,但你拿不到
5. 不可预知性——你不知道你不知道什么
这是最根本的限制。你只能去查你意识到需要查的东西。如果某个定时任务完全不在你的认知范围内——你根本不知道它的存在——你就不会去搜索它。它可能是整个改动中最关键的影响点,但你对它一无所知
五重约束放在一起,两两互斥:
- 完备性 ↔ 最小性:多找怕噪音,少找怕遗漏
- 完备性 ↔ 容量限制:窗口放不下
- 完备性 ↔ 不可预知性:有些东西你根本不知道应该去找
- 最小性 ↔ 不可预知性:你怎么判断一个你不知道的东西是不相关的?
Context 构建不是一个技术问题——它是一个在理论上有上限的优化问题。你只能逼近最优解,无法达到
迭代构建:Context 的循环
既然一次性构建完美 Context 不可能,那就只能迭代。这个过程可以描述为三步循环:
1 | 当前上下文 → 分析知识缺口 → 获取知识 → 新知识揭示新缺口 → 再获取 → ... → 收敛 |
第一步:知识需求分析
从当前已有的信息出发,问自己一个问题:
基于我现在知道的,我还需要知道什么?
这不是检索,这是判断。输入是当前上下文,输出是一组「知识需求」。以上面的退款任务为例:
1 | 当前上下文:任务描述 + CLAUDE.md(提到退款模块在 order-svc) |
注意:这个清单在第一轮是不完整的。因为你还不知道 MQ 的存在,所以你不会列出「MQ 消费者的代码在哪」这个需求。这个需求会在第二轮——读到了 MQ 生产者代码之后——才会出现
知识需求分析的质量,取决于你已经知道多少。 而这又回到了 Seed Context 的重要性。
第二步:知识获取
针对每一个知识需求,从六层知识空间中获取。但不同层的知识,获取方式完全不同:
| 知识层 | 获取方式 | 难点 |
|---|---|---|
| 代码 | 读文件、追踪调用链、搜索关键字 | 量大,搜索精度低——搜索「退款」可能返回 200 个文件,其中 150 个不相关 |
| 业务逻辑 | 从多个代码片段中逆向还原 | 无法直接获取,需要合成——读了五个文件,每个有一段相关逻辑,但没有一个文件告诉你完整规则 |
| 架构设计 | 依赖图、调用图、目录结构 | 代码里的显式依赖能工具化获取,但隐式的架构意图需要推理 |
| 历史决策 | 检索 ADR、Wiki、Issue、PR | 离散、稀疏,找到的文档可能已经过时 |
| 需求 | 查找原始 Issue/PRD | 大概率找不到——需求迭代多次后,代码是唯一可信的记录 |
| 基础知识 | 不需要获取(已在模型内) | 但需要激活——在正确的代码位置想起对应的知识 |
关键洞察:Context 构建不是一种统一操作,而是针对不同知识层的不同策略组合
代码层的获取是高频、动态的——每次任务都要读,但读什么取决于任务。架构层可以预计算——依赖图、调用关系,一次构建、多次复用。历史决策层是检索加验证——找到了还要判断这份 ADR 在三年前的决策现在是否仍然有效
第三步:缺口判断
每一轮获取之后,问自己另一个问题:
有没有什么东西,我不知道,但我感觉我应该知道?
这一步和第一步不同。第一步是正向推导——「我需要 X」。这一步是反向审视——「我是不是漏了什么」。
几个典型的缺口信号:
- 因果链断裂:「我看到了幂等检查,但不知道为什么存在」→ 缺历史决策
- 调用链未闭合:「我看到了 MQ 生产者,但没看到消费者」→ 缺架构知识
- 模式不匹配:「这个写法和项目里其他类似模块不一样」→ 缺历史原因
- 边界不清晰:「我不知道这个修改会影响几个系统」→ 缺依赖关系
有缺口 → 回到第一步,把缺口转化为新的知识需求
没有明显缺口 → Context 收敛,进入 Decision
这个循环的困难不在于循环本身
循环的逻辑不复杂。复杂的是循环中每一个步骤的执行质量
知识需求分析的质量取决于 Seed Context。Seed Context 差 → 第一轮需求清单就不全 → 获取方向就偏 → 需要在更晚的轮次才发现缺失。而每一轮额外的循环都在消耗时间和注意力
知识获取的质量取决于检索精度和合成能力。搜索「退款」出来 200 个文件——你看得过来吗?看了之后能不能从五个文件的片段里拼出完整业务规则?AI 帮你读,但 AI 的信息合成同样可能漏掉关键连接
缺口判断的质量取决于经验。新人根本不知道「MQ 消费者」应该存在,自然不会觉得没看到它是一个缺口。缺口判断能发现的是「已知的未知」,无法发现「未知的未知」。这让 Context 的完备性在根本上是一个不可判定问题
几个逼近策略
既然完美解不存在,那就有策略好过没策略。以下几个方向值得讨论:
1. 把 Seed Context 当作投资品
Seed Context 是一次构建、多次复用的资产。CLAUDE.md、架构图、ADR 索引——这些不是为了某一个任务准备的,而是为了降低每一次任务的探索成本。它们在首次创建时有成本,但边际收益随使用次数递增
一个实用的问题是:一份 Seed Context 好不好,不看它写了多少,看它让后续任务少走了多少弯路。 如果一个架构文档没有帮你减少探索轮次,它就不是一个好 Seed Context。
2. 不同知识层,不同生命周期
代码层的上下文随任务动态构建,不缓存。架构层的依赖关系可以预计算、随代码变更自动更新。ADR 可以按领域索引,每次相关任务时检索。基础知识不需要管理——管理的是「在什么位置激活哪条基础知识」的绑定关系。
把不同生命周期的知识混在一起管理,结果一定是过时和噪音同时存在。
3. 接受不完美,但标记盲区
既然无法保证完备性,一个次优策略是:把「这一轮我没覆盖但可能需要关注」的领域显式标记出来。不假装自己什么都知道——而是诚实地标注:「以下领域本轮未深入探索:定时任务补偿逻辑、跨系统的 Event 消费端」
这不会让 Context 更完备,但会让 Decision 更有自知之明。知道自己的决策建立在什么盲区之上,本身就是一种有价值的信息
4. 上下文窗口是一个预算问题
如果上下文窗口容量是固定的,Context 构建可以理解为:按照相关性从高到低填充窗口,直到窗口满。相关性排序决定了哪些知识进去,哪些留在外面。
排序的质量取决于 Seed Context 和每一轮的知识需求分析。你排得越准,窗口里装的东西越有价值。排得不准——窗口满了,但里面有一半是噪音,而你真正需要的东西没装进去
写在最后
这篇文章讨论了 Context 构建为什么是软件开发中最难的一步——不是因为技术复杂,而是因为它在理论上就没有完美解。五重约束互相冲突,你只能在完备和最小之间做逼近。
但反过来说:正因为难,Context 的能力才是一个工程师(或 AI Agent)核心竞争力的分水岭
你我都有这样的经验:两个工程师面对同一个任务,一个人花半天搞清楚状况然后半小时写完代码,另一个人上来就写然后花三天改 bug。他们的差别不在写代码的速度——而在 Context 构建的效率
在 AI 时代,这个差别可能被进一步放大。因为写代码的速度 AI 已经拉平了,但从知识到上下文的这一跳——知道该读什么、什么时候读够了、什么可以忽略——仍然高度依赖判断力
下一个问题自然就是:Context 构建完成了,接下来怎么用这些信息做出正确的工程决策?
这就是 Decision 要解决的问题。下一篇继续
*这是 AI Native 软件开发系列的第三篇。第一篇讨论了「软件开发管理的对象是知识而不是代码」,第二篇定义了「知识是什么」,这一篇分析了从知识到上下文的筛选过程。如果你有不同的理解或者实践经验,欢迎一起讨论