AI图鉴

AI图鉴首页 › AI 资讯 › 别急着让 AI 写代码:需求里的「熵」,才是你真正的成本

别急着让 AI 写代码:需求里的「熵」,才是你真正的成本

来源:掘金 · 发布时间:10-30 · 由 AI图鉴每小时自动聚合

AI 时代下,信息传递比写代码更重要,AI 时代最贵的不是写代码,是把话说到不需要再猜的程度。下面用信息论和 Cynefin 框架,把「说清楚」这件事拆开算一算。

AI 时代下,信息传递比写代码更重要

先列两个我再开发中遇到的问题事件:

- 上个月一次需求评审,产品把 PRD 投到屏幕上,二十多页。散会后我直接让 AI 按文档拆任务,产出的东西看着挺像回事,真跑起来全歪了。回头我一条条追问那些含糊的地方,补出十几个「大家都懂」的隐含条件,AI 才跑通。

- 我在执行 TDD ( 先写测试再实现(Red→Green→Refactor)) 虽然准确但并不高效,现在很多公司使用公司本地的部署的本地模型,性能和速度都大打折扣,开发速度慢,pm 催上线,所以这种模式并不适合紧急需求,当面对紧急需求时我需要对llm表达准确,让llm理解我的需求,并给出准确的执行方案,让代码快速落地。

这两件事之后我越来越确信:AI 时代最贵的不是写代码,是把话说到不需要再猜的程度。下面用信息论和 Cynefin 框架,把「说清楚」这件事拆开算一算。

一、先量化:信息传递到底在传什么

香农熵(Shannon Entropy)给出的是一个系统「不确定性」的度量:

- \(X\):一个离散随机变量,比如某条需求的可能状态、某段代码的缺陷分布;

- \(p(x_i)\):第 \(i\) 种状态出现的概率,且 \(\sum p(x_i)=1\);

- \(H(X)\):单位为 bit,表示「完整描述这个随机变量所需的平均信息量」。

直观理解只有两种情况:

- 结果唯一确定(\(p=1\))时,\(H(X)=0\),一句话都不用传;

- \(n\) 种结果等概率(\(p=1/n\))时,\(H(X)=\log_2 n\) 取到最大,不确定性最高,要传的信息也最多。

二、熵和 Cynefin:四域就是四种不确定性

这里要先纠一个常见的误解:知识本身不产生熵,熵描述的是接收方对这件事还剩多少不确定。同一段业务逻辑,老员工眼里 \(H \approx 0\);同一个新人,或者同一个 AI,眼里可能接近 \(\log_2 n\)。

所以文档和流程真正干的事,是把接收方的条件熵 \(H(X \mid D)\) 往下压——降的是那一侧的不确定性,世界本身没变。隐性知识难编码,就难在这里:它没写成 \(D\),于是 \(H(X \mid D)\) 一直悬在高位。

它和 Cynefin 四域正好一一对上,按不确定性从高到低排:

在 AI 工作流里,LLM 的核心价值之一就是降熵:把混沌、复杂模式下高熵的黑盒,经由感知—分析—RAG 拆成低熵的子场景。而那些说不出口的经验,靠 review、异常 Agent、性能 Agent 的反馈回流,慢慢从隐性变成显性。所谓信息传递重要,本质上就是在持续压低协作和认知中的不确定性。

三、不可言说知识:经验为什么传不过去

你脑子里的经验是什么?是能写成教程的东西,还是只能自己上手摸出来的东西?

后者才是大部分。举个例子:两个 API 都能实现同一个功能,但内部原理不一样,某个场景下其中一个就是更合适;或者同一个 API 在测试环境和生产环境表现完全不同。这类判断没人写进文档,也没法写——它是在一次次踩坑里长出来的。这就是不可言说知识(tacit knowledge),它只存在于你的大脑中,文字和语言只能传个形状,传不了内核。

我是个篮球爱好者,三分球一直投不到。看了很多教程,照着做还是不进。后来一个教练说:教程只是理论,发力和动作得自己去领会,做不到言传身教。那个「言传身教不了」的部分,就是不可言说知识,也是能力的最终体现。

放到 AI 工作里,同一个道理:教程和文档解决不了的问题,只能靠经验和直觉。经验不是读出来的,是试出来、总结出来的(上篇文章实现的小模型》其原理就是"猜")。

那么问题来了——既然传不了,怎么验证它被正确理解?在 AI 时代,答案是 review 和有意识的前置理解。让人把隐性的判断显性化一次,再让 AI 复述、执行,从结果反推它有没有真的接住。

为什么开发测不出自己的 bug,测试可以

两个原因。

一是惯性思维。代码是自己写的,脑子里那条路径已经跑了几十遍,看代码时眼睛会自动跳过不符合预期的部分。

二是知识类型不一样。开发拿的是 PRD,做的是**显性知识(explicit knowledge)**的实现;测试除了验显性功能,还得做异常操作、冒烟、边界值——这些流程是隐形的、不言说的。测什么、怎么测,靠的是经验里那套「哪里容易出问题」。

在 AI 结对编码的模式下,这件事可以工程化:建一个性能 Agent,专测代码的性能表现;再建一个异常 Agent,专测异常处理能力。这两个 Agent 本质上就是把不可言说知识编码成可执行的检查项——测试结果回流给开发,也就完成了不可言说知识的传递。

四、需求转化:四个域,四套处理链路

Claude Code 这类 Agent 干活时,走的是一条固定的链路:信息感知 → 信息分析 → 信息决策 → 信息执行 → 信息反馈。区别只在于,每一步落在哪个域里,处理方式完全不同。

Cynefin 分成四个域,另外正中间还有一块「失序(Disorder)」,意思是还判断不出它属于哪个域:

清晰模式(Simple):归类就够了

例:归档一份格式已知的文档。

链路:感知(sense)→ 归类(categorize)→ 响应(respond)。

知识充沛、逻辑清楚,剩下的只是执行。这类活儿交给规则或一次 LLM 调用就完事,别多想。

庞杂模式(Complicated):拆开来分析

例:写一个按指定规则排序的函数。

链路:感知(sense)→ 分析(analyze)→ 响应(respond)。

知识是充分的,也能立即取用,但不能一步到位——需要链式推理,一步一步来:先感知要改什么,再分析怎么改,最后落手改。这一步最像 CoT(Chain of Thought,链式思维):链条拉得越长,越容易在中途跑偏,所以通常要配上校验。

复杂模式(Complex):先试探再收敛

例:需求分析、需求拆解、plan 模式。

链路:探索(probe)→ 感知(sense)→ 响应(respond)。

知识不充分,得先调外部知识补进来——这里的外部知识不只是 MCP,还包括人的决策、以及前面说的不可言说经验的试探。补完再分析,生成 TODO List,然后执行、看反馈。

混沌模式(Chaotic):先动手止血

例:偶现的、没人能定位的 bug。

链路:行动(act)→ 感知(sense)→ 响应(respond)。

因果关系是未知的,只能先动一下看反应,从反应里反推原因。找到原因之后,把它拆成子需求,让子需求重新落进清晰、庞杂或复杂模式——这才是混沌域唯一的出口。

五、LLM 怎么参与模式转化

同一个模型放进不同域,表现差得远。清晰模式下它又快又准;复杂模式要多轮检索加追问,时间和成本翻几倍;至于混沌模式,我基本不交给它——它不知道因果,又特别擅长给一个听起来合理的答案,这种猜测比没有答案更危险。

所以真正的工作不是「让 LLM 解决混沌问题」,而是人先把它从混沌里拽出来,拆成清晰或庞杂的子问题,再交给模型。

分解场景:把黑盒拆成白盒

问题卡在混沌或复杂模式,通常都是因为它说不清楚。这时候要做的是切场景,切成一堆尽量独立的子场景。

麻烦在于,子场景往往也切不动——因为它对我们同样是黑盒。这里的做法是借 LLM 来识别黑盒、打开黑盒,再结合自己掌握的上下文把白盒信息串起来,用 CoT 的链式逻辑把复杂问题逐层简化,最终落到清晰模式。

这个过程很少一次做完,多数时候是循环的:切开、看不清、再切。它真正提升的是人「信息感知 → 信息分析」的速度,以及验证一个理解对不对的速度。

六、时间维度:任务是有生命周期的

还有一条特别容易被忽略的拆解线索——时间。

任何需求都有生命周期:什么时候被触发、什么时候装载上下文、什么时候真正执行、什么时候卸载。沿着时间轴切一遍,复杂度会自己摊开,而且 LLM 对时间信号格外敏感。

任务生命周期(UML 状态机)

需求不是一次性输入的,而是有状态、有阶段、有触发时机的实体。把它画成状态机,复杂度就自然沿时间轴展开:每个状态只需要关心「我现在在哪一阶段、下一步被什么事件触发」,不必一次性理解全部细节。

stateDiagram-v2

[*] --> 待触发 : 需求登记

待触发 --> 装载中 : 定时器 / 事件 / API 触发

装载中 --> 初始化 : 配置 + 上下文注入完成

初始化 --> 执行中 : 资源就绪

执行中 --> 校验中 : 产出结构化结果

校验中 --> 执行中 : 校验不通过(CoT 重规划)

校验中 --> 完成 : 校验通过

执行中 --> 异常超时 : 超时 / 异常抛出

异常超时 --> 执行中 : 退避重试 backoff

异常超时 --> 失败 : 重试耗尽

失败 --> 卸载 : 补偿 / 降级

完成 --> 卸载 : 归档 / 回写知识库

卸载 --> [*] : 释放资源 / 清理上下文 / 留快照

关键时间锚点

为什么 LLM 能识别时间维度

- 文本里天然带时间信号:「每天 8 点」「登录后」「支付成功后」「超时 3 秒」「任务结束前」,这些词本身就是 UML 里的触发事件(trigger),不需要额外标注。

- 可以抽成有向状态转移:LLM 只要能回答「上一个状态结束后必然进入哪个状态」,状态机就抽出来了;描述越确定(越靠近清晰模式),抽取越准。

- 复杂度和时间尺度强相关:ms 级(初始化、工具调用)确定性高;s 级(推理生成)是成本主战场;min~h 级(重试、长任务)需要记忆支撑;d 级(需求等待、经验沉淀)属于隐性知识。把成本集中投在高熵的时间段,是 Token 治理的第一原则。

- 状态即上下文边界:明确「什么时候卸载」,就等于明确了上下文该在哪里截断——这和 SubAgents「任务结束即销毁」的策略同构,时间维度是 Agent 生命周期管理的理论依据。

和其他两个维度的关系

时间维度解决「何时做」,另外两个维度一起约束「做什么」:

- 结构维度(下一节要讲的代码架构):任务的组成与依赖关系,决定拆分边界;

- 场景维度(前面的「分解场景」):任务落在 Cynefin 哪个域,决定交给 LLM 还是人。

三者交叉,才能把一个混沌需求切成可独立执行、可独立卸载的子任务。

七、回到日常开发:任务到底怎么拆

前面讲的是从庞杂模式到清晰模式的转化,偏理论。现在换个视角,从日常开发的两个入口来看:产品文档、现有代码结构。

以产品文档为入口

AI 时代的文档,必须比传统文档更清晰、更细致。

PRD 是任务拆解的第一入口,也是 AI 时代落差最大的一环。传统 PRD 是写给「有上下文的人」看的:读者是同一个团队的 PM、前端、后端、测试,共享业务背景、历史包袱和口头共识,于是文档可以大量留白——「按老逻辑处理」「参考 A 页面」「异常情况兜底即可」。这些白被人的经验自动补齐了。

AI 没有这段共享上下文。它只拥有你写给它的信息。文档里没写的,模型只能用概率去猜;猜出来的结果读起来很合理,跑起来不可用。用前面的说法:传统文档把降熵的成本转移到了读者侧(读者有共同背景,凭经验补全),AI 时代必须把降熵做在作者侧(写进文档)。

从文档到任务的拆解流水线大致是这样:

flowchart LR

A[读文档<br/>标出不确定点] --> B[结构化<br/>抽取实体/状态/规则]

B --> C[补黑盒<br/>对每个未定义量追问]

C --> D[对齐<br/>与现有代码/接口比对]

D --> E[产出任务<br/>每条规则一组用例]

E --> F{规则可判定?}

F -- 否 --> C

F -- 是 --> G[进入清晰模式<br/>可直接执行]

对应到手上的动作:

- 先标不确定点,别急着拆任务:通读一遍,把所有「大概 / 类似 / 参考 / 兜底」标出来,这些词就是歧义的定位标记。

- 结构化抽取:把文档拆成四类原子——实体(Entity)、状态(State)、规则(Rule)、动作(Action)。拆不出来的句子,说明还没想清楚。

- 追问黑盒:每个未定义的量都是一次提问机会。这正是复杂模式吃掉人力的地方,也是 LLM 最能补位的地方——它可以把一个模糊问题展开成 10 个待确认的具体问题。

- 与现有代码对齐:文档规则必须能映射到现有数据结构和接口。映射不上,要么文档错,要么代码缺,两种都得在动手前暴露。

- 一规则一任务:拆解粒度以「一条可判定规则 + 一组用例」为单位,而不是以页面为单位。页面是给人看的,规则是给执行者用的。

- 可判定性自检:如果一条任务写不出断言,它就不是任务,只是一句愿望,退回第 3 步。

以前的 PRD 面对的是人,现在面对 AI,得把故事线写完整。我一直觉得 AI Vibe Coding 的终点,是产品自己写代码。

以代码结构为边界

产品文档解决「做什么」,现有代码结构解决「在哪做、改成什么样」。

这两件事是同一个问题的两面:文档给出行为契约,架构给出上下文边界。我们给 LLM 的记忆文件里通常就是项目架构,帮它快速定位代码结构。Agent 改错地方,多数时候不是能力问题,而是它不知道哪块代码属于这个功能。

架构为什么能当上下文用?

因为代码结构不是物理目录,而是已经被人切好的一组功能上下文(Bounded Context)。模块边界、依赖方向、共享类型,本质都是「这里的变更不会影响那里」的承诺。对 LLM 来说,这些边界就是预先切好的上下文切片:

- 不用读全仓库,只需读到相关的子图;

- 任务天然落进某个模块,不用每次重新探索;

- 改动范围有物理边界,越界产生的是编译错误,而不是线上事故。

所以「以代码结构拆任务」的核心动作是收敛:从整个仓库收敛到模块,再到文件,最后到函数。每一跳都必须有证据,否则就是在猜。

flowchart LR

A[全仓库<br/>几十万行] -->|记忆文件:目录树+职责| B[目标模块<br/>一层目录]

B -->|模块契约:入口/依赖| C[入口文件<br/>几个类]

C -->|符号查找:引用方| D[改动点<br/>具体函数]

D --> E[扩散半径<br/>调用方/被调方/共享类型]

E --> F[越界即报错<br/>而非线上事故]

记忆文件是地图,不是百科。

CLAUDE.md / AGENTS.md 这类记忆文件最常见的误区,是把它写成项目百科。它实际的职责只有一个——路由:用最少的 Token 把 Agent 引导到正确的文件和正确的约束前。所以里面该放「索引入口」,不该放「知识正文」。

一份能用的记忆文件大致长这样:

## 架构

src/

├── modules/order/ 订单域:下单、支付回调、状态机

├── modules/coupon/ 优惠域:券模板、发放、核销(纯计算,不依赖 order)

├── modules/user/ 用户域:账号、权限、Profile

├── shared/ 跨域共享:类型、错误码、埋点(只增不改语义)

└── infra/ 基础设施:DB、缓存、MQ 客户端

## 依赖方向

modules/* → shared,modules/* → infra

禁止:modules/a → modules/b 互相直接 import;跨域一律走 shared/contracts

## 命令

pnpm test modules/order 单模块测试

关键一条:依赖方向必须写成禁令。「允许什么」Agent 可能记不住,「禁止什么」它会遵守,因为越界能立刻被验证。

从架构到任务,四个收敛动作:

- 定位(改哪里):把文档里的实体和状态映射到模块。映射不上就是架构缺口——要么新增模块,要么复用现有模块,必须在动手前裁决。

- 锚定(从哪进):找到入口文件——路由注册、事件订阅、定时任务、对外接口。Agent 从入口向内读,比从目录向外扫快一个数量级。

- 算半径(还会动哪):改一个函数,会牵连调用方、被调方与共享类型。半径内的文件要一并列入任务范围,半径外要明确写禁止。

- 划边界(不许动哪):把 shared/、公共组件库、其他域目录写成冻结区。没有冻结区的任务卡,Agent 很容易「顺手优化」出一堆不相关的 diff。

产出的任务卡应当自带定位信息,而不是只写需求:

任务:订单页优惠券叠加计算

目标模块:modules/order(rules/coupon-stacking.ts 新增)

参考契约:modules/coupon(复用 calcDiscount,禁止直接写计算逻辑)

允许改动:modules/order/**、shared/contracts/coupon.ts

禁止改动:modules/coupon/**、shared/errors.ts(错误码复用现有)

验收:pnpm test modules/order -- coupon-stacking

这张卡的信息量,抵得上一份小型架构说明,但它比任何描述都精确——因为路径和禁令都能被工具校验。

人写代码的时候很会偷懒:不写注释、不用设计模式,可读性自然变差。换成 AI 写,这些偷懒的地方就得提前写进约束里。它会越写越快、越写越好,程序员最后大概真的会变成流水线上的操作工——只不过这条流水线的上游,得由我们自己把话说清楚或者把话转述清楚。

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

相关免费羊毛

同源资讯

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 商务合作 · 关于本站 · 600 款 AI 工具分类导航 · 每日 AI 热门项目与动态