<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间</title>
	<atom:link href="https://www.phppan.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 13 Jun 2026 03:34:40 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>如何打造 AI-Native 软件产品研发组织</title>
		<link>https://www.phppan.com/2026/06/how-to-build-an-ai-native-software-product-rd-organization/</link>
		<comments>https://www.phppan.com/2026/06/how-to-build-an-ai-native-software-product-rd-organization/#comments</comments>
		<pubDate>Sat, 13 Jun 2026 03:34:40 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AINative]]></category>
		<category><![CDATA[AI编程]]></category>
		<category><![CDATA[研发效能]]></category>
		<category><![CDATA[研发管理]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2507</guid>
		<description><![CDATA[如果一个团队真的想做 AI-Native 研发组织，第一步是改组织契约：谁可以让 AI 产出代码，谁对代码负责 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">如果一个团队真的想做 AI-Native 研发组织，第一步是改<strong style="color: #0e88eb;">组织契约</strong>：谁可以让 AI 产出代码，谁对代码负责，什么质量底线不能退，什么流程可以砍，什么流程必须更重。</p>
<p data-tool="mdnice编辑器">AI 现在太会写代码了，但是这就会导致一个问题：它把「产出代码」这件事的成本做到很低，低到组织里原本那些靠角色边界、流程延迟、评审机制勉强维持的质量平衡，突然失效了。</p>
<p data-tool="mdnice编辑器">过去一个 PM 提需求，前端写页面，后端写接口，测试回归，整个链条慢，但慢本身也是一种摩擦力。现在 PM 自己就能把页面跑起来，AI 一下午能写出几十个组件，速度是上去了，但系统开始更脆弱了。</p>
<p data-tool="mdnice编辑器">先说 AI-Native 组织不是什么。 AI-Native 组织它不是「让所有人都开始写代码」，也不是「研发岗位会被提示词工程师替代」。</p>
<p data-tool="mdnice编辑器">它是把软件组织里的逻辑重排。</p>
<p data-tool="mdnice编辑器">以前文档是入口，代码是结果。现在代码变成入口，文档变成派生物。 以前角色按工种切，PM、设计、前端、后端、测试各守一段。现在角色要按责任切： 谁定义业务闭环，谁维护系统护栏，谁提供稳定契约，谁为线上事故买单。</p>
<p data-tool="mdnice编辑器">今天我们先聊组织契约，再聊角色重构，然后再把一条研发流水线拆开聊聊。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">组织契约</span></h1>
<p data-tool="mdnice编辑器">没有组织契约，AI 进团队只会把烂流程放大。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">合理预期</span></h2>
<p data-tool="mdnice编辑器">在某些 Demo 场景，或者脚本类场景，10 倍是一个合理的预期。 但是对于一个组织来说，10 倍是一个不科学的场景，因为在组织里面，在产品研发过程中，写代码并不是整个生产过程中最耗时的部分。</p>
<p data-tool="mdnice编辑器">所以，我们要先摈弃一个幻觉：不要拿 10 倍产出当目标。</p>
<p data-tool="mdnice编辑器">AI 在研发组织里的收益区间，我一直认为是 1.5 到 2 倍，高质量前提下的 2 倍。为什么呢？ 因为代码生成速度和有效交付速度不是一回事。AI 能把编码阶段压缩掉一大块时间，但需求澄清、边界判断、异常处理、联调、测试、观测、回滚，这些成本不会自动消失。很多时候还会更高。</p>
<p data-tool="mdnice编辑器">如果团队盯着 10 倍，结果一般都一样：PR 数量暴涨，代码体积暴涨，Review 质量下降，线上回归问题增加，半年后开始集中还债。这就是技术债积累速度第一次超过了团队消化速度。</p>
<p data-tool="mdnice编辑器">所以合理预期应该写进团队共识里：我们追求的是 2 倍高质量交付，不追求 10 倍低质量代码喷射。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">所有权</span></h2>
<p data-tool="mdnice编辑器">每个 AI 输出都必须有所有者。</p>
<p data-tool="mdnice编辑器">谁发起生成，谁提交 PR，谁 approve merge，这中间可以分层，但最终必须落到一个明确的人身上。这个人要能够面对一句话：如果这段代码挂上你的名字，你愿不愿意负责。要是答案是否定的，这段代码就不该进主干。</p>
<p data-tool="mdnice编辑器">这条规则听起来像废话，实际上很多团队做不到。因为 AI 会制造一种错觉：代码好像不是谁写的，是系统吐出来的。人变成了搬运工。只要团队接受这种错觉，质量就开始漂移。一个有所有权的流程大概是这样：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">PR 模板里要求声明 AI 参与范围</section>
</li>
<li>
<section style="color: #010101;">提交人对业务正确性负责</section>
</li>
<li>
<section style="color: #010101;">Reviewer 对工程质量负责</section>
</li>
<li>
<section style="color: #010101;">合并人对上线风险负责</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">责任可以分工，但责任不能悬空。</strong></p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">质量顺序</span></h2>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">质量第一，数量第二。</strong></p>
<p data-tool="mdnice编辑器">团队如果不显式声明这一点，默认会被 AI 拖向另一个方向：先铺功能，再补治理。这个方向对 demo 团队成立，对正式业务组织通常是灾难。因为 AI 特别擅长快速补齐显性功能，却不擅长主动建立长期维护结构。它会顺着需求走，不会替你守架构。</p>
<p data-tool="mdnice编辑器">AI 时代必须把一些以前属于「好习惯」的东西升级为「强制门槛」，也就是我们之前经常强调的规范，流程等等，如测试、监控、CI/CD、单测等等。</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">没有测试，不进主干</section>
</li>
<li>
<section style="color: #010101;">没有异常处理，不进主干</section>
</li>
<li>
<section style="color: #010101;">没有监控埋点，不进主干</section>
</li>
<li>
<section style="color: #010101;">没有 feature flag，不上线</section>
</li>
<li>
<section style="color: #010101;">不能被 reviewer 解释清楚的代码，不 merge</section>
</li>
</ul>
<p data-tool="mdnice编辑器">当代码供给能力暴涨时，质量约束必须同步加码，否则系统会很快从可控变成不可控。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">交付代码</span></h1>
<p data-tool="mdnice编辑器">传统产品研发里，PRD 是上游，代码是下游。这个模型的问题大家都熟：PRD 经常过期，设计稿和实现偏移，字段定义藏在飞书文档、接口平台、聊天记录和前端代码里，最后没有一个地方是真正可靠的。</p>
<p data-tool="mdnice编辑器">AI-Native 组织里：代码作为唯一源，文档从代码反向提取。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">从代码出发</span></h2>
<p data-tool="mdnice编辑器">现在 PM 借助 Codex、Claude Code（最近又有同事被封号了）、Cursor 这类工具，已经可以生成带交互的页面、Mock 数据、状态流转，很多场景下甚至能直接把核心业务路径跑通。既然这样，就不要再让 PM 先写一份长 PRD，再让研发去猜什么叫「列表为空时的引导态」。让页面先跑起来，再从页面反推需求定义，效率和准确率都更高。</p>
<p data-tool="mdnice编辑器">这里有个好处：讨论变得具体了。</p>
<p data-tool="mdnice编辑器">以前评审会上大家讨论一句文档描述，脑子里各自渲染不同 UI。现在把页面摆出来，点击路径、字段结构、交互反馈、错误提示都变成可观察对象。对齐成本直线下降。后端也不需要看十页文字说明，只要看这个页面最终需要什么数据结构，就知道接口该怎么供给。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">文档反向生成</span></h2>
<p data-tool="mdnice编辑器">代码是唯一真相源，不等于不要文档。让文档变成派生物，而且是自动派生物。</p>
<p data-tool="mdnice编辑器">流程大概如下：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">PM 先生成并跑通前端页面，带 Mock 数据</section>
</li>
<li>
<section style="color: #010101;">AI 从页面代码、组件 props、Mock schema 中逆向提取字段说明、交互流程、状态机描述</section>
</li>
<li>
<section style="color: #010101;">PM 对这份自动生成的文档做人审</section>
</li>
<li>
<section style="color: #010101;">后端按确认后的 JSON Schema 或 TypeScript Interface 实现接口</section>
</li>
<li>
<section style="color: #010101;">联调时再根据真实契约让 AI 回写文档</section>
</li>
</ol>
<p data-tool="mdnice编辑器">这样处理后，文档不再是一个独立维护成本很高的系统，而是代码的视图层。它可以读给人看，但它的源头来自真实实现，而不是空想。</p>
<p data-tool="mdnice编辑器">建议把接口契约标准化为 JSON Schema 或 TypeScript Interface</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">角色重构</span></h1>
<p data-tool="mdnice编辑器">AI-Native 组织可能会有一些角色的变更，但是也可能会裁掉一些角色，因为我们是重写角色边界。如果当前人不合适，或者适应不了，就需要换，否则就会变成打造新组织过程中的阻碍。</p>
<p data-tool="mdnice编辑器">很多人讨论这件事喜欢走两个极端。一个极端是「以后人人都是全栈」。另一个极端是「AI 只会替代初级岗位，核心角色不变」。这两个判断都不够准确。</p>
<p data-tool="mdnice编辑器">真正发生的变化是：低门槛生产能力下放，专业门槛上移。简单说，更多人能做出东西，但真正有价值的岗位，会向规范制定、质量兜底、系统抽象和复杂问题处理集中。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">PM 变成产品工程师</span></h2>
<p data-tool="mdnice编辑器">在这个新组织中，变化最大的角色是 PM。</p>
<p data-tool="mdnice编辑器">过去 PM 的主要输出物是 PRD、流程图、原型图和口头解释。现在这些东西很多都可以收敛成一个交互可运行的页面。于是 PM 的角色自然往「产品工程师」移动：他不再只描述需求，他直接构造需求的运行形态。</p>
<p data-tool="mdnice编辑器">但这里一定要说清楚，PM 写了前端代码，不等于 PM 变成前端工程师。PM 的责任边界还是业务逻辑，不是工程治理。</p>
<p data-tool="mdnice编辑器">PM 需要对什么负责：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">用户路径是否闭环</section>
</li>
<li>
<section style="color: #010101;">页面状态是否完整</section>
</li>
<li>
<section style="color: #010101;">文案、条件、分支、空态是否符合业务预期</section>
</li>
<li>
<section style="color: #010101;">Mock 数据结构是否表达真实业务语义</section>
</li>
<li>
<section style="color: #010101;">生成代码是否遵守团队规定的基础组件和样式约束</section>
</li>
</ul>
<p data-tool="mdnice编辑器">PM 不需要对什么负责：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">全局状态架构是否优雅</section>
</li>
<li>
<section style="color: #010101;">包体积是否最优</section>
</li>
<li>
<section style="color: #010101;">路由切换是否引入副作用</section>
</li>
<li>
<section style="color: #010101;">渲染性能是否达标</section>
</li>
<li>
<section style="color: #010101;">工程抽象是否可复用</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些后者还是需要专业前端要接住。</p>
<p data-tool="mdnice编辑器">问题不是让 PM 承担更多，而是让 PM 把过去停留在文档层的业务表达，直接推进到代码层。这样整个团队拿到的是可执行的业务定义，不是抽象说明书。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">设计师变成立法者</span></h2>
<p data-tool="mdnice编辑器">设计角色也会变。</p>
<p data-tool="mdnice编辑器">当 PM 可以直接借助 AI 生成页面时，设计师如果还把大量时间耗在单页面高保真稿上，投入产出比会快速下降。更有价值的位置，是维护设计系统和机器可消费的样式规范。</p>
<p data-tool="mdnice编辑器">设计师的工作重心会变成：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">Design System</section>
</li>
<li>
<section style="color: #010101;">样式 token</section>
</li>
<li>
<section style="color: #010101;">Visual QA</section>
</li>
</ul>
<p data-tool="mdnice编辑器">也就是把颜色、间距、字体、圆角、阴影、组件状态、可访问性规范，从设计稿资产转化成代码和配置资产。比如 Tailwind config、CSS variables、组件规范、图标集、交互动效约束。这些东西一旦进入 AI 生成上下文，PM 或其他角色在生成页面时就不容易跑偏。</p>
<p data-tool="mdnice编辑器">说白了，设计师从「画每一张图」转向「制定法律」。立法做得越完整，AI 和 PM 生成的页面越像一个系统，而不是一堆拼起来的截图。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">前端变成守门员和重构者</span></h2>
<p data-tool="mdnice编辑器">前端是最容易被误读的角色。很多人看到 PM 可以生成页面，就开始下结论：前端会被干掉。实际情况通常相反。前端从体力活里解放出来之后，工程价值反而被放大了。</p>
<p data-tool="mdnice编辑器">因为 PM 生成的页面，最常见的问题不是「长得不对」，而是「能跑但脆」。</p>
<p data-tool="mdnice编辑器">可能会有大量这类代码：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">一个组件里塞满请求、状态、渲染、事件处理</section>
</li>
<li>
<section style="color: #010101;">loading、error、empty 三种状态缺两种</section>
</li>
<li>
<section style="color: #010101;">useEffect 依赖写错</section>
</li>
<li>
<section style="color: #010101;">切页面回来状态丢失</section>
</li>
<li>
<section style="color: #010101;">没有 abort controller，快速切换导致竞态更新</section>
</li>
<li>
<section style="color: #010101;">表单校验只校 happy path</section>
</li>
<li>
<section style="color: #010101;">直接把 mock 字段名写死在视图层，后续接口一改全炸</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些问题不是 PM 能解决的，也不是 AI 自己会主动解决的。前端真正的工作变成三块：</p>
<p data-tool="mdnice编辑器">第一块，Review。<br />
不是审美式 review，也不是找缩进。重点看稳定性、边界条件、状态管理、组件职责、可维护性。</p>
<p data-tool="mdnice编辑器">第二块，Refactor。<br />
把单文件逻辑拆成组件、hooks、domain service，把团队组件库接上，把状态流收敛，把重复逻辑抽掉。这个过程需要持续迭代，做到团队规范中，给到 PM 生成的上下文中。</p>
<p data-tool="mdnice编辑器">第三块，Integration。<br />
把 Mock 替换成真实 API，处理鉴权、缓存、错误回退、重试、并发、路由守卫、埋点、监控。</p>
<p data-tool="mdnice编辑器">这个阶段的前端，更像体验架构师和质量守门员。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">后端变成能力供应商</span></h2>
<p data-tool="mdnice编辑器">后端角色也会收缩：提供稳定、安全、确定性的能力边界。</p>
<p data-tool="mdnice编辑器">如果前端页面能更快被构造出来，后端的价值就不在于「接收一份文档然后写 CRUD」，而在于把系统能力以 API 或 Tools 的形式稳定暴露出来。尤其是 AI Agent 场景下，后端实际上在扮演工具供应商。</p>
<p data-tool="mdnice编辑器">后端重点关注四类问题：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">契约稳定性</section>
</li>
<li>
<section style="color: #010101;">权限与安全</section>
</li>
<li>
<section style="color: #010101;">幂等性与确定性</section>
</li>
<li>
<section style="color: #010101;">可观测性与审计</section>
</li>
</ul>
<p data-tool="mdnice编辑器">前端、PM、Agent 都可能直接消费这些能力。一旦接口设计漂、错误码混乱、鉴权模型含糊，整个上层生成式开发就会持续返工。AI 会把不稳定接口的问题放大得很明显，因为它极度依赖上下文中的确定性。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">流程重构</span></h1>
<p data-tool="mdnice编辑器">只讲角色变化不够，需要把研发流程改成适合 AI 的形状。下面是一版比较能落地。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">需求具象</span></h2>
<p data-tool="mdnice编辑器">第一阶段，需求不再先写成长文档，而是先具象成可运行页面。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">PM 独立生成前端</span></h3>
<p data-tool="mdnice编辑器">PM 拿着明确业务目标，直接在<strong style="color: #0e88eb;">受控环境</strong>里生成页面代码。这个页面至少要包含：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">UI 布局</section>
</li>
<li>
<section style="color: #010101;">基本交互</section>
</li>
<li>
<section style="color: #010101;">主要状态流转</section>
</li>
<li>
<section style="color: #010101;">Mock 数据</section>
</li>
<li>
<section style="color: #010101;">核心校验逻辑</section>
</li>
<li>
<section style="color: #010101;">空态、错误态、加载态</section>
</li>
</ul>
<p data-tool="mdnice编辑器">特别强调最后三项。很多团队让 PM 生成页面，只盯着主流程，结果页面演示时很漂亮，一联调全是坑。空态、错误态、加载态必须在这个阶段一起生成，否则后面每一轮都要补票。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">禁止黑盒交付</span></h3>
<p data-tool="mdnice编辑器">PM 生成页面这件事，最怕黑盒。也就是只看效果图对不对，不管代码是否落在团队跑道上。</p>
<p data-tool="mdnice编辑器">所以团队必须先准备好脚手架和护栏。比如：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">必须使用指定组件库</section>
</li>
<li>
<section style="color: #010101;">必须使用既定样式 token</section>
</li>
<li>
<section style="color: #010101;">必须遵守目录结构</section>
</li>
<li>
<section style="color: #010101;">必须使用指定的数据获取模式</section>
</li>
<li>
<section style="color: #010101;">必须输出可运行测试脚本</section>
</li>
<li>
<section style="color: #010101;">必须带 feature flag 接入点</section>
</li>
</ul>
<p data-tool="mdnice编辑器">否则 PM 生成的代码看似完成任务，实际是给前端制造二次工程。</p>
<p data-tool="mdnice编辑器">不要让 PM 从空白页面开始提示 AI，而是给一个完整的基础模板，再让 AI 在模板内填充。这个模板的价值极高，它决定了团队是让 AI 在高速公路上开车，还是在野地里乱冲。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">契约握手</span></h2>
<p data-tool="mdnice编辑器">第二阶段，前端页面和后端能力正式握手。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">前端反定义接口</span></h3>
<p data-tool="mdnice编辑器">以前是后端先定义接口，前端去适配。现在很多场景可以反过来：前端页面里需要什么数据结构，先自然长出来，再把这份结构抽取成契约。</p>
<p data-tool="mdnice编辑器">这里的关键点是「自然长出来」之后，必须立刻标准化。不能停留在 JS 对象字面量和口头描述。最好直接生成：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">JSON Schema</section>
</li>
<li>
<section style="color: #010101;">TypeScript Interface</section>
</li>
<li>
<section style="color: #010101;">字段说明</section>
</li>
<li>
<section style="color: #010101;">校验规则</section>
</li>
<li>
<section style="color: #010101;">错误码预期</section>
</li>
<li>
<section style="color: #010101;">状态迁移说明</section>
</li>
</ul>
<p data-tool="mdnice编辑器">做到这一步，后端看到的就不是「我要一个用户列表接口」，而是一个明确的数据契约：字段、类型、可空性、分页、排序、过滤、异常响应、鉴权要求，全都摆明。</p>
<p data-tool="mdnice编辑器">这一步会明显减少沟通损耗。后端可以少看很多无效文档，直接围绕契约开发。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">后端提供确定性能力</span></h3>
<p data-tool="mdnice编辑器">后端接手后，不建议只机械实现接口。要借这个机会把系统能力做成稳定工具层。</p>
<p data-tool="mdnice编辑器">如果团队本身就在做 Agent 或工作流系统，这里更应该统一成 Tools 注册机制，而不是散落一地的临时 API。因为未来调用这些能力的，未必只有前端页面，还有内部 Agent、自动化流程、运营工具、甚至外部合作系统。</p>
<p data-tool="mdnice编辑器">后端在这一阶段最常踩的坑有几个：</p>
<p data-tool="mdnice编辑器">第一，返回结构不稳定。<br />
今天 success 返回 data，明天又套一层 payload。前端 AI 生成代码时非常怕这个，会反复产生错误适配。</p>
<p data-tool="mdnice编辑器">第二，错误码语义不清。<br />
鉴权失败、参数错误、资源不存在、限流、业务冲突全都混成一种 message，联调体验很差。</p>
<p data-tool="mdnice编辑器">第三，幂等性缺失。<br />
AI 驱动的调用链经常会重试，如果创建类接口没有幂等控制，很容易重复写入。</p>
<p data-tool="mdnice编辑器">第四，缺少审计。<br />
AI 或 PM 直接驱动系统能力时，操作路径更分散，审计日志不能省。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">联调瓶颈</span></h2>
<p data-tool="mdnice编辑器">真正的瓶颈通常出现在第三阶段：联调、测试、排雷。</p>
<p data-tool="mdnice编辑器">到这一步，很多团队会发现：AI 把前半段提速提得很猛，但最后这段并没有自动消失，甚至更重。因为前面生成得越快，后面积累的隐患越多。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">Review 的重点</span></h3>
<p data-tool="mdnice编辑器">前端 reviewer 在这一阶段要非常克制。不要把 review 做成审美比赛，也不要上来就要求所有代码重写一遍。真正该看的，是那些会直接影响稳定性和维护成本的问题。</p>
<p data-tool="mdnice编辑器">我会把检查项固定成一张表，至少覆盖这些内容：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">异步请求是否有取消机制</section>
</li>
<li>
<section style="color: #010101;">Loading、Empty、Error 状态是否完整</section>
</li>
<li>
<section style="color: #010101;">表单提交是否防重入</section>
</li>
<li>
<section style="color: #010101;">路由切换是否会遗留脏状态</section>
</li>
<li>
<section style="color: #010101;">全局状态有没有被随意污染</section>
</li>
<li>
<section style="color: #010101;">组件职责是否过载</section>
</li>
<li>
<section style="color: #010101;">是否接入统一埋点与异常上报</section>
</li>
<li>
<section style="color: #010101;">是否存在明显重复逻辑</section>
</li>
<li>
<section style="color: #010101;">是否绕过设计系统直接手写样式</section>
</li>
<li>
<section style="color: #010101;">是否引入不必要依赖</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这样做的好处是，review 从个人经验驱动变成组织标准驱动。AI 时代 PR 量会明显增加，没有标准化检查单，review 质量一定掉。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">测试和验收左移</span></h3>
<p data-tool="mdnice编辑器">如果团队已经允许 PM 直接交付前端页面，那测试必须同步左移。否则生成速度越快，测试越像消防队。</p>
<p data-tool="mdnice编辑器">谁生成页面，谁至少生成对应的 E2E 测试脚本。这个要求不算过分。因为页面交互路径是 PM 最熟的，AI 根据这些路径生成 Playwright 或 Cypress 脚本，命中率通常不低。</p>
<p data-tool="mdnice编辑器">在生成代码时，PM 已经完成了第一次的验收。</p>
<p data-tool="mdnice编辑器">测试左移不是为了把测试岗位干掉，而是为了把最贴近业务逻辑的验证提前到需求具象阶段。后面的 QA 才能把精力放在组合场景和系统回归上。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">发布保险</span></h2>
<p data-tool="mdnice编辑器">第四阶段是发布与可观测性。这在 AI-Native 组织里是核心流程。</p>
<p data-tool="mdnice编辑器">因为一旦允许更广泛的人群借助 AI 产出代码，线上报错率几乎一定会上升。组织必须提前接受这个现实，然后把监控和发布控制建好。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">前端监控</span></h3>
<p data-tool="mdnice编辑器">前端监控一定要上，而且要够细。</p>
<p data-tool="mdnice编辑器">至少需要这些能力：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">错误堆栈捕获</section>
</li>
<li>
<section style="color: #010101;">Source map 还原</section>
</li>
<li>
<section style="color: #010101;">用户会话回放</section>
</li>
<li>
<section style="color: #010101;">性能指标采集</section>
</li>
<li>
<section style="color: #010101;">接口失败聚合</section>
</li>
<li>
<section style="color: #010101;">版本维度对比</section>
</li>
</ul>
<p data-tool="mdnice编辑器">像 Sentry、LogRocket 这类工具的价值，在 AI 场景下会更高。因为当报错出现时，你可以把错误堆栈、用户操作轨迹、接口上下文直接喂给 AI，让它先生成修复建议，再由责任人确认。这种协作模式对定位简单问题很有效，尤其是 UI 状态错乱、边界遗漏、类型不匹配这类故障。</p>
<p data-tool="mdnice编辑器">但不要误解成「有了 AI 修 bug 就轻松」。线上修复的难点从来不只是生成 patch，而是确认根因、评估影响面、决定是否回滚、验证是否引入新问题。这些仍然要靠工程纪律。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">灰度发布</span></h3>
<p data-tool="mdnice编辑器">Feature Flag 在这个模式里几乎是必需品。</p>
<p data-tool="mdnice编辑器">原因不复杂：当业务页面大量由 PM + AI 参与生成时，组织需要一个足够便宜的试错阀门。否则每次上线都把全量用户暴露在新逻辑下，风险太高。</p>
<p data-tool="mdnice编辑器">把 Feature Flag 从「重要功能才加」提升为默认机制。尤其是下面这些场景必须强制：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">新页面替换旧页面</section>
</li>
<li>
<section style="color: #010101;">新流程替换旧流程</section>
</li>
<li>
<section style="color: #010101;">高价值转化路径</section>
</li>
<li>
<section style="color: #010101;">涉及支付、权限、数据修改的动作</section>
</li>
<li>
<section style="color: #010101;">首次由非传统研发角色主导产出的功能</section>
</li>
</ul>
<p data-tool="mdnice编辑器">灰度期间要明确看哪些指标，不要只看有没有报错。还要看：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">转化率变化</section>
</li>
<li>
<section style="color: #010101;">页面停留时长</section>
</li>
<li>
<section style="color: #010101;">关键按钮点击率</section>
</li>
<li>
<section style="color: #010101;">表单提交成功率</section>
</li>
<li>
<section style="color: #010101;">接口失败率</section>
</li>
<li>
<section style="color: #010101;">用户退出路径</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些数据能帮助团队判断问题究竟出在代码稳定性，还是业务交互本身。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">基建先行</span></h2>
<p data-tool="mdnice编辑器">前面这些流程能否跑起来，核心在于基建。没有基建，AI-Native 组织会迅速退化成提示词手工作坊。</p>
<p data-tool="mdnice编辑器">可以先建三类基础设施。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">护栏脚手架</span></h3>
<p data-tool="mdnice编辑器">这是第一优先级。</p>
<p data-tool="mdnice编辑器">它包括：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">项目模板</section>
</li>
<li>
<section style="color: #010101;">目录规范</section>
</li>
<li>
<section style="color: #010101;">统一组件库</section>
</li>
<li>
<section style="color: #010101;">设计 token</section>
</li>
<li>
<section style="color: #010101;">数据获取封装</section>
</li>
<li>
<section style="color: #010101;">表单与校验约定</section>
</li>
<li>
<section style="color: #010101;">埋点和监控 SDK</section>
</li>
<li>
<section style="color: #010101;">测试模板</section>
</li>
<li>
<section style="color: #010101;">CI 预设规则</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这套东西是整个组织 Harness 的部分。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">Prompt 资产</span></h3>
<p data-tool="mdnice编辑器">很多团队低估了 Prompt Library 的价值。实际上，当非工程角色也开始产出代码时，提示词模板本身就是组织资产。</p>
<p data-tool="mdnice编辑器">可以把高频场景沉淀成标准模板，比如：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">列表页生成模板</section>
</li>
<li>
<section style="color: #010101;">表单页生成模板</section>
</li>
<li>
<section style="color: #010101;">详情页生成模板</section>
</li>
<li>
<section style="color: #010101;">多步骤流程模板</section>
</li>
<li>
<section style="color: #010101;">异常状态模板</section>
</li>
<li>
<section style="color: #010101;">E2E 测试生成模板</section>
</li>
<li>
<section style="color: #010101;">文档反向提取模板</section>
</li>
<li>
<section style="color: #010101;">重构任务模板</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些模板不是为了追求文风统一，而是为了把团队积累的工程约束嵌进去。你希望 PM 每次生成页面都自动包含 loading、error、empty、feature flag、埋点、测试入口，那就别靠口头提醒，直接写进模板。</p>
<h3 data-tool="mdnice编辑器"><span class="content" style="color: #0e88eb;">CI/CD 闸门</span></h3>
<p data-tool="mdnice编辑器">AI 时代，CI/CD 不能只是跑个 lint。它必须承担质量闸门职责。</p>
<p data-tool="mdnice编辑器">最低限度我会要求：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">Lint</section>
</li>
<li>
<section style="color: #010101;">Type check</section>
</li>
<li>
<section style="color: #010101;">Unit test</section>
</li>
<li>
<section style="color: #010101;">E2E smoke test</section>
</li>
<li>
<section style="color: #010101;">Bundle size 检查</section>
</li>
<li>
<section style="color: #010101;">安全扫描</section>
</li>
<li>
<section style="color: #010101;">依赖合规检查</section>
</li>
<li>
<section style="color: #010101;">自动化预览环境</section>
</li>
<li>
<section style="color: #010101;">灰度发布流水线</section>
</li>
<li>
<section style="color: #010101;">一键回滚</section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果团队现在连这些都没有，先别急着搞 PM 交付前端代码。真把口子放开了，组织会先被事故教育一遍。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">常见误区</span></h1>
<p data-tool="mdnice编辑器">这是几个最常见的误区。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">把 AI 当外包</span></h2>
<p data-tool="mdnice编辑器">这是第一类坑。很多团队使用 AI 的方式，本质上和用廉价外包没区别：把需求甩出去，拿回代码，自己不理解也不维护。</p>
<p data-tool="mdnice编辑器">这个模式短期可能看起来很省，长期一定出问题。因为系统复杂度没有消失，只是被包进了你看不懂的代码里。等线上出事，你会发现团队没有形成任何新的内生能力。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">只提速前半段</span></h2>
<p data-tool="mdnice编辑器">第二类坑是，只提速需求到页面这一段，不提速后面的治理、测试、发布、观测。结果就是项目看板前面一片绿，最后两列全堵死。</p>
<p data-tool="mdnice编辑器">AI 会把组织瓶颈照得更亮。以前慢慢暴露的问题，现在两周就能堆出来。你不改后半段流程，前半段越快，堵得越严重。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">容忍脏代码，也容忍脆弱代码</span></h2>
<p data-tool="mdnice编辑器">我能接受 PM 生成的代码不够优雅。变量命名一般、文件拆分普通、抽象层次一般，这些问题都能后续治理。真正不能接受的是脆弱代码：没异常处理、没监控、没测试、状态流断裂、提交会重复、接口失败直接白屏。</p>
<p data-tool="mdnice编辑器">脏和脆是两回事。组织必须把这条线划清楚。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">用 AI 替代判断</span></h2>
<p data-tool="mdnice编辑器">第四类坑最隐蔽。团队开始迷信 AI 给出的架构建议、重构建议、性能建议，仿佛它给出了答案，工程判断就可以省掉。</p>
<p data-tool="mdnice编辑器">这是错的。AI 非常适合提供候选方案、加快实现、缩短试错周期，但它不拥有系统上下文，不承担线上责任，也不会为半年后的维护成本买单。架构判断、边界判断、风险判断，最终还是人做。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">最后</span></h1>
<p data-tool="mdnice编辑器">如果一定要用一句话概括我对 AI-Native 软件研发组织的理解，那就是：<strong style="color: #0e88eb;">把代码生产普遍化，把工程责任集中化</strong>。</p>
<p data-tool="mdnice编辑器">前者意味着更多角色可以直接参与构造软件。PM 可以交付可运行页面，设计可以把规范直接编码，后端可以把能力做成工具供全组织消费。后者意味着系统质量不能民主化。责任必须更清晰，护栏必须更强，发布必须更克制，观测必须更细。</p>
<p data-tool="mdnice编辑器">「PM 交付前端代码」这件事很容易被包装成一个新故事。真落地时，它首先是工程纪律问题，然后才是工具问题。团队如果没有建立所有权，没有把代码当唯一真相源，没有把契约、测试、监控、灰度这些基础设施补齐，这条路大概率会走成一场持续返工。</p>
<p data-tool="mdnice编辑器">反过来，如果这些条件具备了，AI 确实会把组织推到一个新阶段。PRD 不再是那份总会过期的文档，代码本身就是需求表达。角色不再围着工种转，而是围着责任和系统边界重组。研发流程不再把大量时间浪费在翻译需求上，而是更早进入真实问题：契约、质量、异常、性能、发布。</p>
<p data-tool="mdnice编辑器">这才是我理解的 AI-Native。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/06/how-to-build-an-ai-native-software-product-rd-organization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从碎片检索到深度推理：如何重塑 AI Agent 的知识引擎</title>
		<link>https://www.phppan.com/2026/06/rag-ai-agent-graphrag/</link>
		<comments>https://www.phppan.com/2026/06/rag-ai-agent-graphrag/#comments</comments>
		<pubDate>Sun, 07 Jun 2026 13:03:32 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[GraphRAG]]></category>
		<category><![CDATA[RAG]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2505</guid>
		<description><![CDATA[如果现在对于AI Agent 的知识引擎或者知识库的认知还停留在在「向量库 + embedding + top [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #000000;" data-tool="mdnice编辑器">如果现在对于AI Agent 的知识引擎或者知识库的认知还停留在在「向量库 + embedding + topK 检索」的人，项目大概率正在踩坑。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">传统 RAG 解决的是「能不能把资料塞给模型」的问题，而知识引擎要解决的是「模型能不能沿着知识结构走到正确答案」的问题。</strong> 这两个层级差得很远。前者偏检索，后者开始接近推理。Agent 一旦进入企业场景，尤其是要处理流程、制度、系统配置、投融资关系、风控链路、故障追因这类问题，知识如果还是碎片状态，后面的规划、调用、执行基本都会失真。因为对于模型来说重要的上下文已经是经是不完整的了，自然而然的就开始瞎猜了。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">就 AI Agent 知识引擎来说，研发团队常遇到两个坑：</p>
<p style="color: #000000;" data-tool="mdnice编辑器">第一，很多团队高估了向量检索的上限。<br />
第二，很多团队又低估了图结构落地的工程代价。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">今天我们聊一下AI Agent 的知识引擎如何从碎片检索走到深度推理。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">咱们今天主要聊四件事：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">为什么传统 RAG 在复杂问题上会持续失手</section>
</li>
<li>
<section style="color: #010101;">GraphRAG 到底重塑了什么</section>
</li>
<li>
<section style="color: #010101;">真正可落地的架构怎么搭</section>
</li>
<li>
<section style="color: #010101;">当前有哪些新一些的进展</section>
</li>
</ul>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">传统短板</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">很多人第一次做 RAG，体验都挺好。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">文档切块，做向量化，进库。用户一问，召回几段文本，拼 Prompt，模型生成答案。对于 FAQ、制度查询、产品说明、接口文档检索，这一套能迅速出结果，性价比很高。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">问题在第二阶段才出现：用户不再问单点事实，而是开始问链式问题。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">「2019 年收购 A 公司的企业，其母公司在 2021 年的主要投资对象是谁？」</section>
</li>
<li>
<section style="color: #010101;">「某服务在去年三季度的故障，和两个月前那次容量扩容有没有关联？」</section>
</li>
<li>
<section style="color: #010101;">「这份上百篇研报里，哪些公司通过共同供应商暴露了同类风险？」</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这类问题有一个共同点：<strong style="color: #0e88eb;">答案不在某一个 chunk 里，而是在多个实体、多个时间点、多个文档之间。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">传统 RAG 在这里会出现几种典型失效。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">关系丢失</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">向量检索擅长找「语义相近」。它不擅长找「关系成立」。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">文档切块以后，文本里的结构被打散了。原文中可能存在很清楚的逻辑链：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">公司 B 在 2019 年收购 A</section>
</li>
<li>
<section style="color: #010101;">公司 B 是集团 C 的子公司</section>
</li>
<li>
<section style="color: #010101;">集团 C 在 2021 年投资了 D</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">切完块之后，这三个事实可能散落在不同 chunk、不同文档、不同索引分区里。向量检索能不能一次把它们都召回？经常不能。即便召回了，顺序也未必对，相关性分也未必稳定。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这时候模型只能自己「脑补拼图」。一旦有一块没找到，推理链就断。而且，模型也不会老老实实说「证据不够」，它会倾向于补一个看起来像答案的东西出来。这就是很多人嘴里的幻觉。说白了，很多幻觉并不是生成模型凭空发疯，而是检索层先把它带沟里了。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">上下文碎片化</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">RAG 的 chunk 机制本质上是一个工程妥协。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">chunk 太小，语义不完整；chunk 太大，embedding 稀释，召回变钝，窗口成本暴涨。滑窗重叠能缓解一点，但解决不了跨章节、跨文档、跨时间的连续推理。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">尤其是企业内部知识库，天然不是一本结构优雅的教材，而是一堆：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">PRD</section>
</li>
<li>
<section style="color: #010101;">wiki</section>
</li>
<li>
<section style="color: #010101;">飞书文档</section>
</li>
<li>
<section style="color: #010101;">邮件摘要</section>
</li>
<li>
<section style="color: #010101;">会议纪要</section>
</li>
<li>
<section style="color: #010101;">Jira 评论</section>
</li>
<li>
<section style="color: #010101;">数据库导出</section>
</li>
<li>
<section style="color: #010101;">运维记录</section>
</li>
<li>
<section style="color: #010101;">外部 PDF</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这些东西之间本来就没有自然连续性。你再把它们切成块，信息完整性只会进一步恶化。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">实体歧义</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">只靠向量，相同名字的实体很容易串。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">一个公司简称可能对应母公司、子公司、事业部；一个人名可能在不同组织里都出现过；产品代号可能跨年份复用。向量模型对这种歧义问题并不稳定，尤其在中文企业数据里，简称、别名、历史命名混在一起，非常常见。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">实际项目里，实体歧义一旦没有解决，后面所有链路都白搭。你在检索层把「A 公司」对齐错了，后面再高级的 Agent 规划、工具调用、答案归因，都是建立在错图上。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">时间失真</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这个问题很多团队前期根本没建模。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">企业知识不是静态的。组织关系会变，制度会改版，接口会下线，股权会变更，设备状态有有效期，药品说明有版本，风控规则按月份生效。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">传统 RAG 里，一个 chunk 通常只是一段文本。它不天然携带明确的「生效时间」「失效时间」「版本边界」。所以用户一旦问时间敏感问题，系统就容易把不同年份的事实混成一团。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">你会看到模型回答得头头是道，但时间轴已经错了。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">多跳能力弱</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这个问题其实不是模型不会推理，而是检索不给路。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">Agent 场景里，很多问题天然是多跳的。要找到答案，系统需要先定位实体，再沿某种关系扩展，再结合时间、置信度、来源做过滤，最后拿到一条可靠路径。传统向量召回没有这个导航能力，它只会不断找「像不像」。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这就是为什么很多团队把模型从一个升级到另一个，效果只提升一点点。<strong style="color: #0e88eb;">瓶颈不在模型本身，而在知识引擎没有结构。</strong></p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">图谱价值</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">把知识图谱拉进来，改变了<strong style="color: #0e88eb;">检索目标</strong>。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">传统 RAG 检索的是文本片段。<br />
GraphRAG 检索的是实体、关系、路径、子图，以及这些结构对应的证据。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这个差别很大。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">显式关系</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">知识图谱最核心的价值，是把原来需要模型从文本里隐式猜出来的关系，变成显式结构。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">公司 A — 收购 — 公司 B</section>
</li>
<li>
<section style="color: #010101;">公司 C — 母公司 — 公司 A</section>
</li>
<li>
<section style="color: #010101;">公司 C — 投资 — 项目 D</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">一旦这些关系被建出来，系统面对复杂问题时就不需要靠 Prompt 去碰运气，而是可以沿着边去找路径。路径找到后，再把路径上的证据喂给模型组织语言。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这一步非常关键。因为它把「推理」从纯生成问题，变成了「结构检索 + 受约束生成」。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">多跳导航</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">图结构天然支持多跳。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">从一个节点出发，系统可以做一跳邻域扩展、两跳路径发现、带类型约束的遍历、带时间过滤的路径搜索。很多复杂问题，其实本质上就是图上的 constrained traversal。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">做过故障分析、供应链穿透、组织权限审计的人都知道，真实问题往往不是「找到相似文本」，而是「沿着几类特定关系往外走，直到找到证据链」。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 在这里的优势很直接：<strong style="color: #0e88eb;">它给系统一张路网，而不是一堆散落纸片。</strong></p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">可解释性</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">只要答案建立在路径之上，归因就容易很多。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">传统 RAG 的可解释通常是「我引用了这几段文本」。这不够，因为用户看不出这些片段之间为什么能导出结论。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 则可以给出：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">命中的核心实体</section>
</li>
<li>
<section style="color: #010101;">扩展的关系类型</section>
</li>
<li>
<section style="color: #010101;">经过的中间节点</section>
</li>
<li>
<section style="color: #010101;">命中路径对应的来源文档</section>
</li>
<li>
<section style="color: #010101;">时间和版本约束</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">对于金融、医疗、法务、审计这类场景，是准入门槛。没有路径级证据，系统很难真正进入业务闭环。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">异构融合</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">企业知识天生异构。数据库里有结构化字段，文档里有叙事描述，日志里有事件序列，报表里有指标快照，外部网页里有补充事实。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">知识图谱很适合做这一层统一承载。节点和边上可以挂：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">来源</section>
</li>
<li>
<section style="color: #010101;">时间戳</section>
</li>
<li>
<section style="color: #010101;">置信度</section>
</li>
<li>
<section style="color: #010101;">版本号</section>
</li>
<li>
<section style="color: #010101;">原文锚点</section>
</li>
<li>
<section style="color: #010101;">权限标签</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这样，后面的 Agent 就不需要分别理解十几种数据形态，它只需要围绕图这个统一语义层工作。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">幻觉压制</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">有人说 GraphRAG 「能消灭幻觉」。做工程的人都知道，不存在这种好事。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">更准确地说，GraphRAG 能明显降低两类幻觉：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">检索证据不足导致的瞎补全</section>
</li>
<li>
<section style="color: #010101;">实体关系错配导致的错误推断</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">图结构把推理空间收窄了。<strong style="color: #0e88eb;">其本质上是一个针对知识的 harness 工程</strong>。模型不是面对无边界文本海洋自由发挥，而是在一个有限子图上做组织和归纳。再加上路径归因、时间约束、来源过滤，错误空间会被进一步压缩。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">架构重构</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 要真正落地，分为三段：<strong style="color: #0e88eb;">建图、检索、生成</strong>。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">建图阶段</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">图谱质量决定了 GraphRAG 的上限，建图成本决定了它的下限。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">如果图是脏的、歧义的、缺时间边界的，后面做再多检索优化都只是补洞。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">实体抽取</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">最基础的是从文本中抽实体、属性、关系。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">早期很多方案喜欢直接让 LLM 通读文档抽三元组。这么做效果可以有，但成本极高，而且稳定性不够。文档一长、格式一乱、术语一偏，大模型就开始漂。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">建议：<strong style="color: #0e88eb;">实体抽取和关系抽取要拆开看，不要一把梭。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">实体层往往更适合用稳定的 IE 管线或者轻量模型先打底，再让 LLM 做补充和歧义修正。原因：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">实体是高频基础设施，规模大</section>
</li>
<li>
<section style="color: #010101;">实体错一个，影响整片图</section>
</li>
<li>
<section style="color: #010101;">实体任务相对更规则，适合工程化优化</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">尤其在垂直领域，术语标准化比抽取本身更难。医疗、金融、制造、运维，每个领域都有自己的缩写、版本命名、内部代号。这里如果没有术语表和字典体系，后面图融合基本没法看。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">关系抽取</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">关系抽取是建图里最贵、最脆弱的一层。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">关系比实体更依赖上下文。它牵涉事件角色、动作方向、时间条件、否定表达、范围限定。比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">「计划收购」</section>
</li>
<li>
<section style="color: #010101;">「已完成收购」</section>
</li>
<li>
<section style="color: #010101;">「传闻收购」</section>
</li>
<li>
<section style="color: #010101;">「终止收购」</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果你都抽成同一种边，图就废了。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">所以关系抽取不能只问「有没有关系」，还要问：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">关系类型是什么</section>
</li>
<li>
<section style="color: #010101;">方向是什么</section>
</li>
<li>
<section style="color: #010101;">是否已生效</section>
</li>
<li>
<section style="color: #010101;">时间区间是什么</section>
</li>
<li>
<section style="color: #010101;">证据来源在哪里</section>
</li>
<li>
<section style="color: #010101;">置信度多少</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">不建议一上来就追求极全的关系本体。项目初期，关系类型设计得太花哨，抽取和维护成本会迅速失控。更稳一些的做法是围绕业务问题反推关系集合，先保核心链路通。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">实体对齐</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">这是最容易被低估的坑。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">同一个实体可能有：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">全称</section>
</li>
<li>
<section style="color: #010101;">简称</section>
</li>
<li>
<section style="color: #010101;">英文名</section>
</li>
<li>
<section style="color: #010101;">别名</section>
</li>
<li>
<section style="color: #010101;">历史名称</section>
</li>
<li>
<section style="color: #010101;">内部代号</section>
</li>
<li>
<section style="color: #010101;">不同系统中的主键</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果没有实体对齐，图上会出现大量看似不同、实际相同的节点。结果是路径断裂、统计失真、召回稀释。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">实体对齐需要多种信号联合：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">名称相似度</section>
</li>
<li>
<section style="color: #010101;">类型一致性</section>
</li>
<li>
<section style="color: #010101;">属性重合度</section>
</li>
<li>
<section style="color: #010101;">上下游关系重合度</section>
</li>
<li>
<section style="color: #010101;">来源可信度</section>
</li>
<li>
<section style="color: #010101;">时间重叠情况</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">仅靠名称相似度会出很多事故。尤其在中文企业环境里，简称重复极多。我见过一个项目把两个不同区域的同名子公司合并成一个节点，导致后面整条供应链分析全错。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">时间与版本</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">这部分如果不做，GraphRAG 只是比 RAG 多了一层图壳。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">建议节点和边都尽量具备时间语义，至少要能表达：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">创建时间</section>
</li>
<li>
<section style="color: #010101;">生效时间</section>
</li>
<li>
<section style="color: #010101;">失效时间</section>
</li>
<li>
<section style="color: #010101;">采集时间</section>
</li>
<li>
<section style="color: #010101;">数据版本</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这样系统才能回答历史状态问题。比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">「2021 年时它是否还是子公司」</section>
</li>
<li>
<section style="color: #010101;">「这个接口在 3 月版本里是否存在」</section>
</li>
<li>
<section style="color: #010101;">「某规则在事故发生当日是否已生效」</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果没有时间建模，系统只会返回一个「混合当前状态和历史状态」的伪答案。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">质量控制</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">建图不是 ETL 一次跑完就结束。它需要持续质控。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">至少要有几层控制：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">抽取置信度阈值</section>
</li>
<li>
<section style="color: #010101;">本体约束校验</section>
</li>
<li>
<section style="color: #010101;">规则冲突检测</section>
</li>
<li>
<section style="color: #010101;">抽样人工复核</section>
</li>
<li>
<section style="color: #010101;">高风险实体白名单校正</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">比如，公司类型节点不该挂「毕业院校」属性；人员节点不该作为「机房设备」的父实体；时间区间不能出现失效时间早于生效时间。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这些校验就是 harness 工程。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">检索阶段</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 的检索不是去图库里「搜一下」这么简单。真正有效的检索，至少是一个分层过程。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第一步：实体定位</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">用户问题进来，先要知道问的是谁、什么对象、哪个版本、哪个时间点。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这一步常见做法是实体链接，必要时混合向量召回。目标是在图里找到核心节点。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这里有两个坑特别常见。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">一个是用户表达不规范。简称、口语、错别字、历史名称都可能出现。<br />
另一个是问题里实体不止一个，而且主次关系不清。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">所以实体定位通常需要结合：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">NER</section>
</li>
<li>
<section style="color: #010101;">别名字典</section>
</li>
<li>
<section style="color: #010101;">向量相似召回</section>
</li>
<li>
<section style="color: #010101;">类型约束</section>
</li>
<li>
<section style="color: #010101;">上下文消歧</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果这一步错了，后面全错。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第二步：子图扩展</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">定位到核心节点后，要决定往外扩多少。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这一步不能粗暴按 N 跳展开。图一旦大起来，邻域爆炸是很快的。尤其企业图谱里很多「高连接度节点」会把子图迅速拉成垃圾堆。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">建议的做法是带约束扩展：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">限关系类型</section>
</li>
<li>
<section style="color: #010101;">限最大跳数</section>
</li>
<li>
<section style="color: #010101;">限时间区间</section>
</li>
<li>
<section style="color: #010101;">限置信度</section>
</li>
<li>
<section style="color: #010101;">限来源可信等级</section>
</li>
<li>
<section style="color: #010101;">限节点类型</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">比如查询收购链路，就优先沿「收购」「母公司」「投资」这类关系走，而不是把「合作」「提及」「共现」都混进来。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第三步：路径发现</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">复杂问题很多时候不只是看邻居，而是找满足条件的路径。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">从 A 出发，找到其收购方，再找到收购方母公司，再找到该母公司在某年的主要投资对象</section>
</li>
<li>
<section style="color: #010101;">找到某故障对应组件，再向上找到依赖链，再找变更记录，再对齐时间窗口内的容量调整</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这类问题，本质上就是受约束路径搜索。图数据库和图算法在这里比纯向量库更像工具。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">但有个问题：路径一多，候选会急剧增加。这个时候不能一股脑全喂给模型。要做路径评分和裁剪。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">常见评分信号包括：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">路径长度</section>
</li>
<li>
<section style="color: #010101;">关系类型优先级</section>
</li>
<li>
<section style="color: #010101;">时间匹配度</section>
</li>
<li>
<section style="color: #010101;">节点权威性</section>
</li>
<li>
<section style="color: #010101;">来源可信度</section>
</li>
<li>
<section style="color: #010101;">历史问答反馈</section>
</li>
</ul>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第四步：证据序列化</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">图拿到了，不能直接丢给模型。大多数模型并不擅长原生理解复杂图结构。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">要把子图变成模型好消化的证据形式。常见有两种：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">路径序列化</section>
</li>
<li>
<section style="color: #010101;">子图摘要</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">路径序列化适合事实问答。<br />
子图摘要适合全局分析和开放问题。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">实际工程里，更推荐结构化证据和原文片段联合输入。因为图给的是骨架，文本给的是细节。只有骨架，没有细节，答案会很硬；只有文本，没有骨架，答案会发散。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">生成阶段</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 不是检索完就结束。很多项目到这一步依然翻车，因为 Prompt 设计和输出约束没做好。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">证据优先</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">Prompt 里必须明确要求模型优先依据图证据回答，不足时才引用文本补充，证据冲突时按来源等级和时间规则处理。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">如果不写这些约束，模型还是会习惯性按语言流畅性组织答案，最后把未经验证的补全混进去。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">路径归因</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">对于高事实性场景，建议回答里至少保留轻量级路径说明。未必要全部展示给用户，但系统内部必须保留。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如答案背后至少知道：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">起点实体</section>
</li>
<li>
<section style="color: #010101;">中间关系</section>
</li>
<li>
<section style="color: #010101;">终点实体</section>
</li>
<li>
<section style="color: #010101;">每一步证据来源</section>
</li>
<li>
<section style="color: #010101;">时间过滤条件</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">有了这层，后面才能做审计、回放、纠错、反馈学习。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">模板化输出</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">在财务、医疗、法务、运维这些高风险领域，自由生成要尽量少。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">更稳的方式是字段化输出，例如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">结论</section>
</li>
<li>
<section style="color: #010101;">证据路径</section>
</li>
<li>
<section style="color: #010101;">证据来源</section>
</li>
<li>
<section style="color: #010101;">时间范围</section>
</li>
<li>
<section style="color: #010101;">置信度</section>
</li>
<li>
<section style="color: #010101;">不确定点</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这样做的缺点是可读性略硬，优点是稳定。企业系统最终是要接流程、接审批、接工单、接风控策略的，格式稳定比文采重要得多。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">三类路线</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 现在大体有三条实现路线。可以工程投入和适用边界来看：</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">知识驱动</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这一类把图谱作为主检索引擎。问题进来后，系统尽量转成图查询、路径搜索或者约束遍历，文本只做辅助。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这条路线适合：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">实体和关系相对清晰</section>
</li>
<li>
<section style="color: #010101;">业务规则稳定</section>
</li>
<li>
<section style="color: #010101;">可解释性要求高</section>
</li>
<li>
<section style="color: #010101;">多跳推理占比高</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">典型场景像金融股权穿透、资产关系分析、故障因果链、药物禁忌关系、组织权限依赖。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">优点：精度高、证据链清晰、推理路径稳定。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">缺点：图谱不全时脆弱，对建图质量依赖极大。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">如果你手里只有一堆杂乱文档，图谱覆盖率还没起来，就直接 all in 知识驱动，项目容易陷进「图不够用，文本又没保留好」的尴尬局面。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">索引驱动</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这一类不强调图上直接推理，而是把图结构转成索引增强信号。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">给 chunk 挂上关联实体、关系标签</section>
</li>
<li>
<section style="color: #010101;">把子图摘要拼进 chunk 再做向量化</section>
</li>
<li>
<section style="color: #010101;">用邻居关系信息做 rerank 特征</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这条路线改造成本低，适合已有 RAG 系统做增强。很多团队第一阶段其实更适合走这条，而不是直接重做知识底座。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">缺点也很实在：图只是辅助特征，没有成为真正的推理空间。所以多跳能力提升有限，可解释性也一般。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">混合路线</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">现在真正跑得稳的，大多是混合型。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">简单讲就是双路甚至多路召回：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">一路走图</section>
</li>
<li>
<section style="color: #010101;">一路走向量</section>
</li>
<li>
<section style="color: #010101;">必要时再接关键词或结构化库查询</section>
</li>
<li>
<section style="color: #010101;">最后统一重排、裁剪、融合</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这条路线最像真实世界。因为企业问题本来就不是纯图问题，也不是纯文本问题。很多时候用户一半在问事实链路，一半在问叙事背景。只用一种召回方式，效果往往不稳。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">混合路线的问题在于系统复杂度高。链路变长之后，监控、调参、缓存、权限控制、延迟预算都更难。可它依然是现在最主流、也最靠谱的方案。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">轻量建图</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">微软把 GraphRAG 带火，但慢慢大家发现了一个问题：<strong style="color: #0e88eb;">原版重度 GraphRAG 太贵。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">用 LLM 通读全量文档，逐段抽实体、关系、摘要，这种方案到真实数据规模经常直接失控。于是轻量化建图开始流行。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">实体图思路</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">轻量路线的核心变化是：<strong style="color: #0e88eb;">不再执着于完整关系抽取，而是先把实体和文档块稳定连起来。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">也就是说，先建一个实体图，或者说 relation-light graph：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">节点是实体、文档、chunk</section>
</li>
<li>
<section style="color: #010101;">边主要表达提及、共现、引用、归属、锚定等轻关系</section>
</li>
<li>
<section style="color: #010101;">更复杂的关系留给后续局部推理或按需补抽</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">高质量关系抽取太贵、太慢、太脆。那就先把高确定性的部分做出来，把知识骨架做起来。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">很多场景下，这么做反而更稳。因为实体图能显著改善召回导航，成本又远低于全量三元组图谱。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">成本变化</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">轻量建图通常能把两个指标打下来：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">Token 消耗</section>
</li>
<li>
<section style="color: #010101;">图谱更新时间</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这对中小项目非常关键。一个知识引擎如果更新周期是按天甚至按周算，它对 Agent 的支撑就已经很有限了。现实里的知识是流动的，尤其在运维、客服、风控、投研这些场景，更新速度直接决定答案可信度。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">局限</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">轻量图省成本，但也有边界。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">它更适合做「导航增强」和「局部上下文组织」，不适合承担强逻辑推理的全部职责。因为如果关系层太弱，系统最终还是要回文本里做大量补推断。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">所以我们一般把轻量图看成一个很好的过渡层，或者作为混合架构中的图底座。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">Agent 化检索</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 这两年另一个明显趋势，是从固定流水线走向 Agent 化。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">传统流程大多是：</p>
<p style="color: #000000;" data-tool="mdnice编辑器">用户提问 → 定位实体 → 扩子图 → 生成答案</p>
<p style="color: #000000;" data-tool="mdnice编辑器">问题是，复杂问题往往一轮检索拿不全证据。系统需要边查边判断：现在的证据够不够？应该沿哪类边继续走？有没有必要换一个切入实体？要不要回文档补背景？</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这时候，GraphRAG 和 Agent 很自然就结合起来了。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">查询拆解</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">复杂问题先拆成子问题，是 Agent 化 GraphRAG 的第一步。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<p style="color: #000000;" data-tool="mdnice编辑器">「2019 年收购 A 公司的企业，其母公司在 2021 年的主要投资对象是谁？」</p>
<p style="color: #000000;" data-tool="mdnice编辑器">可以拆成：</p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">谁在 2019 年收购了 A</section>
</li>
<li>
<section style="color: #010101;">该收购方的母公司是谁</section>
</li>
<li>
<section style="color: #010101;">该母公司在 2021 年的主要投资对象有哪些</section>
</li>
<li>
<section style="color: #010101;">哪个对象符合「主要投资对象」定义</section>
</li>
</ol>
<p style="color: #000000;" data-tool="mdnice编辑器">拆解后的每一步都更适合图查询和约束检索。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">迭代探索</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">Agent 可以根据中间结果决定下一步动作：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">命中实体不确定，先做消歧</section>
</li>
<li>
<section style="color: #010101;">路径证据不足，扩大时间窗口</section>
</li>
<li>
<section style="color: #010101;">图证据不全，补文本检索</section>
</li>
<li>
<section style="color: #010101;">文本冲突，回图里查来源优先级</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这比一条固定链路强很多。因为真实问题不会永远按设计者预想的路径走。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">风险</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">Agent 化一听就很高级，但工程上有几个明显的问题：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">延迟增加</section>
</li>
<li>
<section style="color: #010101;">token 消耗增加</section>
</li>
<li>
<section style="color: #010101;">调试复杂度上升</section>
</li>
<li>
<section style="color: #010101;">失败路径变多</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果没有严密的步骤预算和停止条件，Agent 会在图里越走越远，最后拖垮时延和成本。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">所以 Agent 化 GraphRAG 要有明确的控制策略：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">最大迭代轮数</section>
</li>
<li>
<section style="color: #010101;">最大扩展跳数</section>
</li>
<li>
<section style="color: #010101;">最大证据数</section>
</li>
<li>
<section style="color: #010101;">最大 token 预算</section>
</li>
<li>
<section style="color: #010101;">终止阈值</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">没有这些约束，系统会变成一个会自主发散的检索器。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">强化学习</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">2025 到 2026 年，另一个值得关注的方向是把强化学习或者类似 reward-guided 策略引进图检索过程。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这个方向的核心问题是：<strong style="color: #0e88eb;">子图应该扩到哪里停？哪条边值得继续走？</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">以前这类问题多靠规则和启发式。比如限制两跳、限制 topN 邻居、按关系优先级排序。够用，但不够细。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">强化学习的吸引力在于，它试图让检索器学会一件事：<strong style="color: #0e88eb;">在有限上下文预算下，优先收集对答案最有价值的证据。</strong></p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">为什么有价值</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 很容易出现一个悖论：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">扩得太少，证据不够，答不出来</section>
</li>
<li>
<section style="color: #010101;">扩得太多，噪声暴涨，模型反而更容易错</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">最优点其实是一个动态平衡，而且和问题类型、图谱密度、时间约束都相关。静态规则很难适配所有情况。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">落地现实</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这个方向我认为在部分场景是一个比较不错的解。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">原因很简单。强化学习提升的是「策略细节」，前提是你的图已经比较干净，检索反馈链路也能闭合。如果底层图谱质量一般，奖励模型再聪明也学不到稳定策略。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">所以在工程优先级上，我会把它排在：</p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">实体对齐</section>
</li>
<li>
<section style="color: #010101;">时间建模</section>
</li>
<li>
<section style="color: #010101;">混合召回</section>
</li>
<li>
<section style="color: #010101;">路径裁剪</section>
</li>
<li>
<section style="color: #010101;">再考虑 RL 优化</section>
</li>
</ol>
<p style="color: #000000;" data-tool="mdnice编辑器">很多团队喜欢直接追最新论文方向，结果底座没打稳，投入产出很差。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">动态建图</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">静态全量图谱还有一个问题：更新慢。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">数据一旦高频变化，整库重建非常重，增量融合也复杂。于是最近很实用的一条路是<strong style="color: #0e88eb;">查询驱动的动态局部建图</strong>。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">思路</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">先用向量检索或关键词检索，快速圈出和问题强相关的一小批文档或 chunk。然后只针对这部分数据，在内存里临时构建一个局部图，用来做当前问题的推理。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这样做的好处很明显：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">不需要维护一个永远完整的全局大图</section>
</li>
<li>
<section style="color: #010101;">对高频更新数据更友好</section>
</li>
<li>
<section style="color: #010101;">构建成本和时延更可控</section>
</li>
<li>
<section style="color: #010101;">对冷门知识不用提前付建图成本</section>
</li>
</ul>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">适用场景</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这条路线特别适合：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">数据更新快</section>
</li>
<li>
<section style="color: #010101;">查询分布长尾</section>
</li>
<li>
<section style="color: #010101;">很多知识只会被偶尔问到</section>
</li>
<li>
<section style="color: #010101;">无法接受全量图谱重构成本</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">比如运维告警分析、实时舆情、工单流、交易异常调查。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">局限</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">动态建图的问题也很明显：它的全局视角弱。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">如果问题本身需要跨全库的大结构理解，比如全局主题分布、长期模式汇总、跨社区关联，局部动态图就不够用了。所以它更像实时推理层，不是全局知识层的完全替代品。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">多模态图</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 继续演进，节点已经不再局限于文本实体。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">很多真实场景里，关键证据来自图像、图表、表格、时序信号。把这些都挂进图里，才可能支撑更完整的 Agent 推理。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">例如医疗场景里：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">症状描述是文本节点</section>
</li>
<li>
<section style="color: #010101;">检查指标是结构化节点</section>
</li>
<li>
<section style="color: #010101;">影像特征是图像节点</section>
</li>
<li>
<section style="color: #010101;">治疗方案是流程节点</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">如果这些数据还分散在不同系统里，Agent 再强也只能在局部瞎猜。多模态图的价值，在于把这些证据接成可遍历的语义网络。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">不过这块别急着神化。多模态 GraphRAG 落地难度明显高于文本图谱，主要难点有三类：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">跨模态对齐难</section>
</li>
<li>
<section style="color: #010101;">证据置信度难统一</section>
</li>
<li>
<section style="color: #010101;">存储与检索链路更复杂</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">所以大多数团队短期内更现实的做法，是先把表格和结构化字段接进来，再逐步引入图像等模态。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">超图方向</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">传统的知识图谱仅支持二元关系（即一条边连接两个节点，如 实体A → 关系 → 实体B）。然而在真实复杂场景中，事实往往是多维度的。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">三家公司共同参与某项目</section>
</li>
<li>
<section style="color: #010101;">一笔交易涉及买方、卖方、标的、通道、时间、地区</section>
</li>
<li>
<section style="color: #010101;">一个法律案件涉及原告、被告、法条、法院、时间节点</section>
</li>
</ul>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">超图与超边</strong>（Hypergraph &amp; Hyperedges）：超图允许单条“超边”连接任意数量的实体/节点。例如在医学场景中，描述“某症状”的发生可能涉及“患者、医生、检查手段、特定药物和治疗结果”。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">低阶与高阶关联</strong>：通过超图，系统能同时无损存储成对的“低阶关联”与包含多个实体的“高阶关联”，从根本上减少信息压缩带来的损失。</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这个方向在金融穿透、案件分析、复杂项目协作里很有吸引力。因为它能更自然地表达群体事件和多方关系。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">但说实话，超图现在离大规模工程标配还有距离。核心原因不是理念不对，而是生态、工具链、调试经验都还不够成熟。一般团队现在没必要急着上，除非业务场景确实被二元关系表达卡住了。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">落地取舍</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">那么 <strong style="color: #0e88eb;">团队到底该怎么选？</strong></p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">只做传统 RAG 就够的情况</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">如果你的问题大多是：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">单文档问答</section>
</li>
<li>
<section style="color: #010101;">FAQ 查询</section>
</li>
<li>
<section style="color: #010101;">产品知识助手</section>
</li>
<li>
<section style="color: #010101;">代码片段召回</section>
</li>
<li>
<section style="color: #010101;">对多跳推理要求不高</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">那就别急着上图。把 chunk、embedding、rerank、query rewrite、metadata filter、citation 做好，收益通常更高。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">应该上轻量 GraphRAG 的情况</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">如果已经出现这些信号：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">同一个问题答案分散在多个文档</section>
</li>
<li>
<section style="color: #010101;">实体名称混乱、简称多</section>
</li>
<li>
<section style="color: #010101;">用户经常追问上下游关系</section>
</li>
<li>
<section style="color: #010101;">向量召回结果看起来相关，答案却老是不对</section>
</li>
<li>
<section style="color: #010101;">需要更稳定的引用与归因</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">那我建议先上轻量图。先做实体层和文档锚定层，不要一口气建满关系宇宙。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">应该上重度 GraphRAG 的情况</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">如果业务本身就是关系驱动的：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">金融穿透</section>
</li>
<li>
<section style="color: #010101;">医疗知识网络</section>
</li>
<li>
<section style="color: #010101;">风控依赖链</section>
</li>
<li>
<section style="color: #010101;">故障根因传播</section>
</li>
<li>
<section style="color: #010101;">供应链关联追踪</section>
</li>
<li>
<section style="color: #010101;">组织权限审计</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">那重度图谱是值得的。因为这里的问题本质上就是图问题，用纯文本方案长期只能堆补丁。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">Agent 化和 RL 什么时候考虑</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">当且仅当你已经满足这些条件时再往上走：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">图谱基础质量稳定</section>
</li>
<li>
<section style="color: #010101;">问题复杂度确实需要多轮探索</section>
</li>
<li>
<section style="color: #010101;">现有检索链路可观测、可回放</section>
</li>
<li>
<section style="color: #010101;">成本和延迟预算允许</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">否则，先把基础检索做好，比上复杂策略更值。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">小结</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">图结构是检索的 harness 工程。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">向量检索还是底座，图结构正在变成高阶能力的分水岭。</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">没有向量，系统对模糊语义和开放文本会很迟钝。<br />
没有图，系统对关系、路径、时间、一致性会很脆弱。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">未来成熟的 Agent 知识引擎，大概率都不是单一路线，而是分层组合：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">向量层负责广覆盖召回</section>
</li>
<li>
<section style="color: #010101;">图层负责关系组织与多跳导航</section>
</li>
<li>
<section style="color: #010101;">结构化查询层负责精确过滤</section>
</li>
<li>
<section style="color: #010101;">Agent 层负责查询拆解与迭代探索</section>
</li>
<li>
<section style="color: #010101;">生成层负责受约束表达与归因输出</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">说到底，知识引擎这件事，已经从「找资料」变成「构造可推理的证据空间」。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">GraphRAG 不是给 RAG 多加一个数据库，也不是给模型多喂一点上下文。它把原来松散、偶然、靠模型自行拼接的知识，重组成了一张可以遍历、可以约束、可以回放的网络。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">对 AI Agent 来说，对于 AI Agent 的上下文构建来说，这是关键的一步。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">因为 Agent 一旦开始承担任务，它需要的就不再是几个看起来相关的文本块，而是一条能走通、能解释、能复核的知识路径。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">以上。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/06/rag-ai-agent-graphrag/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>聊聊 Harness：从 Agent 到组织</title>
		<link>https://www.phppan.com/2026/05/harness-engineering-anent-org/</link>
		<comments>https://www.phppan.com/2026/05/harness-engineering-anent-org/#comments</comments>
		<pubDate>Sat, 30 May 2026 09:35:05 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[harness]]></category>
		<category><![CDATA[harness engineering]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2501</guid>
		<description><![CDATA[我们在落地 Agent 时面临的核心矛盾，是大模型的概率生成机制与工程系统所需的绝对确定性存在天然冲突。要获取 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">我们在落地 Agent 时面临的核心矛盾，是大模型的概率生成机制与工程系统所需的绝对确定性存在天然冲突。要获取大规模、可维护且值得信赖的代码，必须在系统外围构建 Harness。<strong>Harness 的本质是将不确定性转化为确定性。</strong></p>
<p data-tool="mdnice编辑器">提高信任度和可靠性需要极度压缩 Agent 的解决方案空间。我们必须放弃让模型「生成任何内容」的灵活性，转而采用包含大量技术细节的提示、规则和框架。特定的架构模式、强制执行的边界以及标准化的结构，构成了这套护栏的物理基础。</p>
<p data-tool="mdnice编辑器">当前越来越多的团队在持续快速的产生代码，而这些演示很好看，当真的进入整个软件生命周期中，就会产生混乱，当越来越多的人随着时间的推移在仓库中堆砌代码，组织就开始堆人进行 review、反复返工，最后 AI 的吞吐量被人类注意力卡死，表面上用了 Agent，实际产能没上去，维护成本还更高。</p>
<p data-tool="mdnice编辑器">当然，这是一种结果，也有人在过程中不停的构建基建，做 Harness 工程，整个代码不再是无序的扩张。从这个逻辑来讲，harness 的作用是把<strong>大模型输出从概率事件压回工程确定性的系统设计</strong>。</p>
<h1 data-tool="mdnice编辑器"><span class="content">Agent 的 Harness</span></h1>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">harness 是什么</span></h2>
<p data-tool="mdnice编辑器"><strong>很多人把 harness 比作一个操作系统，模型是 CPU，但我觉得并不是。</strong></p>
<p data-tool="mdnice编辑器">如果模型真的是 CPU，那它接收指令后的执行结果应该是绝对严格且可预测的；但大模型本质上是一个概率引擎，它在潜空间里做的是模式匹配与概率生成。因此，harness 并不是像操作系统那样去调度底层硬件资源或分配内存，它更像是一套概率过滤器和对齐机制。它依靠纯粹的工程手段，把模型那种发散的、充满不确定性的「创造力」或「幻觉」，强行压缩进一条狭窄、严谨且符合人类预期的流水线里。</p>
<p data-tool="mdnice编辑器">这种工程逻辑在实践中，体现为无处不在的防御性设计和反馈闭环。当模型吐出一串代码或一个决策时，harness 并不负责直接「运行」它，而是负责「质检」和「纠偏」。它通过静态检查、架构规则扫描、自动化测试和沙箱验证，把模型给出的「大概率正确」转化为工程上非黑即白的「通过或驳回」。正是这种让概率不断撞击确定性规则的过程，才使得最终沉淀到代码库里的产物是安全、可控且符合系统长期利益的。</p>
<p data-tool="mdnice编辑器">harness 解决确定性问题的终极目的，是为了在系统中建立无需人工干预的信任，从而真正释放 AI 的吞吐量。如果没有这套逻辑，模型生成的代码越多，人类审查的负担就越重，整个组织的运转速度依然会被人类的注意力瓶颈卡死。</p>
<p data-tool="mdnice编辑器">Martin Fowler 的博客中发表了 Thoughtworks 的技术专家的一篇文章，将 OpenAI 文章中所描述的 harness 分为三个方面：</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">上下文工程</span></h2>
<p data-tool="mdnice编辑器">上下文工程需要做到动态与静态的交织</p>
<p data-tool="mdnice编辑器">单纯依赖超长 Prompt 无法解决复杂工程问题。上下文工程的核心在于构建代码库中持续增强的知识库，并打通 Agent 对动态上下文的访问路径。</p>
<p data-tool="mdnice编辑器">静态知识库定义了系统的基础法则。我们将领域模型、API 契约和历史架构决策文档化，作为 Agent 初始化的基线上下文。动态上下文决定了 Agent 在运行时的决策质量。系统需要将实时的可观测性数据、测试覆盖率报告甚至浏览器导航状态，实时注入到 Agent 的工作流中。缺乏动态上下文的 Agent 就像蒙眼狂奔的打字机，产出的代码在语法上完美，在逻辑上完全脱离系统现状。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">架构约束</span></h2>
<p data-tool="mdnice编辑器">架构约束是确定性的防线。</p>
<p data-tool="mdnice编辑器">完全依赖 LLM 进行自我反思和代码审查，在生产环境中极度危险。架构约束必须由确定性的自定义代码检查器和结构测试来强制执行。</p>
<p data-tool="mdnice编辑器">我们通过静态分析工具拦截不合规的依赖调用，利用 AST（抽象语法树）解析确保代码分层符合规范。当 Agent 试图在 UI 层直接发起数据库连接时，确定性的检查器会立即阻断该行为，并将具体的错误堆栈和修复路径作为反馈输入给 Agent。这种混合架构确保了系统的底线由死板的规则守卫，Agent 的创造力被严格限制在安全的沙盒内。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">垃圾回收</span></h2>
<p data-tool="mdnice编辑器">垃圾回收主要是用于对抗代码熵增。</p>
<p data-tool="mdnice编辑器">完全自主的智能体引入了代码库衰败的新问题。Agent 会精准且不知疲倦地复现代码仓库中已存在的模式，包含那些不均衡或不够理想的遗留设计。随着时间的推移，这种行为不可避免地导致系统架构漂移。</p>
<p data-tool="mdnice编辑器">最初，人类开发者试图手动处理这个问题。团队过去每周五要花费 20% 的时间清理「AI 残渣」。这种依赖人力的做法毫无可扩展性。</p>
<p data-tool="mdnice编辑器">我们将资深工程师的主观品味转化为机械规则，提炼为「黄金原则」并直接编码到代码仓库中，建立了一个循环清理流程。我们强制要求使用共享的实用程序包，禁止手工编写零散的辅助工具，确保不变式集中管理。我们严禁使用猜测性的数据探测，强制验证边界或依赖类型化的 SDK，防止 Agent 基于虚幻的结构进行构建。</p>
<p data-tool="mdnice编辑器">系统定期运行一组后台 Agent 任务，扫描代码库中的偏差、更新质量等级，并发起有针对性的重构 Pull Request。这些 PR 大多可以在一分钟内完成审查并自动合并。这套机制的功能等同于内存管理中的垃圾回收。技术债务如同高息贷款，通过高频的微小重构不断偿还，远胜过让债务累积到系统崩溃。人类的架构品味一旦被捕获并规则化，就会无情地应用于每一行代码，每天自动发现并消灭不良模式。</p>
<h1 data-tool="mdnice编辑器"><span class="content">AI Agent Harness 的工程化落地</span></h1>
<p data-tool="mdnice编辑器">从几个流行的框架来看，主要是从流程强化、规格沉淀、任务编排等逻辑上来做事情。</p>
<p data-tool="mdnice编辑器">将这些逻辑拆开可以分为四个维度：</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">上下文工程</span></h2>
<p data-tool="mdnice编辑器">上下文工程主要是在规范层解决问题，其主要解决的「规则文件失控」的问题，实现规格沉淀与对齐，以及上下文工程的可控。</p>
<p data-tool="mdnice编辑器">之前，我们习惯把所有规范塞进类似于单个 <code style="color: #ef7060;">.cursorrules</code> 文件，导致 AI 上下文过载且容易忽略细节。这一层落地的第一步是建立结构化、按需加载的规范体系。主要做到如下的点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">规范模块化</strong>：将系统架构、数据库规范、错误处理等拆分为独立的结构化文档。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">按需检索</strong>：AI 不需要每次都通读所有规范，而是根据当前所处的任务阶段，动态检索并加载所需的上下文。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">任务记忆隔离</strong>：为每个独立任务建立物理隔离的工作区和日志。AI 每次开启新会话时，只读取当前任务的精确记忆，既解决了“跨会话失忆”，又屏蔽了无关信息的干扰。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">以 Trellis 框架为例，Trellis 摒弃了单一庞大的全局提示词文件，而是采用 spec/ 目录将规范模块化（如拆分为 database-guidelines.md）。在执行任务时，它利用 tasks/ 目录下的 JSONL 配置文件，让 Agent 动态检索并按需加载上下文。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">架构约束</span></h2>
<p data-tool="mdnice编辑器">架构约束的<strong>核心逻辑：用代码约束代码，实现闭环自愈。</strong> 口头约定或纯文本规范在 AI 面前是脆弱的，它极易为了「跑通逻辑」而破坏架构分层。</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">规则代码化</strong>：将核心的架构依赖规则（例如“前端组件严禁直接调用数据库”）编写为静态分析脚本或自定义 Linter。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">带解释的强阻断</strong>：在代码提交或验证阶段强制执行这些拦截器。关键在于，报错信息不能仅仅是「检查失败」，必须输出高度结构化的指导：明确告诉 AI“为什么违反了规则”以及“正确的做法是什么”。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">自动修复</strong>：AI 读取到结构化的报错指导后，能够自动理解并修正代码，形成无需人类介入的自愈闭环。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">反馈循环</span></h2>
<p data-tool="mdnice编辑器"><strong>核心逻辑：降噪处理，防范死循环。</strong> LLM 的注意力会被长篇大论的日志（如几千行的覆盖率输出）稀释注意力，从而忽略真正致命的错误。 因此我们需要做到：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">零输出原则</strong>：改造验证脚本。如果测试通过，脚本应保持完全沉默；如果失败，只输出精简的错误堆栈和失败原因。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">强制验收清单</strong>：在 AI 试图标记任务「已完成」之前，系统应强制拦截，要求其对照需求文档逐项确认边界条件。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">防死循环干预</strong>：设定重试阈值。如果 AI 对同一文件连续修改多次且测试依然失败，系统应主动中断并强制其回滚代码、重新审视需求，防止 AI 陷入无效的「幻觉修 Bug」循环。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">熵管理</span></h2>
<p data-tool="mdnice编辑器">熵管理主要是阻断「坏模式」的指数级扩散</p>
<p data-tool="mdnice编辑器"><strong>核心逻辑：快速偿还技术债。</strong> AI 复制坏代码的速度是指数级的。一旦允许一个临时的妥协方案合入主分支，AI 会在极短时间内将其复制到整个代码库。</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">高频垃圾收集</strong>：彻底放弃“集中清技术债”的传统做法。每天必须安排固定时间，专门 Review AI 生成的代码（人工或 AI 自动），及时识别新引入的坏模式。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">规范资产的动态演进</strong>：一旦发现坏模式，立即让 AI 深度分析根因，并<strong style="color: #000000;">自动将正确的防范规则更新到规范库中</strong>。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">团队级免疫</strong>：由于规范库与代码同源管理（存在于 Git 仓库中），当这段新规则被提交后，团队其他成员拉取代码时，他们的 AI 助手就能立刻“学会”这个新技能。这把偿还技术债的动作，变成了每天自动化、可积累的系统进化。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">以 Cursor 为例，可以更新 Team Rules</p>
<h1 data-tool="mdnice编辑器"><span class="content">组织级 Harness</span></h1>
<p data-tool="mdnice编辑器">聊完 Agent 的 Harness，再聊一下组织的。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">人的角色已经变了</span></h2>
<p data-tool="mdnice编辑器">大家都知道康威定律，简单来说就是：<strong>设计系统的组织，其产生的设计受限于这些组织的沟通结构。</strong></p>
<p data-tool="mdnice编辑器">而系统设计到最后，也一定会遇到一个问题：<strong>谁来定义规则，谁来解释例外，谁来承担后果。</strong></p>
<p data-tool="mdnice编辑器">以前的软件开发分工相对稳定。PM 写需求，设计出稿，前后端分别实现，测试验证，运维发布。大家各自占一段链路，边界虽然有摩擦，但总体清楚。</p>
<p data-tool="mdnice编辑器">AI 进来以后，边界开始模糊。</p>
<p data-tool="mdnice编辑器">PM 已经可以直接产出前端原型，很多时候产出的还不是静态图，而是真能跑的页面代码。设计师也不再只是给稿子，很多交互和组件约束可以直接沉淀成生成资产。前端工程师花在纯页面搭建上的时间下降，开始更多介入状态管理、交互抽象、可维护性收拢。后端和算法也更早被拉进来，因为很多 AI 生成的原型一开始就会碰到真实数据和能力边界。</p>
<p data-tool="mdnice编辑器">这是现在很多团队正在进行的转型。</p>
<p data-tool="mdnice编辑器">如果组织还按旧的分工运转，Agent 会把协作缝隙快速放大。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">组织级 Harness 要管什么</span></h2>
<p data-tool="mdnice编辑器">我理解的组织级 harness，重点在三件事：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">定义新的协作接口</strong></section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">重新分配注意力</strong></section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">把责任从「谁写了代码」改成「谁定义了系统」</strong></section>
</li>
</ol>
<h3 data-tool="mdnice编辑器"><span class="content">协作接口要前移</span></h3>
<p data-tool="mdnice编辑器">以前很多问题可以留到开发阶段再对齐。现在不行。</p>
<p data-tool="mdnice编辑器">因为 PM 通过 AI 已经能直接产出前端代码，需求不再是文字说明，而可能是一个可交互原型；设计规范也不再只是 Figma 标注，而是可以半自动映射到组件约束；后端接口能力如果不提前讲清楚，前面的生成很容易一路偏到错误方向。</p>
<p data-tool="mdnice编辑器">所以组织里的评审必须前移，重点也得改。</p>
<p data-tool="mdnice编辑器">过去的需求评审，很多时候在讨论功能要不要做。现在要多讨论三件事：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">验收标准到底是什么</section>
</li>
<li>
<section style="color: #010101;">哪些边界不能突破</section>
</li>
<li>
<section style="color: #010101;">哪些部分允许先用原型推进，哪些必须工程化收拢后才能上线</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这几个东西不提前定，后面会出现一个很常见的问题：原型阶段看起来进展飞快，进入工程化后才发现返工巨大。</p>
<h3 data-tool="mdnice编辑器"><span class="content">注意力要重新分配</span></h3>
<p data-tool="mdnice编辑器">我现在越来越少鼓励资深工程师花时间逐行抠低风险代码。</p>
<p data-tool="mdnice编辑器">这不是说 review 不重要，而是注意力要贵着用。</p>
<p data-tool="mdnice编辑器">在 Agent 环境里，重要的工作变成了：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">定义验收标准</section>
</li>
<li>
<section style="color: #010101;">设计架构边界</section>
</li>
<li>
<section style="color: #010101;">提炼黄金原则</section>
</li>
<li>
<section style="color: #010101;">识别系统性失败信号</section>
</li>
<li>
<section style="color: #010101;">决定哪些异常值得阻塞主流程</section>
</li>
<li>
<section style="color: #010101;">审核高风险改动和高影响面重构</section>
</li>
</ul>
<p data-tool="mdnice编辑器">反过来，低风险、重复性、局部性的东西，应该尽量交给自动化校验和后台清理任务。</p>
<p data-tool="mdnice编辑器">如果一个组织还在让最贵的人力去看大批格式化差异、小工具改名、重复样板代码，那 harness 基本等于没有。</p>
<h3 data-tool="mdnice编辑器"><span class="content">责任归属要重写</span></h3>
<p data-tool="mdnice编辑器"><strong>在 AI-Native 组织里，谁对结果负责？</strong></p>
<p data-tool="mdnice编辑器">PM 产出了页面代码，前端做了工程化收拢，Agent 自动补了测试，清理 Agent 又改了一轮共享工具。最后线上出问题，算谁的？</p>
<p data-tool="mdnice编辑器">如果这个问题没有明确答案，团队会很快进入防御状态。每个人都怕接 AI 产出的锅，于是流程开始重新变重，所有人都试图把责任往后传。</p>
<p data-tool="mdnice编辑器">所以组织级 harness 一定要明确责任模型。</p>
<p data-tool="mdnice编辑器">可以按三层分：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #000000;">需求责任</strong>：谁定义了目标与验收标准，谁负责需求正确性</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">架构责任</strong>：谁定义了边界、模式和约束，谁负责系统一致性</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #000000;">发布责任</strong>：谁决定进入生产环境，谁负责风险接受</section>
</li>
</ul>
<p data-tool="mdnice编辑器">不要再执着于「谁手写了这行代码」。就像团队管理一样，最后拍板的人担责。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #ffffff;">可落地的 AI-Native 研发流程</span></h2>
<p data-tool="mdnice编辑器">以下为我们当前在跑的流程：</p>
<h3 data-tool="mdnice编辑器"><span class="content">需求生成</span></h3>
<p data-tool="mdnice编辑器">第一步由 PM 主导，但交付物不再只是 PRD，而是<strong>带验收标准的可运行原型</strong>。</p>
<p data-tool="mdnice编辑器">但是，原型代码不等于可直接上线代码。它的价值是澄清需求、暴露分歧、提前感知交互复杂度。</p>
<p data-tool="mdnice编辑器">所以 PM 可以生成，但不能默认拥有工程决策权。最终所有的代码都需要前端工程师构建的工具链条，以及 AI 和人工的审核及合入。</p>
<h3 data-tool="mdnice编辑器"><span class="content">联合评审</span></h3>
<p data-tool="mdnice编辑器">第二步是全员参与的需求评审与架构设计。设计、前端、后端、算法都要尽早介入。</p>
<p data-tool="mdnice编辑器">这个阶段重点不是抠实现细节，而是确定：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">用户路径是否成立</section>
</li>
<li>
<section style="color: #010101;">数据流怎么走</section>
</li>
<li>
<section style="color: #010101;">状态边界怎么划</section>
</li>
<li>
<section style="color: #010101;">哪些能力用现有服务承接</section>
</li>
<li>
<section style="color: #010101;">哪些模块需要新增抽象</section>
</li>
<li>
<section style="color: #010101;">风险点在哪</section>
</li>
<li>
<section style="color: #010101;">验收怎么自动化</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这这一步的产出结构化进仓库，因为后面它会直接成为 Agent 的约束输入。</p>
<h3 data-tool="mdnice编辑器"><span class="content">工程收拢</span></h3>
<p data-tool="mdnice编辑器">第三步是工程化整合。这个阶段前端、后端、算法开始把前面的原型和需求收敛进正式系统。</p>
<p data-tool="mdnice编辑器">这里 Agent 会大量参与，但人类不能退出。重点工作包括：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">把原型重构进现有组件和模块体系</section>
</li>
<li>
<section style="color: #010101;">校正状态管理、错误处理、埋点、权限、监控</section>
</li>
<li>
<section style="color: #010101;">对接真实接口和算法能力</section>
</li>
<li>
<section style="color: #010101;">补齐类型、边界验证和回归测试</section>
</li>
<li>
<section style="color: #010101;">处理跨模块影响面</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这一段最考验 harness，因为原型代码最容易带着局部最优、全局失真、风格漂移的问题冲进主仓。</p>
<h3 data-tool="mdnice编辑器"><span class="content">自动验证与灰度</span></h3>
<p data-tool="mdnice编辑器">最后一步是自动化测试、灰度发布和反馈回收。</p>
<p data-tool="mdnice编辑器">这一步先由专门的工程团队来负责，加入部分的 AI 成分，固化系统。</p>
<h1 data-tool="mdnice编辑器"><span class="content">从 Agent 到组织，真正难的是控制系统</span></h1>
<p data-tool="mdnice编辑器">很多人以为 AI 落地的核心挑战在模型能力、成本或者工具接入。我现在看，最大挑战更集中在三个词：<strong>环境、反馈回路、控制系统。</strong></p>
<p data-tool="mdnice编辑器">环境决定 Agent 看到了什么、能做什么、不能做什么。</p>
<p data-tool="mdnice编辑器">反馈回路决定错误会被放大，还是会被系统吸收成改进信号。</p>
<p data-tool="mdnice编辑器">控制系统决定生成能力增长之后，组织是变得更稳，还是更乱。</p>
<p data-tool="mdnice编辑器">这三个东西做不好，模型再强也只是更快地产生问题。</p>
<p data-tool="mdnice编辑器">做得好，哪怕模型能力没到最顶尖，系统一样能稳定进化。因为工程上真正稀缺的，从来不是一次惊艳输出，而是长期重复地产出靠谱结果。</p>
<h1 data-tool="mdnice编辑器"><span class="content">组织的 AI-Native 化</span></h1>
<p data-tool="mdnice编辑器">组织的 AI-Native 化也是慢慢进货，逐步推进的，先从小范围试起，再根据结果不断调整规则和流程。并且各家有各家的风格和气质。</p>
<p data-tool="mdnice编辑器">第一，<strong>选一条链路打透</strong>。不要一开始就全组织铺开。先找一个协作关系清楚、反馈周期短、风险相对可控的场景，比如中后台、运营工具、内部系统，或者低风险服务改造。重点不是让 AI 多写代码，而是先验证：信息怎么给、边界怎么定、错误怎么发现、问题怎么清理。</p>
<p data-tool="mdnice编辑器">第二，<strong>先改规则，再谈效率</strong>。很多团队一上来就问产能能提升多少，但更重要的是：规则有没有沉淀下来，错误能不能回流，坏模式能不能及时发现并清掉。如果这些没做好，所谓提效往往只是把问题推后，甚至把混乱放大。</p>
<p data-tool="mdnice编辑器">第三，<strong>把人的位置往上移</strong>。资深工程师要逐渐从大量写代码，转向定规则、画边界、看反馈；技术管理者要从盯人和排期，转向设计流程、分层风险、明确责任；产品可以更早参与原型，但不能越过工程判断。</p>
<p data-tool="mdnice编辑器">组织真正变成 AI-Native，不是因为每个人都在用 Agent，而是协作方式已经围绕 Agent 被重新设计过。</p>
<p data-tool="mdnice编辑器">模型当然重要，但不是决定性因素。真正拉开差距的，是谁先意识到：Agent 不是一个更快的开发者，而是一个高吞吐的生产单元。它会放大环境本身。规则清楚，它就放大规则；流程混乱，它就放大混乱。</p>
<p data-tool="mdnice编辑器">所以到最后，harness 这件事谈的根本不只是 AI。</p>
<p data-tool="mdnice编辑器">谈的是工程纪律怎么重新编码。</p>
<p data-tool="mdnice编辑器">谈的是组织协作怎么重新布线。</p>
<p data-tool="mdnice编辑器">谈的是我们怎么把概率生成系统，放进一个仍然要求长期维护、长期演进、长期负责的软件世界。</p>
<p data-tool="mdnice编辑器">这是我理解的「从 Agent 到组织」。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/05/harness-engineering-anent-org/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自进化 Agent 实现的 4 个层面</title>
		<link>https://www.phppan.com/2026/05/self-improving-agent/</link>
		<comments>https://www.phppan.com/2026/05/self-improving-agent/#comments</comments>
		<pubDate>Sat, 23 May 2026 03:04:48 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[harness]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[Self-Improving Agent]]></category>
		<category><![CDATA[自进化Agent]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2499</guid>
		<description><![CDATA[如果 AI 能像人一样，随着时间，经验，反馈不断学习，会发生什么？ 我们现在常用的 Agent，不管是豆包，还 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器"><strong>如果 AI 能像人一样，随着时间，经验，反馈不断学习，会发生什么？</strong></p>
<p data-tool="mdnice编辑器">我们现在常用的 Agent，不管是豆包，还是编程用的 Cursor，本质是还是一次性对话工程，你问一句，它答一句，或者执行完一堆任务，任务结束后不会成长。</p>
<p data-tool="mdnice编辑器">如果能成长呢？这将会是不一样的世界。</p>
<p data-tool="mdnice编辑器">这也是今年硅谷比较热门的方向。</p>
<p data-tool="mdnice编辑器">为什么是这个方向：</p>
<ol data-tool="mdnice编辑器">
<li>
<section>人性，人天生懒惰，公司逐利</section>
</li>
<li>
<section>成本，自动化的 Agent 比 Chat AI 消耗的成本高出数个数量级，而自己进化的 Agent 所消耗的 token 又比一般的自动化的 agent 要高出多个数量级</section>
</li>
<li>
<section>上下文更长、模型更大、工具更多，这些路线都还有效，但边际收益已经没有前几年那么夸张。当其它维度进入瓶颈的时候，<strong>时间永远是可以考虑的重要维度</strong>，</section>
</li>
</ol>
<p data-tool="mdnice编辑器">自我进化不是「长期记忆」，而是 Agent 依据自身交互情况、任务反馈或环境信号，对上下文、记忆、技能、工作流甚至模型参数进行持续更新。这些更新会直接干预未来的任务执行。</p>
<p data-tool="mdnice编辑器">通过不断调整大模型和 harness 的边界，在静态世界里不断进化，模型能够能够越来越熟悉环境工具记忆等</p>
<p data-tool="mdnice编辑器">任何演进路径都必须压在评估、版本、回滚、权限与供应链治理的基础之上。</p>
<p data-tool="mdnice编辑器">自我进化用一句来讲，大概是这样：</p>
<blockquote class="custom-blockquote multiquote-1" data-tool="mdnice编辑器"><p>真实任务里的经验，怎么变成下一次任务可复用、可验证、可治理的能力。</p></blockquote>
<p data-tool="mdnice编辑器">拆解一下，可以分为四层或者说四种可实现路径：</p>
<ol data-tool="mdnice编辑器">
<li>
<section><strong>上下文进化</strong>：将执行经验、用户偏好或环境约束写回本地记忆文件、会话索引或技能目录，在下一次任务触发时通过检索提取并拼接入上下文。</section>
</li>
<li>
<section><strong>技能进化</strong>：把经验外化成结构化的 SKILL.md、技能包或工作流脚本。系统依据执行报错或反馈信号，自动修改技能代码，在测试集中跑通后覆盖老版本，失败则触发版本回滚。</section>
</li>
<li>
<section><strong>群体智能进化</strong>：多个 Agent、多台机器、多个用户的本地经验接入云端共享层。系统在服务端完成轨迹去重、冲突合并、安全脱敏与质量验证，最后将提纯后的技能或记忆分发回所有终端。</section>
</li>
<li>
<section><strong>策略进化</strong>：将真实的交互轨迹与成败反馈收集起来，直接修改 Agent 的核心调度代码、工作流拓扑，甚至转化为强化学习（RL）的标量奖励来更新大模型的底层参数。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">从工程落地的角度来说，上下文闭环和技能闭环是不错的起始点，也是能快速落地，快速带来结果的点。</p>
<p data-tool="mdnice编辑器">这两层改动的基本都是文本资产，容易审计，且故障可控，回滚成本低。如果实现群体闭环或策略闭环，就会多出很多数据脱敏，权限控制等等问题。</p>
<h1 data-tool="mdnice编辑器"><span class="content">上下文进化</span></h1>
<p data-tool="mdnice编辑器">上下文进化是指让 Agent 在自己的主循环里，将执行经验、用户偏好或环境约束写进以后还能用到的上下文资产。</p>
<p data-tool="mdnice编辑器">典型的上下文资产包括：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>跨会话记忆</section>
</li>
<li>
<section>会话检索</section>
</li>
<li>
<section>用户画像</section>
</li>
<li>
<section>项目级上下文</section>
</li>
<li>
<section>技能目录的动态装载</section>
</li>
<li>
<section>失败后的反思记录</section>
</li>
</ul>
<p data-tool="mdnice编辑器">优点：轻、快、容易落地。</p>
<p data-tool="mdnice编辑器">缺点：如果底层模型本身不够强，光靠上下文很难突破上限；如果没有治理，错误经验会稳定污染后续行为。</p>
<p data-tool="mdnice编辑器">以 Hermes Agent 为例，它将持久记忆（MEMORY.md / USER.md）、跨会话检索（SQLite + FTS5）和技能创建塞进同一个对话主循环。</p>
<p data-tool="mdnice编辑器">工程实现上，Hermes 走的是一条贴紧主循环的轻量闭环。用户下发任务，Agent 经由消息网关调用终端工具。执行反馈与用户纠正产生分叉，一部分写入策展记忆，一部分沉淀为 Skills。当新任务到来，系统通过 session_search 将历史记忆与技能一并汇入下一轮上下文。</p>
<p data-tool="mdnice编辑器">上下文进化解决的问题是Agent 的「金鱼记忆」与重复试错成本。在早期开发中，大模型每次新建会话都会丢失之前的上下文。昨天刚通过多轮对话教会它如何绕过内网的 SSL 证书校验，今天遇到同样的报错，它依然会从零开始盲目重试。上下文进化通过持久化存储（如 SQLite 配合 FTS5 全文检索），让 Agent 在行动前先查阅历史成功路径，直接跳过无效的探索阶段。</p>
<p data-tool="mdnice编辑器">适用于单兵作战的个人助手、轻量级代码副驾、日常办公辅助。只要底层大模型的推理能力在线，且任务经验不需要跨团队、跨设备共享，这是投入产出比最高的一层。它不需要复杂的评测沙箱，几百行代码就能让单体 Agent 的可用性产生质变。</p>
<h1 data-tool="mdnice编辑器"><span class="content">技能进化</span></h1>
<p data-tool="mdnice编辑器">上下文闭环再往前一步，就是把经验沉淀成可复用技能。</p>
<p data-tool="mdnice编辑器">当经验开始重复出现，必须将其从松散的记忆层提升到结构化的技能层。把经验外化成结构化的 SKILL.md、技能包或工作流脚本。系统依据执行报错或反馈信号，自动修改技能代码，在测试集中跑通后覆盖老版本，失败则触发版本回滚。</p>
<p data-tool="mdnice编辑器">在 Agent Skills 生态里，SKILL.md 充当了 Agent 的程序性记忆，定义了触发时机、执行脚本、环境约束和异常处理逻辑。</p>
<p data-tool="mdnice编辑器"><code>SKILL.md</code> 这一类开放技能格式，是这波 Agent 工程里比较实用的中间层，它有如下的特点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>比记忆更结构化</section>
</li>
<li>
<section>比代码改动更轻</section>
</li>
<li>
<section>比参数训练更便宜</section>
</li>
<li>
<section>可迁移、可 diff、可版本化、可回滚</section>
</li>
</ul>
<p data-tool="mdnice编辑器">所以当我们发现某类经验开始重复出现，就不应该继续把它留在记忆层，而应该上升成技能资产。</p>
<p data-tool="mdnice编辑器">Darwin Skill 把 SKILL.md 当作可评测、可回滚的资产。</p>
<p data-tool="mdnice编辑器">Hermes Agent 的技能系统不是静态文档库，它允许 agent 自己创建、编辑、补丁、删文件、写附属文件。核心工具是 <code>skill_manage</code>。[skill_manager_tool.py]</p>
<p data-tool="mdnice编辑器">Hermes Agent 的技能进化，并不完全依赖用户主动说「把这个存成技能」。</p>
<p data-tool="mdnice编辑器">它有两层自动复盘机制。</p>
<p data-tool="mdnice编辑器">第一层是 nudge。memory 按用户回合数触发，skills 按工具迭代次数触发。达到阈值以后，系统会在主任务完成后，后台 fork 一个 review agent，让它复查当前会话，看有没有东西值得落 memory 或 patch/create skill。[run_agent.py] run_agent.py#L2448-L2547 [run_agent.py] run_agent.py#L11586-L11612</p>
<p data-tool="mdnice编辑器">第二层是 guidance。系统提示里直接写明：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>复杂任务、踩坑任务、发现可复用流程，要考虑存技能</section>
</li>
<li>
<section>技能用着发现过时或不完整，要立刻 patch</section>
</li>
</ul>
<p data-tool="mdnice编辑器">它已经把「技能进化」从人工运营动作，拉进了 agent 自己的工作流。系统不再等人整理文档，而是把复盘变成运行时行为。</p>
<p data-tool="mdnice编辑器">但这种触发还是偏软。nudge 只是提醒，review agent 还是模型自己判断。只靠提示词和后台复盘，技能库后面大概率会出现三类问题：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>有价值流程没被沉淀</section>
</li>
<li>
<section>沉淀下来的技能版本缺少来源和上下文</section>
</li>
<li>
<section>技能被 patch 多次以后，质量开始飘</section>
</li>
</ul>
<p data-tool="mdnice编辑器">技能进化解决的问题是纯文本记忆的非结构化缺陷。当任务复杂度上升，大模型在处理几万字的自然语言排错记录时极易丢失细节，甚至产生幻觉。复杂的业务需要确定的执行路径。人工维护这些包含几十个步骤的 SOP（标准作业程序）脚本成本极高，且极易因外部 API 的微小变动而全盘失效。技能进化将脆弱的静态脚本转化为能够依据报错信息自我修复的动态资产。</p>
<p data-tool="mdnice编辑器">其适用于垂直领域的自动化流水线、运维巡检、复杂数据清洗。当业务要求 Agent 严格遵循既定流程操作，且操作环境（如第三方接口、依赖库版本）会频繁发生变化时，技能进化是维持系统长期稳定运行的唯一解。</p>
<h1 data-tool="mdnice编辑器"><span class="content">群体智能进化</span></h1>
<p data-tool="mdnice编辑器">当你有多个 Agent、多台机器、多个用户时，单机技能闭环就不够了。</p>
<p data-tool="mdnice编辑器">因为最大浪费会变成另一件事：<br />
<strong>同一个坑被不同实例反复踩。</strong></p>
<p data-tool="mdnice编辑器">这时候我们就需要一个共享层，把个人经验抽出来，变成全体可复用资产。</p>
<p data-tool="mdnice编辑器">这就是群体闭环要解决的问题。</p>
<p data-tool="mdnice编辑器">它的收益大，但治理也复杂。因为共享意味着：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>权限问题</section>
</li>
<li>
<section>隐私问题</section>
</li>
<li>
<section>脱敏问题</section>
</li>
<li>
<section>质量门控问题</section>
</li>
</ul>
<p data-tool="mdnice编辑器">等等</p>
<p data-tool="mdnice编辑器">Ultron 将散落在各次会话里的经验蒸馏成群体知识，提供 Memory Hub、Skill Hub 和 Harness Hub。</p>
<p data-tool="mdnice编辑器">Memory Hub 实现了 HOT / WARM / COLD 分层存储。系统根据命中次数进行再平衡，引入时间指数热度衰减公式 hotness = exp(-α × days)。未经衰减处理的记忆库是一场灾难，Agent 会频繁召回半年前已经废弃的内部 API 规范。</p>
<p data-tool="mdnice编辑器">数据入库前，系统通过 Presidio 进行中英 PII（个人身份信息）检测与脱敏。这是企业级落地的底线。一旦某个 Agent 将包含真实客户手机号的排错日志写入群体记忆，整个系统的合规风险将彻底失控。</p>
<p data-tool="mdnice编辑器">Skill Hub 负责将进入 HOT 层的记忆结晶为多步工作流技能。Harness Hub 则将人设、记忆、技能打包为蓝图，支持一键导入。这种设计抹平了多实例部署的知识水位差。</p>
<p data-tool="mdnice编辑器">其主要解决的问题是规模化部署下的经验孤岛。如果团队里有 50 个开发人员，每个人的 Agent 都在本地独立摸索公司内部 CI/CD 系统的某一个奇葩报错，相当于团队为同一个坑支付了 50 遍大模型的 API 账单。群体智能进化打破了实例之间的物理隔离，让一个 Agent 踩过的坑成为全团队的免疫抗体，抹平多实例部署的知识水位差。</p>
<p data-tool="mdnice编辑器">其适用场景于企业级 Agent 矩阵、跨部门研发协同、大型内部工具链。团队规模越大，这层进化的网络效应越明显。前提是必须在共享层前置严苛的 PII（个人身份信息）脱敏与恶意 Prompt 注入拦截机制，防止单一终端的脏数据污染全局技能库。</p>
<h1 data-tool="mdnice编辑器"><span class="content">策略进化</span></h1>
<p data-tool="mdnice编辑器">再往下走，就是最重的一层：让系统直接改策略本身。</p>
<p data-tool="mdnice编辑器">这里的策略可能是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>代码</section>
</li>
<li>
<section>工作流拓扑</section>
</li>
<li>
<section>模型参数</section>
</li>
<li>
<section>策略网络</section>
</li>
<li>
<section>推理路径分配</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这层能力上限很高，也很危险。因为一旦我们把真实用户反馈接进训练或策略更新链路，很多以前可以拖着不管的问题会立刻变成硬约束：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>数据许可</section>
</li>
<li>
<section>脱敏</section>
</li>
<li>
<section>训练延迟</section>
</li>
<li>
<section>奖励劫持</section>
</li>
<li>
<section>评测作弊</section>
</li>
<li>
<section>安全回滚</section>
</li>
<li>
<section>服务与训练解耦</section>
</li>
</ul>
<p data-tool="mdnice编辑器">将真实的交互轨迹与成败反馈收集起来，直接修改 Agent 的核心调度代码、工作流拓扑，甚至转化为强化学习（RL）的标量奖励来更新大模型的底层参数。</p>
<p data-tool="mdnice编辑器">其主要解决的问题是基座模型能力天花板的绝对限制。无论是追加记忆还是改写技能，本质上都在做外挂。当遇到模型根本无法理解的深层逻辑或极度复杂的推理链条时，外挂方案会全线崩溃。策略进化直接向底层动刀，利用在线交互产生的真实反馈信号（如代码是否编译通过、测试用例是否全绿）来微调模型权重或重构 Agent 的执行逻辑。</p>
<p data-tool="mdnice编辑器">主要适用场景是拥有充足算力预算的 AI 基础设施团队、需要将开源模型逼出闭源模型效果的核心业务。这一层工程风险极大。任务必须具备极度清晰、可自动化判别的反馈信号（如数学定理证明、代码生成）。如果反馈信号存在噪声，模型会迅速在错误的梯度中崩溃，产生严重的奖励作弊（Reward Hacking）现象。</p>
<h1 data-tool="mdnice编辑器"><span class="content">核心工程挑战</span></h1>
<p data-tool="mdnice编辑器">上面讲了四层进化，但是要落地自进化系统，必须跨越四道工程天堑。</p>
<p data-tool="mdnice编辑器">评估器比生成器更重要。没有评估器的自动修改，只是在自动制造生产事故。Darwin Skill 的棘轮、Ultron 的升级门控、OpenClaw-RL 的 PRM，都在解决同一个问题：证明改动没有让系统变坏。评估器的算力消耗通常是生成器的三倍以上。</p>
<p data-tool="mdnice编辑器">可回滚是自进化的基础设施。技能层的优势在于天然支持版本化。参数层的更新回滚成本极高。工程实践的逻辑是：能在记忆层解决的异常，绝不改写技能；能在技能层修补的逻辑，绝不修改代码；能在代码层绕过的缺陷，绝不在线更新权重。</p>
<p data-tool="mdnice编辑器">共享经验需要严苛治理。群体智能闭环的引入，意味着污染风险的全局放大。一个包含 rm -rf / 的错误技能一旦进入共享库，会摧毁整个团队的开发环境。权限分层、PII 脱敏、候选验证和版本审计，是系统上线的强制前置条件。</p>
<p data-tool="mdnice编辑器">技能供应链安全无法事后补救。Agent Skills 已经演变为跨生态的能力封装格式。它包含脚本、远程依赖和执行指令。技能市场必须审查其是否读取敏感路径、是否执行危险命令、是否下载未知来源的 Shell 脚本。沙箱隔离和系统调用拦截必须做在宿主的最底层。</p>
<p data-tool="mdnice编辑器">当前可落地执行的路线，不要一步到位堆砌在线 RL。可分阶段实施：</p>
<p data-tool="mdnice编辑器">第一阶段，夯实上下文治理。让 Agent 具备最基本的成长能力：记录偏好、总结失败、检索历史。重点是让项目上下文和工具状态形成稳定机制，控制上下文窗口的 Token 消耗。</p>
<p data-tool="mdnice编辑器">第二阶段，跑通技能资产沉淀。当排错经验重复出现，将其提取为 SKILL.md。引入类似 Darwin Skill 的测试集与评估机制。这是当前投入产出比最高的一步，能够立竿见影地降低 API 调用成本。</p>
<p data-tool="mdnice编辑器">第三阶段，建设跨端群体智能。部署共享存储与演化服务，实现多终端经验的去重、验证与分发。在这个阶段，投入 50% 的研发精力解决隐私脱敏与权限审计问题。</p>
<p data-tool="mdnice编辑器">第四阶段，谨慎切入参数与工作流修改。只有当你的 PRM 准确率达到生产可用级别，沙箱隔离足够坚固，版本回滚延迟降到秒级时，才去触碰在线强化学习。</p>
<p data-tool="mdnice编辑器">自进化 Agent 的核心壁垒，从来不是模型本身有多聪明，而是底层的评估、沙箱、治理和安全机制有多扎实。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/05/self-improving-agent/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AI Coding 时代如何有效度量研发效能</title>
		<link>https://www.phppan.com/2026/05/how-to-effectively-measure-rd-efficiency-in-the-ai-%e2%80%8b%e2%80%8bcoding-era/</link>
		<comments>https://www.phppan.com/2026/05/how-to-effectively-measure-rd-efficiency-in-the-ai-%e2%80%8b%e2%80%8bcoding-era/#comments</comments>
		<pubDate>Sun, 10 May 2026 02:26:45 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AI 编程]]></category>
		<category><![CDATA[研发效能]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2496</guid>
		<description><![CDATA[在 AI Coding 时代，当我们说提升效能 50%，从老板的角度，那应该要干掉 50% 的人。 但又不能真 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #191b1f;" data-first-child="" data-pid="IVu4RS9A">在 AI Coding 时代，当我们说提升效能 50%，从老板的角度，那应该要干掉 50% 的人。</p>
<p style="color: #191b1f;" data-pid="dAt3HQ-y">但又不能真这么干，这是一个简单的财务逻辑，也是一种本能的反应。</p>
<p style="color: #191b1f;" data-pid="ZNwfCLRQ">但研发又不是完全标准化的流水线。AI 在各环节都有提速，但对于需求理解的一致性，线上兜底，发布治理这些环节没法完全同步提速。</p>
<p style="color: #191b1f;" data-pid="qEIBeo4i">人是可以砍，但最终系统脆弱性、技术债务和交付风险一起放大。</p>
<p style="color: #191b1f;" data-pid="tWGqq14c">以 AWS 为例，公众知道的类似线上故障发生不止两次了。</p>
<p style="color: #191b1f;" data-pid="7LV30f7z">并且在一些大的公司，也出现了开发人员不知道自己写的是什么的情况，对于大公司一套巨大的工程体系，如果是一个完全的黑盒，没有人知道里面发生了什么，出现大的问题只是迟早的事情。</p>
<p style="color: #191b1f;" data-pid="TZ7boPhN">聊远了点，回到今天的主题，如何有效的度量研发效能。</p>
<p style="color: #191b1f;" data-pid="EzDGBjqe">当我们用上 AI Coding 之后，在上面所说的整体提效的逻辑中，我们需要细化这个效能的度量逻辑。</p>
<p style="color: #191b1f;" data-pid="Mo10ZUqM">同样规模的团队，能不能交付更多有效价值，能不能把交付周期压短，能不能在不明显增加事故和历史债务的前提下，把过去做不到的事情做起来。</p>
<p style="color: #191b1f;" data-pid="L9qMwABL">从指标的逻辑来看，DevOps 里那套大家已经很熟的「四个指标」——部署频率、交付周期、变更失败率、平均恢复时间依然是很能打的一组指标。尤其是 TTM，也就是从需求进入系统到真正产生用户价值的时间，还是王牌指标。</p>
<p style="color: #191b1f;" data-pid="COzAPZba">AI 时代，研发效能的核心指标体系要刷新，但不能推倒重来。</p>
<p style="color: #191b1f;" data-pid="-3RVQXs_">整个度量的逻辑不仅仅在于指标，同时也在于到底把指标绑在什么对象上，怎么解释，怎么防止它们被 AI 放大之后失真。</p>
<h2 style="font-weight: 500; color: #191b1f;">度量谁</h2>
<p style="color: #191b1f;" data-pid="2r4-_NFj">一个问题，AI 产出要不要单独度量？</p>
<p style="color: #191b1f;" data-pid="8ukjJQXs">建议是：可以记录，不要单独考核。</p>
<p style="color: #191b1f;" data-pid="Dgb4vNDM">因为 AI 没有责任主体。它不会背 SLA，不会参加事故复盘，也不会在需求失败时承担后果。</p>
<p style="color: #191b1f;" data-pid="heX0Rcay">如果我们把「AI 生成代码占比」或者「AI 采纳率」当核心 KPI，很快就会把团队带进一个很熟悉的坑：为了优化指标而优化行为，最后牺牲真实交付质量。</p>
<p style="color: #191b1f;" data-pid="fnqMf8bu">研发效能度量一定要有清晰主体。这个主体通常有三层：</p>
<ul style="color: #191b1f;">
<li data-pid="lhJS4prB">工程师个体</li>
<li data-pid="OHI-uZjX">团队</li>
<li data-pid="uWG5WbWq">价值流或产品线</li>
</ul>
<p style="color: #191b1f;" data-pid="00-2A5Jv">在 AI 时代，我更建议把主分析单位放在团队和价值流，但个体数据也需要通晒，人与人还是需要比较，并且人的实际使用情况也需要观测和度量。</p>
<p style="color: #191b1f;" data-pid="by0JkYhW">说到个体度量，可能会有人觉得开始卷了，回到了粗暴管理</p>
<p style="color: #191b1f;" data-pid="w2KM1aHb">我觉得个体数据要看，并且要公开到一定程度。否则团队里谁在真正把 AI 用进生产，谁还停留在传统写法，谁能稳定地产出高质量变更，谁在用 AI 快速制造垃圾代码，管理层根本看不见，辅导也无从下手。问题从来不在于能不能比较，问题在于拿什么比较，以及比较之后准备用它干什么。</p>
<p style="color: #191b1f;" data-pid="3vJhAc_g">可以把「个体可观测」和「个体直接考核」分开。前者要做细，后者要克制。个体层的数据主要解决三类问题：第一，识别使用差异。第二，识别能力短板。第三，识别风险人群。比如有的人 AI 使用频率很高，但交付周期没有改善，返工率还在上升，这通常不是工具问题，是任务拆解、约束描述、结果校验出了问题。还有一种人，表面上 AI 使用不多，但需求稳定、事故极少、关键模块掌控力强，这类人往往承担了大量隐性复杂度，单纯看「人均产出量」会被严重低估。</p>
<p style="color: #191b1f;" data-pid="WuDk_tov">所以个体层该看的，不是「生成了多少」，而是「借助 AI 之后，个人交付行为发生了什么变化」。可以重点盯几组数据：需求从领取到合并的中位时长、PR 平均大小、评审往返次数、回退率、线上问题关联率、测试补充情况、跨模块变更占比、AI 建议采纳后的修改幅度。这里面真正有价值的，不是某个数字本身，而是前后变化和人与人之间的分布差异。一个人 AI 采纳率高，不说明他强；采纳率高、评审一次过、上线后稳定，这才说明工具真的转化成了产能。反过来，另一个人采纳率也高，但 PR 越来越大、review 来回打架、上线后热修频繁，这种数据就已经在报警了。</p>
<p style="color: #191b1f;" data-pid="ZVvzdeRy">个体数据为什么要通晒？</p>
<p style="color: #191b1f;" data-pid="ouAO8bd-">团队协作里，很多能力本来就是相对的。谁的需求吞吐稳定，谁总能把复杂变更压在可控范围里，谁的变更老是把测试链路打爆，谁对 AI 的依赖已经超过了校验能力，这些不能永远藏在「一团和气」后面。通晒的价值是建立真实参照系。工程团队里最怕的一种状态，是大家都觉得自己做得差不多，实际上产出质量、稳定性、问题密度差了一倍以上。数据如果不拉平，组织就只剩印象管理。</p>
<p style="color: #191b1f;" data-pid="iNyUIi68">当然，个体通晒有前提。第一，口径必须统一。第二，不能只晒单一指标。第三，必须配上下文。比如一个人长期负责遗留系统改造，变更失败率高一点、交付周期长一点，很正常；另一个人长期做边缘功能，吞吐高也不稀奇。脱离任务难度做横向比较，最后一定会逼着大家抢简单活。我的经验是，个体层至少要把任务类型、系统风险等级、需求大小这几个维度一起带上。不然表面上是在做精细化管理，实际上是在奖励投机。</p>
<p style="color: #191b1f;" data-pid="0K28qT92">团队层和价值流层还是主轴，因为真正的交付结果最终只能在这两层闭环。一个工程师再强，也没法单独决定发布窗口、联调效率、测试环境、跨团队依赖和灰度策略。AI 时代尤其如此。代码写快以后，瓶颈更容易从个体编码能力转移到团队协作和系统机制上。所以如果一个团队整体 TTM 没降、变更失败率在升、MTTR 也没有改善，那就算团队里有几个人个体数据很亮眼，也不能说明这个组织真的变快了。很多管理者容易被「明星工程师 + AI」的局部高产迷惑，这是很危险的。效能度量最终看的是系统，不是看谁在局部冲得猛。</p>
<p style="color: #191b1f;" data-pid="aqurpqUO">价值流这一层更关键，因为它决定了管理层看到的是局部繁荣，还是真实效率。一个需求从业务提出到用户可用，中间穿过产品、研发、测试、运维、合规、数据、客服，任何一个环节卡住，前面所有 AI 提速都白搭。很多团队以为自己效能不差，问题出在开发不够快；把价值流拉直一看，开发可能只占整个周期的 20%。剩下 80% 都耗在等待、确认、返工和协调上。这个时候你还在研究个人 AI 采纳率有没有上去，说实话，方向已经偏了。</p>
<p style="color: #191b1f;" data-pid="cF2OOyBi">所以，三层都要度量，但角色不同。</p>
<ul style="color: #191b1f;">
<li data-pid="ki8gf0W1">个体层，解决的是识别差异、暴露问题、推动辅导。</li>
<li data-pid="kHeC1hsd">团队层，解决的是交付责任和工程能力。</li>
<li data-pid="1ldhjOSW">价值流层，解决的是端到端效率和组织瓶颈。</li>
</ul>
<p style="color: #191b1f;" data-pid="ap8lvaYI">如果非要再说得更落地一点，可以这样处理：</p>
<ul style="color: #191b1f;">
<li data-pid="MmeanmrQ">个体层数据全量采集，有限通晒，谨慎用于绩效</li>
<li data-pid="tccCqnTa">团队层数据作为正式经营指标，进入月度复盘</li>
<li data-pid="ZI-lbID1">价值流层数据进入管理层决策，用来推动跨部门改造</li>
</ul>
<p style="color: #191b1f;" data-pid="NjkJNbGb">这里还有一个问题：AI 使用情况本身，确实会逐渐演化成个体能力差异的一部分。今天还可以说是工具习惯差异，再过一段时间，它会越来越接近工程生产方式差异。有人能用 AI 快速完成问题拆解、方案比选、代码生成、测试补全、文档整理，再自己完成严格校验；有人只是把 AI 当高级补全，甚至生成什么就提交什么。两者的产出质量和成长速度，一定会拉开。这个差异如果不观测，组织是在主动放弃识别新能力结构的机会。</p>
<p style="color: #191b1f;" data-pid="DIzFj1jY">还是那句话，观测不等于迷信，比较不等于唯排名论。个体层数据要服务于两件事：培养更强的人，提前发现风险。它不能把团队带回那种老派的、只会按数字压人的管理习惯。因为 AI 时代最不缺的，就是表面上很漂亮的数字。真正缺的，是能把这些数字放回具体工程语境里解释清楚的人。</p>
<h2 style="font-weight: 500; color: #191b1f;">量结果</h2>
<p style="color: #191b1f;" data-pid="yTEt8Ykr">我一直不太赞成把研发效能拆成「过程指标 vs 结果指标」两个完全对立的东西。工程里没这么清楚。很多大家嘴里的结果指标，实际上掺了很多过程含义。代码行数就是典型例子。</p>
<p style="color: #191b1f;" data-pid="EcSZBIiF">很多人说 LOC 已经过时，这话只说了一半。更准确一点：LOC 从来就不是好的一线效能指标，在 AI 时代更差。</p>
<p style="color: #191b1f;" data-pid="Ofk5vb4E">原因有三个。</p>
<ul style="color: #191b1f;">
<li data-pid="3Fsyf-2t">生成成本塌了：过去一个人写 500 行和写 50 行，投入成本通常不同。现在 AI 补全、生成、重构、搬迁代码，几分钟就能吐出成百上千行。代码行数和人类投入的相关性被打穿了。</li>
<li data-pid="YvGdAiZq">噪音变大：AI 很容易生成「看起来很完整」的实现：参数校验、日志、适配层、重复样板、冗余抽象全都给你带上。LOC 涨得飞快，但业务价值未必增加。</li>
<li data-pid="sWScR0hh">债务被掩盖：最麻烦的是第三点。AI 会让代码库存增长速度明显快于架构治理速度。表面上看，产出变高了；往后看，维护成本、理解成本、回归成本都在涨。LOC 会把这个问题盖住。</li>
</ul>
<p style="color: #191b1f;" data-pid="02pP4FHu">所以代码行数其实没有啥意义，只当成代码影响范围的辅助信号，别当产出指标。</p>
<p style="color: #191b1f;" data-pid="thOSqhyh">那真正该量什么结果？</p>
<p style="color: #191b1f;" data-pid="MwMd1uSl">可以分三类。</p>
<h2 style="font-weight: 500; color: #191b1f;">交付结果</h2>
<p style="color: #191b1f;" data-pid="A6FKZuLo">这是最基础的一层：</p>
<ul style="color: #191b1f;">
<li data-pid="QkAsvExG">需求交付数量</li>
<li data-pid="VvNo_pOY">需求交付周期</li>
<li data-pid="mL4wgKQ1">按期上线率</li>
<li data-pid="o0G5GCd9">有效发布次数</li>
<li data-pid="V43rmmgA">版本回退率</li>
</ul>
<p style="color: #191b1f;" data-pid="IwUxQtxB">这里要提醒一下：上线需求数也很容易失真。<br />
因为需求切得越碎，上线数越好看；但碎到一定程度，用户感知价值可能没有提升，反而多了协调成本。</p>
<p style="color: #191b1f;" data-pid="xF9LNAJ4">所以需求数量一定要配合需求粒度标准化。最少要把需求分成几类：小修复、小功能、中型功能、跨系统项目、技术治理项。不同类型分开统计，不然一张表里什么都看不出来。</p>
<h2 style="font-weight: 500; color: #191b1f;">业务结果</h2>
<p style="color: #191b1f;" data-pid="q8nXaFw-">如果团队做的是业务研发，最终还是得往用户影响上落。常见的：</p>
<ul style="color: #191b1f;">
<li data-pid="s1zZpvY6">某类功能从提出到用户可用的时间</li>
<li data-pid="v_GT3W2K">用户覆盖范围</li>
<li data-pid="osoa_Vn-">功能使用率</li>
<li data-pid="QMerzRBb">转化率变化</li>
<li data-pid="_4Tuinjl">关键漏斗改善</li>
<li data-pid="dEOqMMDq">客诉下降</li>
<li data-pid="XfrbvdpW">线上稳定性对收入的影响</li>
</ul>
<p style="color: #191b1f;" data-pid="I9jr-7Sy">很多技术管理者不敢碰业务指标，怕研发背锅。我反而觉得该看，但不要简单归因。业务结果可以做关联分析，不要粗暴做责任归因。比如某个推荐功能两周上线了，但实验效果一般，这不等于研发效能差。研发效能差，应该体现在 TTM 过长、试验成本过高、回滚困难、迭代慢，而不是实验结论本身不好。</p>
<h2 style="font-weight: 500; color: #191b1f;">工程结果</h2>
<p style="color: #191b1f;" data-pid="4IqON1Ms">如果团队做的是平台、基建、中间件、工具链，不能硬套 GMV、转化率这种业务指标。这个场景下我更看这些：</p>
<ul style="color: #191b1f;">
<li data-pid="aYGH5qu0">接入团队数</li>
<li data-pid="X5qUKLw2">接入周期</li>
<li data-pid="AI3SJ2Zx">平台能力调用成功率</li>
<li data-pid="xzEOu__5">构建时长变化</li>
<li data-pid="GbNG_Jg3">测试时长变化</li>
<li data-pid="xl9nSavZ">故障发现提前量</li>
<li data-pid="FlMu7dBw">资源成本变化</li>
<li data-pid="omAh5Gbe">事故数和事故影响面</li>
<li data-pid="BSFdugjA">研发人均可支撑服务数</li>
</ul>
<p style="color: #191b1f;" data-pid="u5npbZMy">基建团队最吃亏的一点，是价值释放往往滞后，而且分散在别人的效率提升里。如果你只盯上线需求数，这类团队会被系统性低估。</p>
<h2 style="font-weight: 500; color: #191b1f;">TTM 还是核心指标</h2>
<p style="color: #191b1f;" data-pid="QmGaOUYg">如果只能保留一个指标，我还是会选 TTM。</p>
<p style="color: #191b1f;" data-pid="ASRfRqhv">这里说的 TTM，不是立项到上线的日历时间，而是价值从进入研发系统到到达用户手里的有效时间。很多公司嘴上讲敏捷，实际上量的是开发开始到提测结束，这根本不是 TTM。</p>
<p style="color: #191b1f;" data-pid="moUbsype">为什么 TTM 在 AI 时代更重要？</p>
<p style="color: #191b1f;" data-pid="Fw618ypu">因为 AI 最直接改变的，就是局部生产速度。代码写快了，单测补得快了，文档初稿也快了。问题是，这些加速会不会真的传导到业务价值交付，完全不确定。</p>
<p style="color: #191b1f;" data-pid="Rwe7RQZb">我见过最典型的一种情况：</p>
<ul style="color: #191b1f;">
<li data-pid="tmwc70ow">开发编码时间下降 40%</li>
<li data-pid="rvWlNrSt">PR 数量上涨 2 倍</li>
<li data-pid="j6-ui5QP">评审负担上涨 60%</li>
<li data-pid="PCEdsesx">测试回归时长上涨 35%</li>
<li data-pid="pRCOOcPx">发布前冻结窗口变长</li>
<li data-pid="04WiF7Xr">最终需求上线周期几乎没变</li>
</ul>
<p style="color: #191b1f;" data-pid="lbIUJFov">如果你只看编码侧的数据，会得出一个完全错误的结论：AI 大幅提升了效能。<br />
如果你看 TTM，问题一下就暴露了：加速发生在局部，瓶颈转移到了下游。</p>
<p style="color: #191b1f;" data-pid="AUsucYQ3">所以 TTM 的价值就在这里。它不关心你用了什么先进工具，它只看最终有没有把价值更快交出去。</p>
<h2 style="font-weight: 500; color: #191b1f;">TTM 怎么拆</h2>
<p style="color: #191b1f;" data-pid="0Rg8mrLo">TTM 不能只看一个总时长，否则只能看热闹。要拆段看：</p>
<ul style="color: #191b1f;">
<li data-pid="uxeHjF7A">需求澄清耗时</li>
<li data-pid="BeyHcHDL">方案设计耗时</li>
<li data-pid="AtpEm18h">开发耗时</li>
<li data-pid="bzGRwf6L">评审耗时</li>
<li data-pid="-N7fJkZ4">测试耗时</li>
<li data-pid="TqE3i5PO">等待发布耗时</li>
<li data-pid="ik50TGUz">灰度验证耗时</li>
</ul>
<p style="color: #191b1f;" data-pid="KOsIiN92">拆完之后，你才知道 AI 真正帮到了哪一段，堵在了哪一段。</p>
<p style="color: #191b1f;" data-pid="dj-o9Z1h">另外，在 AI 时代，这种拆段是否合理，是否可以合并不同阶段或角色，让一些信息的流转内化为个人的思考逻辑，实现端到端的 AI 化。</p>
<p style="color: #191b1f;" data-pid="Ihsm4Zah">比如，产品经理直接从需求到前端代码的实现。</p>
<h2 style="font-weight: 500; color: #191b1f;">看分布，不看均值</h2>
<p style="color: #191b1f;" data-pid="TmxjSanu">TTM 最忌讳只看平均值。平均值非常会骗人。正确看法至少要有：</p>
<ul style="color: #191b1f;">
<li data-pid="I_qfQtDl">P50</li>
<li data-pid="IPNJGfZp">P75</li>
<li data-pid="thx1i-B9">P90</li>
<li data-pid="j-wRqbMd">超过阈值的长尾需求占比</li>
</ul>
<p style="color: #191b1f;" data-pid="wqmoyNWp">因为 AI 往往会优先优化简单需求。这样 P50 可能很好看，P90 反而更差。原因也简单，简单需求被快速吞掉之后，系统里剩下的都是复杂需求、遗留系统改造、跨域联动项目，长尾会更长。</p>
<p style="color: #191b1f;" data-pid="Ajn-ES1p">如果你只看均值，会误判整个系统在变快。</p>
<h2 style="font-weight: 500; color: #191b1f;">四个指标</h2>
<p style="color: #191b1f;" data-pid="XNGrSMKS">DevOps 四指标在今天依然能打，但口径需要有一些升级。</p>
<h2 style="font-weight: 500; color: #191b1f;">部署频率</h2>
<p style="color: #191b1f;" data-pid="G526zvCv">部署频率比 LOC 强太多。因为它至少是靠近真实交付动作的。代码写了多少行没人关心，真正有意义的是你有没有把变更安全地推到生产环境。</p>
<p style="color: #191b1f;" data-pid="INqvioNN">但部署频率也不能裸看。</p>
<h3 style="font-weight: 500; color: #191b1f;">高频不等于高效</h3>
<p style="color: #191b1f;" data-pid="9Pvujplc">AI 上来之后，一个常见变化就是团队更愿意切小 PR，发小版本。这个方向本身没问题，小批量交付通常更安全。问题在于，有些团队把一个正常需求拆成大量低价值、低独立性的碎发布，频率上去了，用户价值没上去，测试和发布成本反而更高。</p>
<p style="color: #191b1f;" data-pid="Wm8g-Gf3">所以我一般会同时看三个维度：</p>
<ul style="color: #191b1f;">
<li data-pid="tWEQV_az">单位时间部署次数</li>
<li data-pid="o1udSqTv">每次部署的有效变更量</li>
<li data-pid="dDx3CjAY">每次部署的独立可验证价值</li>
</ul>
<p style="color: #191b1f;" data-pid="0NxPQ2An">没有后两项约束，部署频率很容易变成表演数据。</p>
<h3 style="font-weight: 500; color: #191b1f;">适用场景不同</h3>
<p style="color: #191b1f;" data-pid="gKpckYe-">业务团队和基建团队的部署频率口径也不能完全一样。</p>
<p style="color: #191b1f;" data-pid="F_w1qKOB">业务团队可以看：</p>
<ul style="color: #191b1f;">
<li data-pid="aPKRqnVV">每周或每日生产发布次数</li>
<li data-pid="SvX6dLBl">灰度发布次数</li>
<li data-pid="SwRpzCJB">从功能完成到触达用户的次数</li>
</ul>
<p style="color: #191b1f;" data-pid="kssJTadn">基建团队更适合看：</p>
<ul style="color: #191b1f;">
<li data-pid="3oq55QE4">核心服务变更发布频率</li>
<li data-pid="e-05D7cI">非工作时段紧急发布占比</li>
<li data-pid="iQjAwCss">配置变更自动化发布比例</li>
<li data-pid="sP3lVTt-">平台工具链版本迭代频率</li>
</ul>
<p style="color: #191b1f;" data-pid="RblJNaXr">基建团队很多时候更强调稳定和兼容，追求极端高频未必合理。比如数据库、中间件、网关这类系统，频率太高反而可能说明变更管理有问题。</p>
<h2 style="font-weight: 500; color: #191b1f;">交付周期</h2>
<p style="color: #191b1f;" data-pid="JGgghvkd">交付周期本质上是 TTM 在工程流水线里的展开版。通常从代码提交到生产运行，也有人从需求进入开发开始统计。具体口径可以按团队定，但必须固定。</p>
<p style="color: #191b1f;" data-pid="OxJR-LFE">AI 时代交付周期最容易出现两个假象。</p>
<ul style="color: #191b1f;">
<li data-pid="nMYzPDzt">
提交变快了，交付没变快： 这是前面说的局部提速问题。AI 让提交更快，但后续验证没跟上，周期不会实质下降。</li>
<li data-pid="ycOdt9MI">
PR 变多了，周期看起来更短：如果团队把一个需求拆成多个极小 PR，每个 PR 周期都很短，报表会很好看。但从需求视角看，整体仍然很慢。所以我建议同时维护两套口径：一个是 PR 级交付周期，另一个是需求级交付周期。PR 级用于观察工程流水线摩擦，需求级用于看真实业务交付。只看其中一个，都会失真。</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">变更失败率</h2>
<p style="color: #191b1f;" data-pid="BBM-FWTz">这个指标在 AI 时代的重要性实际上上升了。</p>
<p style="color: #191b1f;" data-pid="qnC0YhK0">因为生成式编码提高了变更速度，也提高了「错误以正确形式出现」的概率。以前很多错误是写不出来，现在是能很快写出一个逻辑闭环、风格统一、还能过部分测试的错误实现。它更隐蔽，也更容易通过表面检查。</p>
<p style="color: #191b1f;" data-pid="P_l2T_ls">可以把变更失败率定义得更工程化一点，至少包含这些事件：</p>
<ul style="color: #191b1f;">
<li data-pid="7P0jO_LP">发布后触发回滚</li>
<li data-pid="5axg9ai8">发布后引发 Sev 事故</li>
<li data-pid="BYjvmIAv">发布后触发热点修复</li>
<li data-pid="r-iw2QFp">发布后造成核心指标异常</li>
<li data-pid="JUMYVtIo">发布后引起客户可感知故障</li>
</ul>
<p style="color: #191b1f;" data-pid="mSM9CjYy">如果定义太窄，只算重大事故，这个指标会钝化。定义太宽，又会把正常试错算进去。团队需要自己定边界，但边界一旦定了就别频繁改。</p>
<h3 style="font-weight: 500; color: #191b1f;">AI 对失败率的影响机制</h3>
<p style="color: #191b1f;" data-pid="lcwAt09n">这里有几个常见来源：</p>
<ul style="color: #191b1f;">
<li data-pid="0oU-t3md">生成代码对边界条件覆盖不足</li>
<li data-pid="2y3BBf3O">引入不必要抽象，增加理解偏差</li>
<li data-pid="I2ROqU-N">与历史代码风格和隐式约束不一致</li>
<li data-pid="mbkOVjzr">测试代码同样由 AI 生成，出现「同源缺陷」</li>
<li data-pid="chUuAl8n">变更面比工程师主观预期更大</li>
</ul>
<p style="color: #191b1f;" data-pid="LxiDiugE">我自己比较警惕第四点。很多团队现在喜欢让 AI 顺手补测试，看起来很完整。但如果实现和测试都建立在同一段错误理解上，测试通过并不能证明正确。</p>
<p style="color: #191b1f;" data-pid="LKR_j45m">所以在高风险变更里，测试不能只依赖生成。关键路径一定要有人做反例设计。</p>
<h2 style="font-weight: 500; color: #191b1f;">平均恢复时间</h2>
<p style="color: #191b1f;" data-pid="qMaDfKJG">很多团队对 MTTR 的重视程度不够，尤其是业务团队。实际上，AI 时代恢复能力的重要性在上升。</p>
<p style="color: #191b1f;" data-pid="HNQOznIK">当代码生成速度变快，进入生产环境的变更更多、更碎、更频繁，系统面对故障的概率和复杂度都在变化。你不一定能把每次变更都做得完美，但你必须能快速止血。</p>
<p style="color: #191b1f;" data-pid="fqCL_3SW">MTTR 的价值在于衡量团队是否真正具备工程韧性。这个指标背后对应的是一整套能力：</p>
<ul style="color: #191b1f;">
<li data-pid="Pz-OruGC">监控和告警是否有效</li>
<li data-pid="aaLz8Ap9">变更是否可追踪</li>
<li data-pid="MSeoCgGq">是否支持快速回滚</li>
<li data-pid="jHZKszXk">灰度和开关能力是否完善</li>
<li data-pid="2q1dW4wM">值班机制是否靠谱</li>
<li data-pid="IJuv-rbG">故障定位信息是否充分</li>
</ul>
<p style="color: #191b1f;" data-pid="xquZD19w">很多团队前面三个指标都好看，MTTR 很差。这样的系统经不起规模化 AI 变更。因为一旦出事，恢复慢会把前面所有频率和周期优势全吃掉。</p>
<h2 style="font-weight: 500; color: #191b1f;">历史债务</h2>
<p style="color: #191b1f;" data-pid="ygazSmpA">AI Coding 真正会在一年后把团队拉开差距，这里的差距在于谁更早处理历史债务。</p>
<p style="color: #191b1f;" data-pid="5h5_5jWc">这个问题现在还没有被足够重视。但是实际中已经越来越严重了。</p>
<p style="color: #191b1f;" data-pid="abnds2kY">因为短期内 AI 会制造一种繁荣感：交付更快，PR 更多，需求吞吐上升。可如果代码库质量、模块边界、测试资产、文档一致性没有同步改善，债务会以更快速度积累。</p>
<h2 style="font-weight: 500; color: #191b1f;">AI 会怎样放大债务</h2>
<p style="color: #191b1f;" data-pid="KZ7Yt7jP">几个很典型的模式。</p>
<ul style="color: #191b1f;">
<li data-pid="GsAYz9NY">
重复实现增多：工程师直接让 AI 在局部上下文里生成功能，很容易绕过现有抽象和公共能力。短期最省事，长期就是重复轮子到处长。</li>
<li data-pid="JSX96oYz">
中间层膨胀：AI 很擅长生成 adapter、wrapper、facade 这类看起来规整的中间层。问题是很多层根本没有必要，只是为了让局部代码更顺。半年后系统会变得非常厚，排障和重构都很痛苦。</li>
<li data-pid="h6lYsMZY">
测试资产劣化：生成测试很快，真正有效的测试不快。团队如果只追求覆盖率，很快就会积累大量低价值测试：断言脆弱、依赖实现细节、执行慢、维护成本高。最后 CI 时间越来越长，大家开始跳过测试。</li>
<li data-pid="gaGUryfk">
文档与实现漂移更快：AI 可以快速生成设计说明、变更记录、接口文档初稿。但只要流程里没有强约束，这些文档过几周就过时。文档数量增加，不代表知识管理更好。</li>
<li data-pid="aibQQ65b">
债务怎么量：技术债务很难精确，但不代表不能量。至少看四组信号：</p>
<ul>
<li data-pid="p8yTS_vP">重复代码比例或重复能力点数量</li>
<li data-pid="32syOwRL">关键模块复杂度变化</li>
<li data-pid="g1nGn0Qc">测试执行时长与稳定性</li>
<li data-pid="4wba2UZM">历史模块变更失败率和恢复时长</li>
</ul>
</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">指标落地</h2>
<p style="color: #191b1f;" data-pid="iAIEvjCk">说一堆指标，如果最后只是做一张月报，那基本没用。研发效能度量要落地，大概可以看三点：统一口径、自动采集、和决策绑定。</p>
<h2 style="font-weight: 500; color: #191b1f;">统一口径</h2>
<p style="color: #191b1f;" data-pid="bVk2jGm_">很多团队最大的问题不是没数据，是每个人嘴里同一个词代表不同意思。</p>
<p style="color: #191b1f;" data-pid="Bk-6ZvvW">比如「交付周期」，有人从排期开始算，有人从开始开发算，有人从代码提交算。你拿这三种数据做横向比较，结论一定错。</p>
<p style="color: #191b1f;" data-pid="hncg-XWl">所以第一件事就是给每个指标下工程定义：</p>
<ul style="color: #191b1f;">
<li data-pid="y-Pixiwi">起点是什么</li>
<li data-pid="zb98rLnb">终点是什么</li>
<li data-pid="KVugRoEP">谁负责标记状态</li>
<li data-pid="_3Gx-vke">异常情况怎么处理</li>
<li data-pid="hzksKm4Y">统计周期是什么</li>
<li data-pid="7cHI1kMR">看均值还是分位数</li>
</ul>
<p style="color: #191b1f;" data-pid="eezNBvp6">这一步很枯燥，但躲不过去。没有口径统一，报表越华丽越危险。</p>
<h2 style="font-weight: 500; color: #191b1f;">自动采集</h2>
<p style="color: #191b1f;" data-pid="Z0jGaiOB">凡是靠人手填的效能数据，最后都会漂。</p>
<p style="color: #191b1f;" data-pid="OQG9Jewc">我建议优先从这些系统自动取数：</p>
<ul style="color: #191b1f;">
<li data-pid="X0In9PO7">需求系统</li>
<li data-pid="mA_kVXRz">Git 仓库</li>
<li data-pid="bpLQIvE0">CI/CD 平台</li>
<li data-pid="Hi_IocmK">测试平台</li>
<li data-pid="RJn7dpmL">线上监控与事故系统</li>
<li data-pid="xI6CktxF">发布平台</li>
<li data-pid="9WAl_wFR">值班和告警系统</li>
</ul>
<p style="color: #191b1f;" data-pid="VQkbQjEI">AI 时代还可以补一些辅助数据，比如：</p>
<ul style="color: #191b1f;">
<li data-pid="KzLHMDVK">AI 建议采纳率</li>
<li data-pid="XpPXJQpI">AI 生成代码在最终提交中的占比</li>
<li data-pid="6aERWQs1">AI 相关变更的回退率</li>
<li data-pid="tng7_tSu">AI 生成测试的失败分布</li>
</ul>
<p style="color: #191b1f;" data-pid="GGLLuM6F">但这些只建议做诊断维度，不建议直接进核心看板。</p>
<h2 style="font-weight: 500; color: #191b1f;">和决策绑定</h2>
<p style="color: #191b1f;" data-pid="PREbVUeD">指标如果不进入真实决策流程，就只会变成汇报材料。</p>
<p style="color: #191b1f;" data-pid="TsC-RXSw">可以把不同指标绑定到不同节奏：</p>
<h3 style="font-weight: 500; color: #191b1f;">周维度</h3>
<p style="color: #191b1f;" data-pid="KL2CVOHi">团队看流水线摩擦：</p>
<ul style="color: #191b1f;">
<li data-pid="bobe4HsZ">PR 周期</li>
<li data-pid="jLEUScfh">构建失败率</li>
<li data-pid="NuAVqwTE">测试时长</li>
<li data-pid="Nw6HHAiw">发布次数</li>
<li data-pid="BH9E7MQa">热修次数</li>
</ul>
<h3 style="font-weight: 500; color: #191b1f;">月维度</h3>
<p style="color: #191b1f;" data-pid="Dau-jjfT">管理者看交付和稳定性：</p>
<ul style="color: #191b1f;">
<li data-pid="dqxXlamS">TTM 分布</li>
<li data-pid="XjI-ezh3">部署频率</li>
<li data-pid="MsOGV517">变更失败率</li>
<li data-pid="Yka2UGI-">MTTR</li>
<li data-pid="qlR1AuPI">技术债务信号</li>
</ul>
<h3 style="font-weight: 500; color: #191b1f;">季度维度</h3>
<p style="color: #191b1f;" data-pid="QqJvGoaU">看结构性问题：</p>
<ul style="color: #191b1f;">
<li data-pid="LPfjP-oZ">跨团队依赖导致的等待占比</li>
<li data-pid="00lrllyM">核心系统复杂度变化</li>
<li data-pid="CfxJeJMt">自动化覆盖关键路径的比例</li>
<li data-pid="jLgLFGqC">平台能力复用率</li>
<li data-pid="PzGyLzDN">人均支撑规模变化</li>
</ul>
<p style="color: #191b1f;" data-pid="pgoWAJXC">只有当指标驱动了人力投入、架构治理、流程调整，度量才算产生价值。</p>
<h2 style="font-weight: 500; color: #191b1f;">常见误区</h2>
<ul style="color: #191b1f;">
<li data-pid="QOQmSbk-">
误区一： 拿 AI 使用量替代研发效能。 比如：人均每天调用 AI 多少次，AI 生成了多少代码，采纳率多少，这些数据可以看工具渗透率，不能代表团队变快了，更不能代表交付变好了。高采纳率有时候只是因为团队在写更多样板代码。</li>
<li data-pid="0ce_Kofn">
误区二：把效能问题当管理问题。 一看到周期长，就加日报、加审批、加同步会。这个思路在 AI 时代更危险，因为编码加速之后，组织摩擦会成为主要瓶颈。继续加管理动作，只会让非编码时间更长。效能最后还是要回到工程根上：架构边界、测试自动化、环境一致性、发布能力、可观测性、模块治理。</li>
<li data-pid="msFpQWO8">
误区三：把所有团队放在一套指标下排序。业务、基建、平台、安全、数据团队的工作性质差异很大。统一看板可以有，统一排名很容易把组织带偏。</li>
<li data-pid="Mlo4WvVf">
误区四：只看短期提速，不看长期维护成本。 AI 最容易制造的错觉就是这个月吞吐上涨了，于是大家默认效能提升。可如果未来三个月回归变慢、故障变多、重构困难，这个月的漂亮报表只是透支。</li>
<li data-pid="vMs2WjiX">
误区五：只看平均值。平均值会把长尾、异常、结构性阻塞全部抹平。工程管理里，很多真正该解决的问题都藏在 P90 之后。</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">最后</h2>
<p style="color: #191b1f;" data-pid="6Asi1Ee4">研发效能从来不是一个分数问题，它是一个约束系统问题。得回答下面的问题：</p>
<ul style="color: #191b1f;">
<li data-pid="ElDGnJpT">价值有没有更快交出去</li>
<li data-pid="-AgoudLo">变更是不是足够安全</li>
<li data-pid="pOjxmh86">出问题能不能快速恢复</li>
<li data-pid="20QgHh0y">现在的速度有没有透支未来</li>
</ul>
<p style="color: #191b1f;" data-pid="t_8zndsp">这四件事里，TTM 仍然是排第一的。因为业务不会为你写了多少代码买单，也不会为你调了多少次 AI 买单。业务只关心一件事：价值多久能到用户手里。</p>
<p style="color: #191b1f;" data-pid="wA6d0OF9">AI Coding 改变了工程生产函数。</p>
<p style="color: #191b1f;" data-pid="ZbPrYsQk">但它没有改变研发效能的基本物理规律。局部速度不等于系统速度，生成能力不等于交付能力，短期吞吐不等于长期效率。</p>
<p style="color: #191b1f;" data-pid="0IdJ6p9j">不要急着发明一套全新的效能宗教。先把工程里的主干指标守住，把主体放对，把业务团队和基建团队分开看，把技术债务纳入视野，再去观察 AI 到底在哪些环节真的创造了增益。</p>
<p style="color: #191b1f;" data-pid="lFZELd7A">这样量出来的数据，才能指导决策。否则报表再热闹，也只是另一种噪音。</p>
<p style="color: #191b1f;" data-pid="2-PyPPP3">以上。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/05/how-to-effectively-measure-rd-efficiency-in-the-ai-%e2%80%8b%e2%80%8bcoding-era/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>对最近 AI 落地工程实践的一些想法和思考</title>
		<link>https://www.phppan.com/2026/04/some-thoughts-and-reflections-on-recent-ai-implemented-engineering-practices/</link>
		<comments>https://www.phppan.com/2026/04/some-thoughts-and-reflections-on-recent-ai-implemented-engineering-practices/#comments</comments>
		<pubDate>Sat, 25 Apr 2026 00:56:17 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[AI幻觉]]></category>
		<category><![CDATA[AI架构]]></category>
		<category><![CDATA[RAG]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2493</guid>
		<description><![CDATA[最近和小区某上市公司的 CFO 喝茶聊 AI，在过程中思维和实际场景的碰撞，记录如下： 穿透复杂的表象，当前  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #424b5d;" data-tool="mdnice编辑器">最近和小区某上市公司的 CFO 喝茶聊 AI，在过程中思维和实际场景的碰撞，记录如下：</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">穿透复杂的表象，当前 LLM 的底层运行逻辑其实非常单一：它本质上是一个自回归的序列生成器，根据已有的上下文，计算词表中每一个 token 出现的概率分布，然后从中采样出下一个 token。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">但这里的「概率」绝非毫无逻辑的随机掷骰子。 这种概率分布，是模型在海量预训练数据中内化的语言规律、世界知识以及逻辑推理能力的数学投影。通过多层 Transformer 网络与注意力机制（Attention），模型在极高的维度上完成了对上下文语义的深度解析与特征关联，从而将符合人类逻辑、契合当前语境的 token 赋予极高的概率权重。它是在用统计学的方式，重现人类的逻辑推理过程。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">然而，无论其内部的概率计算多么精密，从软件工程的宏观视角来看，我们本质上依然是在传统的确定性系统中，强行引入了一个基于概率采样的非确定性组件。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">传统软件工程建立在严格的确定性之上。输入特定的参数，经过固定的业务逻辑，必然得到预期的输出。现在我们将核心逻辑交由概率模型处理，相同的输入在不同的时间点，可能会产生完全不同的输出路径。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">幻觉无法被根除。它是自回归模型的内生特性，是概率采样的必然产物。我们在进行系统架构设计时，必须将幻觉视为系统的常态。试图通过修改 Prompt 来彻底消除幻觉，在工程上徒劳无功。我们需要在系统边界处建立起拦截机制，用确定性的规则去兜底概率模型的不确定性。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">容错度决定落地</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">当前商业化落地最顺畅、ROI 最高的场景，全部集中在高容错度领域。写行业报告、生成营销文案、文生图、视频生成、游戏 NPC 对话。这类场景的核心特征在于缺乏绝对的客观标准。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在内容创作领域，模型偶尔的逻辑发散会被用户视为创造力。工程团队不需要在接口的绝对可用性和输出的绝对准确性上死磕，只需要保证底线的内容安全和合理的响应延迟。系统可用性达到 95% 就能让用户产生极强的获得感。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">一旦进入低容错度场景，工程实现的复杂度会呈指数级上升。医疗诊断、工业控制、核心交易链路。在这些领域，0.1% 的幻觉率都会导致灾难性的业务后果。我们在评估一个 AI 项目是否立项时，首要考量指标就是业务场景的容错底线。容错度越低，外围需要的确定性校验代码就越厚重，最终会导致系统的维护成本远超 AI 带来的效率提升。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">知识外挂 RAG</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">RAG 的出现是为了解决模型内部知识更新滞后和私有数据隔离的问题。其核心原理是将外部文档切片、向量化，在用户提问时检索相关切片，拼接到 Prompt 中作为上下文喂给大模型。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在实际的工程环境里，RAG 的核心瓶颈在检索链路。切片策略直接决定了召回质量。按固定 token 长度切分会破坏语义完整性，导致关键信息被腰斩。按标点符号或段落切分会导致切片长度方差过大，影响向量化模型的表达能力。我们在生产环境中通常需要针对不同格式的文档编写定制化的解析器，将 PDF 或 Word 还原为结构化的文档树，再基于文档树的层级进行语义切片。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">单一的向量检索在面对专有名词和长尾词汇时表现极差。我们必须采用混合检索架构：稠密向量检索加上稀疏词表检索。向量检索负责语义泛化，处理同义词和模糊表达。词表检索负责精准匹配产品型号、人名和内部项目代号。混合检索引入了多路召回合并的问题，通常需要引入倒数秩融合算法来重排结果。系统复杂度和查询延迟会成倍增加。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">数据清洗占据了 RAG 项目 80% 的研发精力。直接将企业内部的原始文档灌入向量数据库，最终的问答准确率通常不到 40%。文档中存在大量的废话、过期的流程规范以及相互冲突的条款。垃圾进，垃圾出。我们在构建知识库之前，必须通过脚本和人工介入，对语料进行严格的去重、降噪和结构化提取。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">工具调用确定性</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">为了弥补概率模型的缺陷，我们需要引入确定性的工具。Function Calling 机制本质上是给 LLM 接上双手。模型负责理解自然语言意图并提取结构化参数，具体的业务逻辑交由传统的确定性脚本执行。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器"><strong>工具调用的工程难点在于参数提取的稳定性</strong>。当注册的工具数量超过十个，或者参数结构嵌套层级过深时，模型的输出格式极易崩溃。我们在中间层必须加入严格的 Schema 校验机制。一旦校验失败，需要截断错误信息并触发重试。重试次数上限通常设定为 3 次，继续增加会耗尽上下文窗口并导致请求超时。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">多轮工具调用会带来严重的延迟问题。模型每决定调用一次工具，都需要经历一次完整的网络请求和推理过程。如果一个复杂任务需要串行调用三个工具，用户的等待时间会轻易突破 10 秒。我们在架构设计时，需要尽可能将细粒度的 API 聚合成粗粒度的宏接口，<strong>减少模型与业务系统的交互频次</strong>。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">Agent 架构的脆弱性与状态管理</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">多智能体（Multi-Agent）架构在技术社区被过度神话。多个大模型相互协作、自主规划任务的 Demo 看起来非常惊艳。在真实的工业场景中，完全由 LLM 自主驱动的 Agent 链路极其脆弱。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">误差会在多步推理中被迅速放大。假设单个 Agent 节点的输出准确率为 90%，一个包含五个节点的串行任务，最终的成功率会暴跌至 59%。任何一个节点的幻觉都会导致后续链路彻底跑偏。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">我们在生产环境中构建复杂任务流时，坚决摒弃由 LLM 自主决定执行路径的黑盒模式。控制流必须由传统的有向无环图（DAG）或状态机来接管。LLM 仅仅作为状态机中的一个计算节点，负责处理非结构化数据的理解和生成。节点与节点之间的状态流转、条件判断、异常重试，全部由确定性的代码实现。这种设计牺牲了系统的灵活性，换取了业务系统必须具备的稳定性和可观测性。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">非确定性系统的测试与监控</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">非确定性系统的测试与监控，是传统软件工程团队转型 AI 开发时遇到的最大痛点。传统的单元测试基于断言，期望输出是固定的字符串或数值。面对 LLM 每次都不一样的回答，基于精确匹配的 CI/CD 流水线会全线崩溃。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">我们重构了整个测试评估体系。引入 LLM-as-a-Judge 机制，使用一个能力更强、参数规模更大的模型来评估业务模型的输出质量。评估维度被拆解为相关性、事实一致性、格式合规性等具体指标。在每次模型版本迭代或 Prompt 修改后，必须在包含上千个真实业务 Case 的黄金数据集上运行自动化评估。只有各项指标的波动在可控范围内，才能进行灰度发布。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在监控层面，传统的 APM 工具无法满足需求。我们需要采集每一个请求的 Prompt 模板版本、输入变量、输出结果、Token 消耗量以及推理延迟。这些数据是后续进行 Bad Case 分析和模型微调的唯一原料。针对 Token 消耗的监控直接与业务成本挂钩。我们会在网关层设置严格的并发限制和预算熔断机制，防止恶意请求或死循环调用导致账单失控。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">两种范式的碰撞</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">AI First 与 AI 辅助是完全不同的架构逻辑。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">AI 辅助是在现有系统中打补丁。主干流程依然是传统的表单和按钮，AI 作为一个侧边栏或悬浮窗存在，提供总结、翻译、润色功能。开发成本极低，对原有系统无侵入。用户在遇到问题时，可以选择性地向 AI 求助。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">AI First 要求重构整个交互形态和底层流转逻辑。系统不再依赖预设的菜单树，由 LLM 充当中央路由。用户的自然语言输入直接驱动底层状态机流转。这要求所有内部 API 具备极高的自描述能力，业务逻辑必须高度解耦。我们在推进 AI First 架构时，面临的最大阻力通常来自老旧系统的技术债。历史遗留的紧耦合代码根本无法被封装成独立的工具供模型调用。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">财务场景的拆解</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">财务场景是典型的低容错度、高确定性要求的领域。将概率模型直接应用于财务核心链路会引发严重的合规风险。可落地的切入点集中在外围的非结构化数据处理和信息流转环节。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">发票与报销单据的信息抽取是一个高价值场景。传统 OCR 结合正则匹配在面对版式多变的票据时维护成本极高。引入大模型进行多模态信息抽取，将非结构化的图片或 PDF 转换为结构化的 JSON 数据。抽取后的数据必须经过传统规则引擎的二次校验，例如金额试算平衡验证、税号合规性检查。模型在这里承担的是「粗加工」角色，最终的业务落库动作依然由确定性代码把控。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">财务制度问答可以大幅降低沟通成本。基于企业内部报销规范构建 RAG 系统。员工在提单前通过自然语言查询报销标准。这里的 RAG 必须严格限制模型的发散，Prompt 中需强制要求「仅根据检索到的内容回答，未提及的内容直接回复不知道」。为了防止模型编造财务政策，我们会在输出层增加一层文本相似度校验，确保模型的回答与检索到的原文保持高度一致。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">财务分析报告初稿生成也是一个可行的方向。将结构化的财务报表数据通过代码转换为文本描述，作为上下文喂给模型，让其生成趋势分析和异常波动提示。模型在这里仅作为「翻译官」和「排版员」，不参与任何数值计算。所有的同比、环比计算必须在传统代码层完成，将计算结果以明确的数值形式提供给模型。让 LLM 去做算术题是工程上的反模式。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">数据隐私在财务场景中是不可逾越的红线。公有云 API 无法满足审计要求。我们通常需要采用本地私有化部署的开源模型。7B 到 14B 参数规模的模型经过量化处理后，可以在单张消费级显卡上流畅运行。通过针对财务语料的微调，这些小模型在特定信息抽取任务上的表现可以持平甚至超越千亿参数的通用大模型。私有化部署带来了硬件采购和模型运维的额外成本，需要在项目初期进行严格的 ROI 测算。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">以上</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/04/some-thoughts-and-reflections-on-recent-ai-implemented-engineering-practices/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Claude Code 的 SKILLS 技能渐进式披露实现原理解析</title>
		<link>https://www.phppan.com/2026/04/claude-code-ai-skills-source/</link>
		<comments>https://www.phppan.com/2026/04/claude-code-ai-skills-source/#comments</comments>
		<pubDate>Sun, 12 Apr 2026 03:47:23 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[skills]]></category>
		<category><![CDATA[渐进式披露]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2490</guid>
		<description><![CDATA[SKILLS 和 渐进式披露 是 A 家最早提出来的方案，也是 OpenClaw 火了后大家一直讨论的哪个技能 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-pm-slice="0 0 []">
<p data-tool="mdnice编辑器">SKILLS 和 <strong style="color: #0e88eb;">渐进式披露</strong> 是 A 家最早提出来的方案，也是 OpenClaw 火了后大家一直讨论的哪个技能好用很核心的强依赖的实现逻辑。</p>
<p data-tool="mdnice编辑器">如果把 Claude Code 的 skills 理解成一堆 prompt 文件，后面的很多设计都解释不通。</p>
<p data-tool="mdnice编辑器">从其源码实现来看，会发现它在解决的核心问题是：<strong style="color: #0e88eb;">怎么让模型保留足够强的技能召回能力，同时又不把常驻上下文撑爆。</strong></p>
<p data-tool="mdnice编辑器">这件事说穿了就是五个字：<strong style="color: #0e88eb;">渐进式披露</strong>。</p>
<p data-tool="mdnice编辑器">大概的逻辑是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">先告诉模型「系统里存在 skills 机制」。</section>
</li>
<li>
<section style="color: #010101;">再告诉它「当前有哪些 skill 名称和简短说明」。</section>
</li>
<li>
<section style="color: #010101;">等它真的决定调用某个 skill 时，再把正文、权限、hooks、模型覆盖、附加工具权限这些重内容展开。</section>
</li>
<li>
<section style="color: #010101;">如果某些 skill 还和路径、目录、文件类型绑定，那就继续往后拖，拖到模型真的碰到对应文件时再激活。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这是一个优雅且干净的工程化设计。它没有发明一套复杂到难以维护的 skill runtime，也没有把所谓智能寄托在黑盒检索器上，而是先把「披露成本」这件事控制住。</p>
<p data-tool="mdnice编辑器">我们按工程实现往下拆：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">skill 在系统里到底被建模成什么</section>
</li>
<li>
<section style="color: #010101;">多来源 skill 是怎么统一装配的</section>
</li>
<li>
<section style="color: #010101;">渐进式披露具体分了哪几层</section>
</li>
<li>
<section style="color: #010101;">条件激活和动态发现是怎么接进文件操作链路的</section>
</li>
<li>
<section style="color: #010101;">inline 和 fork 两条执行路径分别解决什么问题</section>
</li>
<li>
<section style="color: #010101;">这套设计真正适合什么场景，代价又是什么</section>
</li>
<li>
<section style="color: #010101;">如果要在自己的 Agent 里复刻，最短落地路径应该怎么走</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">一、先看 skills 在系统里被建模成什么</span></h1>
<p data-tool="mdnice编辑器">Claude Code 里，skill 最终会被统一建模成 <code style="color: #0e8aeb;">Command</code>，而且类型是 <code style="color: #0e8aeb;">prompt</code>。</p>
<p data-tool="mdnice编辑器">最核心的构造函数是 createSkillCommand：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">return</span> {
<span style="color: #c678dd;">type</span>: <span style="color: #98c379;">'prompt'</span>,
  name: skillName,
  description,
  hasUserSpecifiedDescription,
  allowedTools,
  argumentHint,
  argNames: argumentNames.length &gt; <span style="color: #d19a66;">0</span> ? argumentNames : <span style="color: #56b6c2;">undefined</span>,
  whenToUse,
  version,
  model,
  disableModelInvocation,
  userInvocable,
  context: executionContext,
  agent,
  effort,
  paths,
  contentLength: markdownContent.length,
  isHidden: !userInvocable,
  progressMessage: <span style="color: #98c379;">'running'</span>,
  userFacingName(): <span style="color: #e6c07b;">string</span> {
    <span style="color: #c678dd;">return</span> displayName || skillName
  },
  source,
  loadedFrom,
  hooks,
  skillRoot: baseDir,
<span style="color: #c678dd;">async</span> getPromptForCommand(args, toolUseContext) {
    ...
    <span style="color: #c678dd;">return</span> [{ <span style="color: #c678dd;">type</span>: <span style="color: #98c379;">'text'</span>, text: finalContent }]
  },
}
</code></pre>
<p data-tool="mdnice编辑器">这段代码说明有几个关键点：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">skill 不是特殊 runtime object，而是 <code style="color: #0e8aeb;">prompt command</code></section>
</li>
<li>
<section style="color: #010101;">skill 本体是 <code style="color: #0e8aeb;">getPromptForCommand()</code> 生成的一组文本 block</section>
</li>
<li>
<section style="color: #010101;">skill 可以带：</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">allowedTools</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">model</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">effort</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">paths</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">hooks</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">context: inline | fork</code></section>
</li>
</ul>
</li>
<li>
<section style="color: #010101;">skill 的调用结果，不是「执行一段脚本」，而是<strong style="color: #0e88eb;">把 skill 展开成后续对话消息，或者 fork 成子代理执行</strong></section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果我们自己做 Agent，建议参考。skill 不要单独发明一套 DSL runtime，直接把它抽象成「可延迟展开的 prompt 命令」就够了。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">二、skills 的来源有哪几类</span></h1>
<p data-tool="mdnice编辑器">skills 并不只来自一个目录。<code style="color: #0e8aeb;">getSkills()</code> 会把多个来源统一聚合。[commands.ts] commands.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L353</a>-L398</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">const</span> [skillDirCommands, pluginSkills] = <span style="color: #c678dd;">await</span> <span style="color: #e6c07b;">Promise</span>.all([
  getSkillDirCommands(cwd)...
  getPluginSkills()...
])
<span style="color: #c678dd;">const</span> bundledSkills = getBundledSkills()
<span style="color: #c678dd;">const</span> builtinPluginSkills = getBuiltinPluginSkillCommands()
</code></pre>
<p data-tool="mdnice编辑器">然后 <code style="color: #0e8aeb;">loadAllCommands()</code> 再把这些东西和 workflow/plugin/内建命令一起合并。[commands.ts] commands.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L445</a>-L469</p>
<p data-tool="mdnice编辑器">也就是说，skills 的来源至少有：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">bundled skills</section>
</li>
<li>
<section style="color: #010101;">磁盘上的 <code style="color: #0e8aeb;">/skills/</code></section>
</li>
<li>
<section style="color: #010101;">plugin skills</section>
</li>
<li>
<section style="color: #010101;">builtin plugin skills</section>
</li>
<li>
<section style="color: #010101;">兼容旧 <code style="color: #0e8aeb;">/commands/</code> 目录加载进来的 prompt commands</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">SkillTool 根本不需要知道 skill 来自哪里</strong>。只要最后是 <code style="color: #0e8aeb;">prompt command</code>，就能走统一调用路径。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">三、skills 的「渐进式披露」分 5 层</span></h1>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）第一层：系统提示只声明「技能机制存在」</span></h2>
<p data-tool="mdnice编辑器">系统提示里不会把所有 skill 正文直接塞进去。它只给一个能力声明，告诉模型：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">用户说 <code style="color: #0e8aeb;">/&lt;skill-name&gt;</code>，其实是在指 skill</section>
</li>
<li>
<section style="color: #010101;">可以用 <code style="color: #0e8aeb;">SkillTool</code> 去执行</section>
</li>
<li>
<section style="color: #010101;">不要乱猜，只能调用列出来的那些</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这段在 [prompts.ts] prompts.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L353</a>-L401：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;">hasSkills
  ? <span style="color: #98c379;">`/&lt;skill-name&gt; (e.g., /commit) is shorthand for users to invoke a user-invocable skill. When executed, the skill gets expanded to a full prompt. Use the <span style="color: #e06c75;">${SKILL_TOOL_NAME}</span> tool to execute them. IMPORTANT: Only use <span style="color: #e06c75;">${SKILL_TOOL_NAME}</span> for skills listed in its user-invocable skills section - do not guess or use built-in CLI commands.`</span>
  : <span style="color: #56b6c2;">null</span>
</code></pre>
<p data-tool="mdnice编辑器">这一步只暴露了<strong style="color: #0e88eb;">机制</strong>，没有暴露<strong style="color: #0e88eb;">内容</strong>。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）第二层：只披露 skill 名称和短描述</span></h2>
<p data-tool="mdnice编辑器">真正给模型看的 skill 列表，是通过 <code style="color: #0e8aeb;">getSkillToolCommands()</code> 过滤出来的。[commands.ts] commands.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L561</a>-L580</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">return</span> allCommands.filter(
  cmd =&gt;
    cmd.type === <span style="color: #98c379;">'prompt'</span> &amp;&amp;
    !cmd.disableModelInvocation &amp;&amp;
    cmd.source !== <span style="color: #98c379;">'builtin'</span> &amp;&amp;
    (
      cmd.loadedFrom === <span style="color: #98c379;">'bundled'</span> ||
      cmd.loadedFrom === <span style="color: #98c379;">'skills'</span> ||
      cmd.loadedFrom === <span style="color: #98c379;">'commands_DEPRECATED'</span> ||
      cmd.hasUserSpecifiedDescription ||
      cmd.whenToUse
    ),
)
</code></pre>
<p data-tool="mdnice编辑器">这段有两个要点：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">只有 <code style="color: #0e8aeb;">prompt</code> 命令才能进 skill 列表</section>
</li>
<li>
<section style="color: #010101;">并不是所有 prompt command 都自动暴露，至少得满足可描述性要求</section>
</li>
</ul>
<p data-tool="mdnice编辑器">也就是说，<strong style="color: #0e88eb;">可执行集合</strong>和<strong style="color: #0e88eb;">对模型披露集合</strong>不是完全相同的。<br />
Claude Code 在这里收了一刀，避免模型看到一堆没有描述、无法判断用途的技能。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3）第三层：列表本身还要走预算裁剪</span></h2>
<p data-tool="mdnice编辑器">skill 列表不是全量原文塞进 prompt，而是按预算压缩过的。核心逻辑在 [prompt.ts] tools/SkillTool/prompt.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L20</a>-L171。</p>
<p data-tool="mdnice编辑器">最关键的常量：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">export</span> <span style="color: #c678dd;">const</span> SKILL_BUDGET_CONTEXT_PERCENT = <span style="color: #d19a66;">0.01</span>
<span style="color: #c678dd;">export</span> <span style="color: #c678dd;">const</span> DEFAULT_CHAR_BUDGET = <span style="color: #d19a66;">8</span>_000
<span style="color: #c678dd;">export</span> <span style="color: #c678dd;">const</span> MAX_LISTING_DESC_CHARS = <span style="color: #d19a66;">250</span>
</code></pre>
<p data-tool="mdnice编辑器">以及格式化逻辑：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">return</span> <span style="color: #98c379;">`- <span style="color: #e06c75;">${cmd.name}</span>: <span style="color: #e06c75;">${getCommandDescription(cmd)}</span>`</span>
</code></pre>
<p data-tool="mdnice编辑器">和预算裁剪：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">if</span> (fullTotal &lt;= budget) {
  <span style="color: #c678dd;">return</span> fullEntries.map(e =&gt; e.full).join(<span style="color: #98c379;">'\n'</span>)
}
</code></pre>
<p data-tool="mdnice编辑器">如果超预算，就会：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">bundled skills 尽量保留完整描述</section>
</li>
<li>
<section style="color: #010101;">其它 skills 截断 description</section>
</li>
<li>
<section style="color: #010101;">极端情况下退化成只发 <code style="color: #0e8aeb;">- skill-name</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">这就是很典型的渐进式披露：<strong style="color: #0e88eb;">先给最小可用索引，不给正文</strong>。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4）第四层：列表还是增量下发，不是每轮全量重发</span></h2>
<p data-tool="mdnice编辑器">技能列表通过 <code style="color: #0e8aeb;">skill_listing</code> attachment 发给模型。发送逻辑在 [attachments.ts] utils/attachments.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L2669</a>-L2752。</p>
<p data-tool="mdnice编辑器">核心逻辑：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">const</span> newSkills = allCommands.filter(cmd =&gt; !sent.has(cmd.name))
...
<span style="color: #c678dd;">for</span> (<span style="color: #c678dd;">const</span> cmd of newSkills) {
  sent.add(cmd.name)
}
...
<span style="color: #c678dd;">return</span> [
  {
    <span style="color: #c678dd;">type</span>: <span style="color: #98c379;">'skill_listing'</span>,
    content,
    skillCount: newSkills.length,
    isInitial,
  },
]
</code></pre>
<p data-tool="mdnice编辑器">这个 <code style="color: #0e8aeb;">sentSkillNames</code> 机制说明：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">第一次发的是初始批次</section>
</li>
<li>
<section style="color: #010101;">后面只发新增的 skill</section>
</li>
<li>
<section style="color: #010101;">resume 之后还会 suppress，避免重复污染上下文</section>
</li>
</ul>
<p data-tool="mdnice编辑器">然后 <code style="color: #0e8aeb;">messages.ts</code> 会把它包成系统提醒。[messages.ts] utils/messages.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L3763</a>-L3772</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">return</span> wrapMessagesInSystemReminder([
  createUserMessage({
    content: <span style="color: #98c379;">`The following skills are available for use with the Skill tool:\n\n<span style="color: #e06c75;">${attachment.content}</span>`</span>,
    isMeta: <span style="color: #56b6c2;">true</span>,
  }),
])
</code></pre>
<p data-tool="mdnice编辑器">很多 Agent 会每轮把所有 tools / skills 全量重发，Claude Code 显然在认真控 token。 当然，如果技能不多，也可以直接全量发，不要过早优化。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">5）第五层：真正的 skill 内容延迟到调用时才展开</span></h2>
<p data-tool="mdnice编辑器">直到调用 <code style="color: #0e8aeb;">SkillTool</code>，skill 的真实正文才会通过 <code style="color: #0e8aeb;">command.getPromptForCommand()</code> 生成。[SkillTool.ts] utils/processUserInput/processSlashCommand.tsx<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L869</a>-L920</p>
<p data-tool="mdnice编辑器">这里才会发生：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">$ARGUMENTS</code> 替换</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">${CLAUDE_SKILL_DIR}</code> 替换</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">${CLAUDE_SESSION_ID}</code> 替换</section>
</li>
<li>
<section style="color: #010101;">markdown 内嵌 shell 执行</section>
</li>
<li>
<section style="color: #010101;">hooks 注册</section>
</li>
<li>
<section style="color: #010101;">附加权限 attachment 注入</section>
</li>
<li>
<section style="color: #010101;">invoked skill 记录</section>
</li>
</ul>
<p data-tool="mdnice编辑器">换句话说，skill 的重内容、重权限、重上下文副作用，都是<strong style="color: #0e88eb;">按需加载</strong>。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">四、除了延迟加载，它还做了「条件激活」</span></h1>
<p data-tool="mdnice编辑器">这也是渐进式披露的重要一层，而且很多人会漏掉。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）带 <code>paths</code> frontmatter 的 skill，不会启动即暴露</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">getSkillDirCommands()</code> 里会把 skill 分成两类：[loadSkillsDir.ts] loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L771</a>-L803</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">if</span> (
  skill.type === <span style="color: #98c379;">'prompt'</span> &amp;&amp;
  skill.paths &amp;&amp;
  skill.paths.length &gt; <span style="color: #d19a66;">0</span> &amp;&amp;
  !activatedConditionalSkillNames.has(skill.name)
) {
  newConditionalSkills.push(skill)
} <span style="color: #c678dd;">else</span> {
  unconditionalSkills.push(skill)
}
</code></pre>
<p data-tool="mdnice编辑器">然后 conditional skills 被先放进 <code style="color: #0e8aeb;">conditionalSkills</code> map，而不是直接进入模型可见集合。</p>
<p data-tool="mdnice编辑器">这意味着：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">你定义了某个 skill 只适用于 <code style="color: #0e8aeb;">*.tsx</code></section>
</li>
<li>
<section style="color: #010101;">它不会在项目启动时就干扰所有任务</section>
</li>
<li>
<section style="color: #010101;">只有模型真的碰到匹配文件时，这个 skill 才会被激活</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）激活时机挂在文件操作上</span></h2>
<p data-tool="mdnice编辑器">FileRead / FileWrite / FileEdit 三个工具里，都有两步副作用：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">发现上层目录里的 <code style="color: #0e8aeb;">.claude/skills</code></section>
</li>
<li>
<section style="color: #010101;">激活匹配当前文件路径的 conditional skills</section>
</li>
</ul>
<p data-tool="mdnice编辑器">比如 FileReadTool：[FileReadTool.ts] /tools/FileReadTool/FileReadTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L575</a>-L591</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">const</span> newSkillDirs = <span style="color: #c678dd;">await</span> discoverSkillDirsForPaths([fullFilePath], cwd)
...
addSkillDirectories(newSkillDirs).catch(() =&gt; {})
...
activateConditionalSkillsForPaths([fullFilePath], cwd)
</code></pre>
<p data-tool="mdnice编辑器">对应的激活实现是 [activateConditionalSkillsForPaths] skills/loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L997</a>-L1058：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">const</span> skillIgnore = ignore().add(skill.paths)
...
<span style="color: #c678dd;">if</span> (skillIgnore.ignores(relativePath)) {
  dynamicSkills.set(name, skill)
  conditionalSkills.delete(name)
  activatedConditionalSkillNames.add(name)
}
</code></pre>
<p data-tool="mdnice编辑器">这一步非常像条件规则系统，而不是纯静态注册。<br />
效果就是：<strong style="color: #0e88eb;">技能集合会随着你读写哪些文件而变化</strong>。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">五、动态发现本身也是渐进式披露的一部分</span></h1>
<p data-tool="mdnice编辑器">除了 path-conditional activation，Claude Code 还支持<strong style="color: #0e88eb;">目录级动态发现</strong>。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）启动时只加载一部分 skill 目录</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">getSkillDirCommands()</code> 启动时会加载：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">managed</section>
</li>
<li>
<section style="color: #010101;">user</section>
</li>
<li>
<section style="color: #010101;">project dirs</section>
</li>
<li>
<section style="color: #010101;">additional dirs</section>
</li>
<li>
<section style="color: #010101;">legacy commands</section>
</li>
</ul>
<p data-tool="mdnice编辑器">但它不会把所有嵌套目录里的 <code style="color: #0e8aeb;">.claude/skills</code> 一次性全扫出来。[loadSkillsDir.ts] skills/loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L638</a>-L804</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）当模型碰到某个文件时，再向上走目录树找嵌套 skill</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">discoverSkillDirsForPaths()</code> 会从当前文件的父目录开始，一路往上走到 cwd，查找 <code style="color: #0e8aeb;">.claude/skills</code>。[loadSkillsDir.ts] skills/loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L861</a>-L915</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">while</span> (currentDir.startsWith(resolvedCwd + pathSep)) {
  <span style="color: #c678dd;">const</span> skillDir = join(currentDir, <span style="color: #98c379;">'.claude'</span>, <span style="color: #98c379;">'skills'</span>)
  ...
  <span style="color: #c678dd;">await</span> fs.stat(skillDir)
  ...
  newDirs.push(skillDir)
}
</code></pre>
<p data-tool="mdnice编辑器">而且还做了两个非常实用的约束：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">已检查过的目录不会重复 stat</section>
</li>
<li>
<section style="color: #010101;">gitignored 目录里的 skills 不会静默加载</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这个设计让：<br />
<strong style="color: #0e88eb;">技能跟着你进入子目录而出现，不跟整个仓库一起一次性曝光。</strong></p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">六、SkillTool 的调用链，实际上分 inline 和 fork 两条路</span></h1>
<p data-tool="mdnice编辑器">这是技能系统和普通 slash command 最大的不同之一。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）调用前校验</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">SkillTool.validateInput()</code> 会做：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">去掉前导 <code style="color: #0e8aeb;">/</code></section>
</li>
<li>
<section style="color: #010101;">检查 skill 是否存在</section>
</li>
<li>
<section style="color: #010101;">检查是否 <code style="color: #0e8aeb;">disableModelInvocation</code></section>
</li>
<li>
<section style="color: #010101;">检查是否为 <code style="color: #0e8aeb;">prompt</code> 类型<br />
见 [SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L355</a>-L430</section>
</li>
</ul>
<p data-tool="mdnice编辑器">关键逻辑：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">const</span> commands = <span style="color: #c678dd;">await</span> getAllCommands(context)
<span style="color: #c678dd;">const</span> foundCommand = findCommand(normalizedCommandName, commands)
...
<span style="color: #c678dd;">if</span> (foundCommand.type !== <span style="color: #98c379;">'prompt'</span>) {
  <span style="color: #c678dd;">return</span> {
    result: <span style="color: #56b6c2;">false</span>,
    message: <span style="color: #98c379;">`Skill <span style="color: #e06c75;">${normalizedCommandName}</span> is not a prompt-based skill`</span>,
  }
}
</code></pre>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）权限检查</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">SkillTool.checkPermissions()</code> 很细，除了 allow / deny 规则，还会对「只有安全属性的 skill」自动放行。[SkillTool.ts] /tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L433</a>-L579</p>
<p data-tool="mdnice编辑器">这个设计的意义是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">简单 declarative skill 不必每次都弹权限</section>
</li>
<li>
<section style="color: #010101;">带额外风险属性的 skill 要 ask user</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3）inline skill：展开成后续对话消息</span></h2>
<p data-tool="mdnice编辑器">默认分支会走 <code style="color: #0e8aeb;">processPromptSlashCommand()</code>。[SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L635</a>-L644</p>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">getMessagesForPromptSlashCommand()</code> 干的事情很丰富：[processSlashCommand.tsx] utils/processUserInput/processSlashCommand.tsx<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L827</a>-L920</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">command.getPromptForCommand(args, context)</code> 得到真正 skill 正文</section>
</li>
<li>
<section style="color: #010101;">注册 hooks</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">addInvokedSkill()</code> 记录 skill 内容，供 compact 时恢复</section>
</li>
<li>
<section style="color: #010101;">从 skill 文本里再抽 attachment</section>
</li>
<li>
<section style="color: #010101;">增加 <code style="color: #0e8aeb;">command_permissions</code> attachment</section>
</li>
<li>
<section style="color: #010101;">生成一批 <code style="color: #0e8aeb;">messages</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">返回结构里最关键的是：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">return</span> {
  messages,
  shouldQuery: <span style="color: #56b6c2;">true</span>,
  allowedTools: additionalAllowedTools,
  model: command.model,
  effort: command.effort,
  command
}
</code></pre>
<p data-tool="mdnice编辑器">也就是说，inline skill 的本质是：<br />
<strong style="color: #0e88eb;">把 skill 变成一段新的上下文和权限修饰，然后让主对话继续跑。</strong></p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4）fork skill：交给子代理跑，再把结果归还</span></h2>
<p data-tool="mdnice编辑器">如果 skill frontmatter 里声明 <code style="color: #0e8aeb;">context === 'fork'</code>，就走 <code style="color: #0e8aeb;">executeForkedSkill()</code>。[SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L622</a>-L633</p>
<p data-tool="mdnice编辑器">它会：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">构造子代理上下文</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">runAgent()</code></section>
</li>
<li>
<section style="color: #010101;">收集 agent messages</section>
</li>
<li>
<section style="color: #010101;">抽取结果文本</section>
</li>
<li>
<section style="color: #010101;">最终返回 <code style="color: #0e8aeb;">{ status: 'forked', agentId, result }</code><br />
见 [executeForkedSkill] /tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L122</a>-L290</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这一步说明 Claude Code 已经把 skill 分成两类：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">知识/流程模板型 skill</strong>：inline 展开</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">工作委派型 skill</strong>：fork 子代理执行</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这个值得学一下。不是所有 skill 都应该展开在主上下文里。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">七、结果返回逻辑</span></h1>
<p data-tool="mdnice编辑器">为什么它也算渐进式披露的一部分？</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）inline skill 的 tool_result</span></h2>
<p data-tool="mdnice编辑器">很轻</p>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">mapToolResultToToolResultBlockParam()</code> 对 inline skill 的返回只是：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;">content: <span style="color: #98c379;">`Launching skill: <span style="color: #e06c75;">${result.commandName}</span>`</span>
</code></pre>
<p data-tool="mdnice编辑器">见 [SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L857</a>-L862</p>
<p data-tool="mdnice编辑器">也就是说，tool_result 本身不承载 skill 的全部结果。<br />
真正有价值的内容在 <code style="color: #0e8aeb;">newMessages</code> 里，已经被送回主会话继续推理。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）fork skill 的 tool_result</span></h2>
<p data-tool="mdnice编辑器">直接带最终结果</p>
<p data-tool="mdnice编辑器">fork skill 返回的是：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;">content: <span style="color: #98c379;">`Skill "<span style="color: #e06c75;">${result.commandName}</span>" completed (forked execution).\n\nResult:\n<span style="color: #e06c75;">${result.result}</span>`</span>
</code></pre>
<p data-tool="mdnice编辑器">见 [SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L848</a>-L855</p>
<p data-tool="mdnice编辑器">这是因为 fork skill 已经在独立上下文里把工作做完了，主线程要拿的是总结结果。</p>
<p data-tool="mdnice编辑器">所以在 Claude Code 里，skill 结果返回不是单一模式，而是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">inline：返回「已加载 skill」，真正内容进主对话</section>
</li>
<li>
<section style="color: #010101;">fork：返回「子代理执行结果」</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这也是一种披露控制。<br />
不同执行语义，对结果暴露方式也不同。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">八、如何简要实现</span></h1>
<p data-tool="mdnice编辑器">一个新 Agent，如何简要实现 skills 的发现、召回、调用、结果返回？</p>
<p data-tool="mdnice编辑器">一个<strong style="color: #0e88eb;">够用、够短、能落地</strong>的最小设计，不追求和 Claude Code 一模一样，但核心思路一致。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1）第一步：统一 skill 数据结构</span></h2>
<p data-tool="mdnice编辑器">最小结构建议这样：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">type</span> Skill = {
  name: <span style="color: #e6c07b;">string</span>
  description: <span style="color: #e6c07b;">string</span>
  whenToUse?: <span style="color: #e6c07b;">string</span>
  contentLoader: (args: <span style="color: #e6c07b;">string</span>, ctx: AgentContext) =&gt; <span style="color: #e6c07b;">Promise</span>&lt;<span style="color: #e6c07b;">string</span>&gt;
  allowedTools?: <span style="color: #e6c07b;">string</span>[]
  model?: <span style="color: #e6c07b;">string</span>
  effort?: <span style="color: #98c379;">'low'</span> | <span style="color: #98c379;">'medium'</span> | <span style="color: #98c379;">'high'</span>
  context?: <span style="color: #98c379;">'inline'</span> | <span style="color: #98c379;">'fork'</span>
  paths?: <span style="color: #e6c07b;">string</span>[]
}
</code></pre>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">contentLoader</code> 允许延迟展开</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">context</code> 决定 inline/fork</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">paths</code> 支持条件激活</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">allowedTools/model/effort</code> 支持 skill 级上下文修饰</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这和 Claude Code 的 <code style="color: #0e8aeb;">createSkillCommand()</code> 思路是一致的。[loadSkillsDir.ts] skills/loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L270</a>-L401</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2）第二步：启动时只加载「索引」，不要加载正文</span></h2>
<p data-tool="mdnice编辑器">最简做法：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">扫描 skills 目录</section>
</li>
<li>
<section style="color: #010101;">解析 frontmatter</section>
</li>
<li>
<section style="color: #010101;">只把 <code style="color: #0e8aeb;">name / description / whenToUse / paths / context</code> 放进 registry</section>
</li>
<li>
<section style="color: #010101;">skill 正文不要此时进 prompt</section>
</li>
</ul>
<p data-tool="mdnice编辑器">示意：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">async</span> <span style="color: #c678dd;">function</span> <span style="color: #61aeee;">loadSkillIndex</span>(skillDirs: <span style="color: #e6c07b;">string</span>[]): <span style="color: #61aeee;">Promise</span>&lt;<span style="color: #61aeee;">Skill</span>[]&gt; {
<span style="color: #c678dd;">const</span> skills: Skill[] = []
<span style="color: #c678dd;">for</span> (<span style="color: #c678dd;">const</span> dir of skillDirs) {
    <span style="color: #c678dd;">for</span> (<span style="color: #c678dd;">const</span> skillFile of <span style="color: #c678dd;">await</span> listSkillFiles(dir)) {
      <span style="color: #c678dd;">const</span> raw = <span style="color: #c678dd;">await</span> readFile(skillFile, <span style="color: #98c379;">'utf8'</span>)
      <span style="color: #c678dd;">const</span> { frontmatter, content } = parseFrontmatter(raw)
      skills.push({
        name: basename(dirname(skillFile)),
        description: <span style="color: #e6c07b;">String</span>(frontmatter.description ?? <span style="color: #98c379;">''</span>),
        whenToUse: frontmatter.when_to_use ? <span style="color: #e6c07b;">String</span>(frontmatter.when_to_use) : <span style="color: #56b6c2;">undefined</span>,
        paths: <span style="color: #e6c07b;">Array</span>.isArray(frontmatter.paths) ? frontmatter.paths : <span style="color: #56b6c2;">undefined</span>,
        context: frontmatter.context === <span style="color: #98c379;">'fork'</span> ? <span style="color: #98c379;">'fork'</span> : <span style="color: #98c379;">'inline'</span>,
        contentLoader: <span style="color: #c678dd;">async</span> () =&gt; content,
      })
    }
  }
<span style="color: #c678dd;">return</span> skills
}
</code></pre>
<p data-tool="mdnice编辑器">这个阶段要学 Claude Code 的不是目录细节，而是<strong style="color: #0e88eb;">索引和正文分离</strong>。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3）第三步：做一个「未发送 skill 集合」</span></h2>
<p data-tool="mdnice编辑器">这是渐进式披露的核心。</p>
<p data-tool="mdnice编辑器">维护一个 session 级状态：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">type</span> SkillDisclosureState = {
  sentSkillNames: Set&lt;<span style="color: #e6c07b;">string</span>&gt;
}
</code></pre>
<p data-tool="mdnice编辑器">每轮只发送新的：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">function</span> <span style="color: #61aeee;">getNewSkillListings</span>(skills: Skill[], sent: Set&lt;<span style="color: #e6c07b;">string</span>&gt;): <span style="color: #61aeee;">Skill</span>[] {
  <span style="color: #c678dd;">const</span> fresh = skills.filter(s =&gt; !sent.has(s.name))
  <span style="color: #c678dd;">for</span> (<span style="color: #c678dd;">const</span> s of fresh) sent.add(s.name)
  <span style="color: #c678dd;">return</span> fresh
}
</code></pre>
<p data-tool="mdnice编辑器">然后把它格式化成短列表，而不是全文：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">function</span> <span style="color: #61aeee;">formatSkillListing</span>(skills: Skill[]): <span style="color: #61aeee;">string</span> {
  <span style="color: #c678dd;">return</span> skills.map(s =&gt; <span style="color: #98c379;">`- <span style="color: #e06c75;">${s.name}</span>: <span style="color: #e06c75;">${s.description}</span>`</span>).join(<span style="color: #98c379;">'\n'</span>)
}
</code></pre>
<p data-tool="mdnice编辑器">这对应 Claude Code 的 <code style="color: #0e8aeb;">sentSkillNames + skill_listing attachment</code> 方案。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4）第四步：把文件操作接成动态发现触发器</span></h2>
<p data-tool="mdnice编辑器">如果你也想要「技能跟着目录出现」，最小版本就是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">用户或模型读/写/改文件时</section>
</li>
<li>
<section style="color: #010101;">从文件父目录往上走到 cwd</section>
</li>
<li>
<section style="color: #010101;">看有没有 <code style="color: #0e8aeb;">.agent/skills</code> 或 <code style="color: #0e8aeb;">.claude/skills</code></section>
</li>
<li>
<section style="color: #010101;">找到新目录就加载 skill index</section>
</li>
</ul>
<p data-tool="mdnice编辑器">示意：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">async</span> <span style="color: #c678dd;">function</span> <span style="color: #61aeee;">discoverSkillDirsForFile</span>(filePath: <span style="color: #e6c07b;">string</span>, cwd: <span style="color: #e6c07b;">string</span>): <span style="color: #61aeee;">Promise</span>&lt;<span style="color: #61aeee;">string</span>[]&gt; {
<span style="color: #c678dd;">const</span> dirs: <span style="color: #e6c07b;">string</span>[] = []
<span style="color: #c678dd;">let</span> current = dirname(filePath)
<span style="color: #c678dd;">while</span> (current.startsWith(cwd + sep)) {
    <span style="color: #c678dd;">const</span> candidate = join(current, <span style="color: #98c379;">'.agent'</span>, <span style="color: #98c379;">'skills'</span>)
    <span style="color: #c678dd;">if</span> (<span style="color: #c678dd;">await</span> exists(candidate)) dirs.push(candidate)
    <span style="color: #c678dd;">const</span> parent = dirname(current)
    <span style="color: #c678dd;">if</span> (parent === current) <span style="color: #c678dd;">break</span>
    current = parent
  }
<span style="color: #c678dd;">return</span> dirs
}
</code></pre>
<p data-tool="mdnice编辑器">Claude Code 的现成参考是 [discoverSkillDirsForPaths] skills/loadSkillsDir.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L861</a>-L915。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">5）第五步：做条件激活，而不是启动时全暴露</span></h2>
<p data-tool="mdnice编辑器">如果 skill 定义里有 <code style="color: #0e8aeb;">paths</code>，就不要一开始暴露。<br />
等碰到匹配文件时再激活：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">function</span> <span style="color: #61aeee;">activatePathScopedSkills</span>(
  pending: Skill[],
  touchedFiles: <span style="color: #e6c07b;">string</span>[],
): { active: Skill[]; remaining: Skill[] } {
<span style="color: #c678dd;">const</span> active: Skill[] = []
<span style="color: #c678dd;">const</span> remaining: Skill[] = []
<span style="color: #c678dd;">for</span> (<span style="color: #c678dd;">const</span> skill of pending) {
    <span style="color: #c678dd;">if</span> (!skill.paths || skill.paths.length === <span style="color: #d19a66;">0</span>) {
      active.push(skill)
      <span style="color: #c678dd;">continue</span>
    }
    <span style="color: #c678dd;">const</span> matched = touchedFiles.some(file =&gt; matchAny(file, skill.paths!))
    <span style="color: #c678dd;">if</span> (matched) active.push(skill)
    <span style="color: #c678dd;">else</span> remaining.push(skill)
  }
<span style="color: #c678dd;">return</span> { active, remaining }
}
</code></pre>
<p data-tool="mdnice编辑器">这就是 Claude Code <code style="color: #0e8aeb;">conditionalSkills -&gt; activateConditionalSkillsForPaths()</code> 的最小复刻。</p>
<hr />
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">6）第六步：调用 skill 时才真正加载正文</span></h2>
<p data-tool="mdnice编辑器">不要提前把 skill 正文塞到 prompt。<br />
调用时再做：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">async</span> <span style="color: #c678dd;">function</span> <span style="color: #61aeee;">invokeSkill</span>(
  skill: Skill,
  args: <span style="color: #e6c07b;">string</span>,
  ctx: AgentContext,
): <span style="color: #61aeee;">Promise</span>&lt;<span style="color: #61aeee;">SkillInvocationResult</span>&gt; {
<span style="color: #c678dd;">const</span> prompt = <span style="color: #c678dd;">await</span> skill.contentLoader(args, ctx)

<span style="color: #c678dd;">if</span> (skill.context === <span style="color: #98c379;">'fork'</span>) {
    <span style="color: #c678dd;">const</span> result = <span style="color: #c678dd;">await</span> runSubAgent({
      prompt,
      allowedTools: skill.allowedTools,
      model: skill.model,
      effort: skill.effort,
    })
    <span style="color: #c678dd;">return</span> { mode: <span style="color: #98c379;">'fork'</span>, result }
  }

<span style="color: #c678dd;">return</span> {
    mode: <span style="color: #98c379;">'inline'</span>,
    newMessages: [
      { role: <span style="color: #98c379;">'user'</span>, content: <span style="color: #98c379;">`[SKILL:<span style="color: #e06c75;">${skill.name}</span>]`</span> },
      { role: <span style="color: #98c379;">'user'</span>, content: prompt, meta: <span style="color: #56b6c2;">true</span> },
    ],
    allowedTools: skill.allowedTools,
    model: skill.model,
    effort: skill.effort,
  }
}
</code></pre>
<p data-tool="mdnice编辑器">这就是 Claude Code <code style="color: #0e8aeb;">SkillTool.call()</code> 的最小骨架。[SkillTool.ts] tools/SkillTool/SkillTool.ts<a class="wx_topic_link" style="color: #576b95 !important;" data-topic="1" data-recommend="">#L581</a>-L863</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">7）第七步：结果返回必须分 inline 和 fork</span></h2>
<p data-tool="mdnice编辑器">直接照 Claude Code 的语义分两种：</p>
<h3 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">inline</span></h3>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">返回一个轻 tool_result：<code style="color: #0e8aeb;">Launching skill: xxx</code></section>
</li>
<li>
<section style="color: #010101;">真正内容通过 <code style="color: #0e8aeb;">newMessages</code> 回到主对话继续推理</section>
</li>
</ul>
<h3 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">fork</span></h3>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">返回最终结果摘要</section>
</li>
<li>
<section style="color: #010101;">子代理对话不污染主上下文</section>
</li>
</ul>
<p data-tool="mdnice编辑器">示意：</p>
<pre data-tool="mdnice编辑器"><code style="color: #abb2bf;"><span style="color: #c678dd;">type</span> SkillInvocationResult =
  | {
      mode: <span style="color: #98c379;">'inline'</span>
      newMessages: Message[]
      allowedTools?: <span style="color: #e6c07b;">string</span>[]
      model?: <span style="color: #e6c07b;">string</span>
      effort?: <span style="color: #e6c07b;">string</span>
    }
  | {
      mode: <span style="color: #98c379;">'fork'</span>
      result: <span style="color: #e6c07b;">string</span>
    }
</code></pre>
<p data-tool="mdnice编辑器">这一步是很多新 Agent 最容易偷懒的地方。<br />
要么所有 skill 都 inline，主上下文爆炸；要么所有 skill 都 fork，失去细粒度引导。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">九、小结</span></h1>
<p data-tool="mdnice编辑器">「skills 的渐进式披露」其实就是 Claude Code 在控制 prompt 成本和能力密度时最典型的设计之一。它真正解决的问题不是「怎么找到一个 skill」，而是「怎么在不把上下文撑爆的前提下，让模型知道自己有技能可用」。</p>
<p data-tool="mdnice编辑器">它背后的思路：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">先给索引</section>
</li>
<li>
<section style="color: #010101;">再给局部集合</section>
</li>
<li>
<section style="color: #010101;">再给真实正文</section>
</li>
<li>
<section style="color: #010101;">最后才给执行结果</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这是一个很像搜索引擎的设计：摘要、点击、展开、消费，而不是把整本书扔给你。</p>
<p data-tool="mdnice编辑器">以上。</p>
<p>&nbsp;</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/04/claude-code-ai-skills-source/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Claude Code 的下一个 AI 范式：KAIROS</title>
		<link>https://www.phppan.com/2026/04/claude-code-ai-kairos/</link>
		<comments>https://www.phppan.com/2026/04/claude-code-ai-kairos/#comments</comments>
		<pubDate>Sun, 12 Apr 2026 03:46:09 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[KAIROS]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2488</guid>
		<description><![CDATA[在 Claude Code 的代码中，如果只算 KAIROS 出现的次数，其出现了 154 次；如果算上以其为 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-pm-slice="0 0 []">
<p data-tool="mdnice编辑器">在 Claude Code 的代码中，如果只算 KAIROS 出现的次数，其出现了 154 次；如果算上以其为前缀的变量啥的，其出现了 365 次。</p>
<p data-tool="mdnice编辑器">KAIROS 是什么？</p>
<p data-tool="mdnice编辑器">简单来说，KAIROS 是 Claude Code 未来的 AI 形态，一个在恰当时机出现的，一直在线的协同工作伙伴。</p>
<p data-tool="mdnice编辑器">KAIROS (καιρός) 源自古希腊语，意为「正确的、关键的或合宜的时刻」，代表定性的、超越时序的「时机」或「关键瞬间」。</p>
<p data-tool="mdnice编辑器">KAIROS 这件事，重点从来不在于它多了几个工具开关，也不在于文档里写了多少「常驻助手」「主动工作」这种产品话术。它真正改变的，是 Claude Code 的运行范式：从「终端里的同步问答器」，切到「长期在线、异步协作、跨渠道接入、能自己维持工作节奏的常驻代理」。</p>
<p data-tool="mdnice编辑器">这个变化很大。大到我不太愿意把它叫成一次功能升级。它更像一次产品类别切换。</p>
<p data-tool="mdnice编辑器">如果这个方向跑通，Claude Code 的竞争对象会变。它不再只是和一批 coding assistant CLI 去比「补全快不快、命令懂不懂、上下文长不长」。它会开始进入另一条赛道：谁更像一个持续在线的工程协作者，谁能承接跨时间、跨终端、跨系统的工作责任。</p>
<p data-tool="mdnice编辑器">问题也在这里。KAIROS 现在的仓库状态，远没有到「产品封版」的程度。外围能力已经长出不少，主闭环还没彻底打穿。Bridge、Brief、频道消息、每日记忆日志、后台任务基础设施，这些都不是 PPT。assistant 主入口、gate、proactive 状态、session discovery 这些地方，又明显还是 stub。方向很清楚，骨架也搭起来了，真正决定产品能不能稳定落地的那几条主链路，还差最后一截硬骨头。</p>
<p data-tool="mdnice编辑器">这篇文章我们不按功能清单来复述一遍。那样太浅，也没工程价值。回答三个关键的问题：</p>
<ol class="list-paddingleft-1">
<li>
<section style="color: #010101;">KAIROS 在 Claude Code 里到底重新定义了什么</section>
</li>
<li>
<section style="color: #010101;">它已经落地了哪些关键能力，哪些地方还没闭环</section>
</li>
<li>
<section style="color: #010101;">为什么它必须改写记忆系统、交互渠道和执行模型</section>
</li>
</ol>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 改写的不是功能，是运行模型</span></h1>
<p data-tool="mdnice编辑器">普通 CLI 的交互模型很简单。</p>
<p data-tool="mdnice编辑器">用户打开终端。输入一条指令。模型分析上下文。调用工具。给出回答。进程结束，或者这一轮逻辑结束。下一次再来，虽然可能还能靠项目文件、历史记录、memory 文件接上一部分语境，但本质上还是新一轮同步请求-响应。</p>
<p data-tool="mdnice编辑器">这个模式有一个天然上限：AI 只在用户看着终端的时候存在。用户不在，系统就不工作。外部事件来了，也接不住。长任务只能靠用户盯着。跨设备继续工作这件事，基本也无从谈起。</p>
<p data-tool="mdnice编辑器">KAIROS 想改掉的，就是这个上限。</p>
<p data-tool="mdnice编辑器">它想要的模型是另一种结构：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">会话可以长期存在</section>
</li>
<li>
<section style="color: #010101;">进程重启后还能接回原来的会话</section>
</li>
<li>
<section style="color: #010101;">外部系统可以把消息推到这个会话里</section>
</li>
<li>
<section style="color: #010101;">用户没有新输入时，Agent 也能继续推进任务</section>
</li>
<li>
<section style="color: #010101;">工作状态通过记忆、日志和摘要维持</section>
</li>
<li>
<section style="color: #010101;">输出形态适配异步消费，而不是只适配终端前的一次性阅读</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这不是「更主动一点」。这是一整套运行时假设变了。</p>
<p data-tool="mdnice编辑器">我一直觉得，很多人看这类能力时容易掉进一个误区：看见 <code style="color: #0e8aeb;">SleepTool</code>、push notification、channels，就以为这只是「给 CLI 加点自动化」。这个理解有点太保守。真正的变化是，系统开始假设自己是一个持续值班的实体，而不是一个按回车键才苏醒的函数调用。</p>
<p data-tool="mdnice编辑器">一旦假设变了，后面的东西都会跟着变。会话管理会变。记忆策略会变。输出格式会变。安全边界会变。成本模型会变。产品定位也会变。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">从代码现状看，KAIROS 已经是一组能力家族</span></h1>
<p data-tool="mdnice编辑器">从文档和源码状态看，KAIROS 不是单点 feature flag，它更像一个能力总开关，把若干子系统串成一个共同叙事。</p>
<p data-tool="mdnice编辑器">已有的子功能包括：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">KAIROS_BRIEF</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">KAIROS_CHANNELS</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">KAIROS_PUSH_NOTIFICATION</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">KAIROS_GITHUB_WEBHOOKS</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">KAIROS_DREAM</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">工具注册层能看到对应工具：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">SleepTool</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">SendUserFileTool</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">PushNotificationTool</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">SubscribePRTool</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">从这些就可能看到背后对应的产品动作：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">控制执行节奏和保活</section>
</li>
<li>
<section style="color: #010101;">主动把结果回传给用户</section>
</li>
<li>
<section style="color: #010101;">在终端外做异步通知</section>
</li>
<li>
<section style="color: #010101;">订阅外部事件，反向驱动内部执行</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这里最值得注意的是其产品动作的形态已经变了。</p>
<p data-tool="mdnice编辑器">传统 CLI 工具的动作集合通常是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">读文件</section>
</li>
<li>
<section style="color: #010101;">写文件</section>
</li>
<li>
<section style="color: #010101;">执行命令</section>
</li>
<li>
<section style="color: #010101;">输出结果</section>
</li>
</ul>
<p data-tool="mdnice编辑器">KAIROS 的动作集合变成：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">等待</section>
</li>
<li>
<section style="color: #010101;">监听</section>
</li>
<li>
<section style="color: #010101;">回传</section>
</li>
<li>
<section style="color: #010101;">唤醒</section>
</li>
<li>
<section style="color: #010101;">跨渠道接入</section>
</li>
<li>
<section style="color: #010101;">持续维持上下文</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这说明它的定位已经不再是单纯的「本地操作器」。它正往「工作流中枢」走。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">Bridge 是 KAIROS 最关键的基础设施之一</span></h1>
<p data-tool="mdnice编辑器">KAIROS 能不能成立，第一件事不是主动性，而是<strong style="color: #0e88eb;">连续性</strong>。</p>
<p data-tool="mdnice编辑器">如果会话不能连续存在，所谓常驻助手就是假的。它最多是一个本地守护进程。用户侧体验还是断裂的。今天在一个终端开的事情，明天换个终端、换个设备、换个入口，就接不上了。那这个产品心智根本立不起来。</p>
<p data-tool="mdnice编辑器">Bridge 正是在补这个问题。</p>
<p data-tool="mdnice编辑器">从现有设计看，Bridge 的数据流：远端入口收到用户消息，通过 bridge 拉取工作，创建或恢复 REPL，会话继续执行，再把结果回传。这个思路解决的是「<strong style="color: #0e88eb;">用户感知到的是不是同一个持续存在的助手</strong>」。</p>
<p data-tool="mdnice编辑器">代码里关键点在 <code style="color: #0e8aeb;">useReplBridge</code>。assistant 模式下会启用 perpetual bridge session，目的是让远端看到的是同一条持续会话，而不是每次 CLI 启动都开一条新的 session。</p>
<p data-tool="mdnice编辑器">没有 perpetual session，用户面对的是很多段相似但断裂的对话。每次恢复都像重新认识一次项目。上下文可能靠 memory 拼回来一点，但主观体验一定是断的。</p>
<p data-tool="mdnice编辑器">有了 perpetual session，用户会开始把这个东西当成「同一个一直在线的协作者」。这就是范式变化真正落地的第一步。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">但真实闭环卡住的地方，不在 Bridge 本身</span></h1>
<p data-tool="mdnice编辑器">Bridge 骨架基本有了，assistant 产品主链路还没闭环。</p>
<p data-tool="mdnice编辑器">很多团队做 Agent 系统时，最容易陷入一种错觉：底层传输能通，远程会话能恢复，消息能送达，就以为产品主路径已经打通。其实不是。通道打通，只代表系统能传递状态。离「一个稳定可用的常驻助手产品」还差几个关键层：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">身份判定</section>
</li>
<li>
<section style="color: #010101;">gate 放行</section>
</li>
<li>
<section style="color: #010101;">会话发现</section>
</li>
<li>
<section style="color: #010101;">assistant 专属上下文初始化</section>
</li>
<li>
<section style="color: #010101;">assistant 专属系统提示</section>
</li>
<li>
<section style="color: #010101;">持续工作状态机</section>
</li>
<li>
<section style="color: #010101;">长期记忆蒸馏</section>
</li>
</ul>
<p data-tool="mdnice编辑器">当前源码里，这些都还没有。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">assistant 主入口还是 stub</span></h2>
<p data-tool="mdnice编辑器">assistant 主模块里，<code style="color: #0e8aeb;">isAssistantMode()</code> 返回 <code style="color: #0e8aeb;">false</code>，初始化函数是空的，assistant 专属 prompt addendum 也是空的。</p>
<p data-tool="mdnice编辑器">这意味着什么？</p>
<p data-tool="mdnice编辑器">意味着一大堆外围逻辑虽然预留了 assistant 分支，但真正运行时根本进不去。Bridge 想按 assistant 模式走 perpetual session，要靠 <code style="color: #0e8aeb;">isAssistantMode()</code>。主程序想按 assistant 模式切换运行逻辑，也要靠它。远端 worker 类型的区分，还是要靠它。</p>
<p data-tool="mdnice编辑器">入口判定没实现，后面这条链就全断了。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS gate 还是 stub</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">isKairosEnabled()</code> 直接返回 false，这表示：产品级放行逻辑还没接上。</p>
<p data-tool="mdnice编辑器">这不是个小洞。因为常驻助手和普通 CLI 完全不是一个风险等级的东西。它能后台执行、接外部消息、长期持有上下文、主动做事。没有真实 gate，这种能力根本不适合大面积开。</p>
<p data-tool="mdnice编辑器">所以从工程视角看，gate 现在是缺实现。从产品视角看，这代表「产品入口控制逻辑还停留在骨架层」。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">session discovery 还是 stub</span></h2>
<p data-tool="mdnice编辑器">如果用户执行 assistant viewer 路径，系统理论上应该能发现已有常驻会话，然后接回去。现在 discovery 返回空数组，这条体验链路就断了。</p>
<p data-tool="mdnice编辑器">这直接伤到产品最核心的承诺之一：会话连续性。</p>
<p data-tool="mdnice编辑器">你都号称是常驻助手了，结果用户回来时找不到自己之前那条会话，这个心智会瞬间塌掉。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">proactive 状态模块还是 stub</span></h2>
<p data-tool="mdnice编辑器">KAIROS 的 prompt 层已经开始定义 autonomous work 的行为协议了，比如：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">没有事做时必须 sleep</section>
</li>
<li>
<section style="color: #010101;">用户不在终端前时偏向自主推进</section>
</li>
<li>
<section style="color: #010101;">用户正在看终端时偏向协作和简洁输出</section>
</li>
</ul>
<p data-tool="mdnice编辑器">行为协议有了，状态机没落地。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 最大的区别，不是工具多，而是 prompt 协议变了</span></h1>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">对于 Agent 系统，prompt 在很多时候就是运行协议的一部分。</strong></p>
<p data-tool="mdnice编辑器">普通模式下，模型更像一个执行器。收到请求，完成任务，给出答复。</p>
<p data-tool="mdnice编辑器">KAIROS 模式下，prompt 在定义另一种工作方式：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">什么时候该自己推进</section>
</li>
<li>
<section style="color: #010101;">什么时候该停下来等待</section>
</li>
<li>
<section style="color: #010101;">什么时候该用 Brief 压缩输出</section>
</li>
<li>
<section style="color: #010101;">什么时候该把结果异步推给用户</section>
</li>
<li>
<section style="color: #010101;">用户是否在终端前，会影响表达风格和协作策略</section>
</li>
<li>
<section style="color: #010101;">没有明确工作时不能空转，必须 sleep</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这本质上是在给模型灌输一套「值班协作者协议」。</p>
<p data-tool="mdnice编辑器">因为常驻助手和一次性问答器的差别，很大一部分在于它是否具备稳定的工作节奏。节奏不稳，再强的工具集也会把系统拖进两个极端：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">一直唤醒，疯狂消耗 token 和 API 调用</section>
</li>
<li>
<section style="color: #010101;">一直沉睡，错过事件和推进时机</section>
</li>
</ul>
<p data-tool="mdnice编辑器">所以 <code style="color: #0e8aeb;">SleepTool</code> 这种东西，表面看是个小工具，本质上是在为 Agent 增加时间维度。普通 CLI 处理的是空间里的资源：文件、命令、输出。KAIROS 开始处理时间里的资源：等待、唤醒、周期、空闲、值班。</p>
<p data-tool="mdnice编辑器">这一步一旦做出来，产品形态就变了。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 为什么必须改写记忆系统</span></h1>
<p data-tool="mdnice编辑器">这里必须要聊一下。</p>
<p data-tool="mdnice编辑器">在长任务中，我们不能拿短会话的记忆模型去硬撑长时间在线系统。写放大、污染、冲突、检索噪音、摘要失真，很快全出来。</p>
<p data-tool="mdnice编辑器">KAIROS 在这件事上切到了 daily log 模式。</p>
<p data-tool="mdnice编辑器">普通模式下，长期记忆更接近「主题文件 + 索引」：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">新信息被整理成相对成型的 topic files</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">MEMORY.md</code> 维护索引</section>
</li>
<li>
<section style="color: #010101;">模型下次需要时按主题读回</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这个模式适合短周期会话。信息密度高，整理成本还能接受。</p>
<p data-tool="mdnice编辑器">KAIROS 场景不一样。它面对的是：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">长时间持续执行</section>
</li>
<li>
<section style="color: #010101;">高频事件流输入</section>
</li>
<li>
<section style="color: #010101;">用户不一定实时盯着</section>
</li>
<li>
<section style="color: #010101;">外部渠道消息可能随时插入</section>
</li>
<li>
<section style="color: #010101;">同一天内工作状态会不断变化</section>
</li>
<li>
<section style="color: #010101;">大量信息是过程态，不适合立刻主题化</section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果还按普通模式那种「一有信息就整理成 topic file」去写，工程上会出三个明显问题。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第一，写放大会很严重</span></h2>
<p data-tool="mdnice编辑器">频繁改 topic files，会导致目录不断抖动。<code style="color: #0e8aeb;">MEMORY.md</code> 也会被频繁重写。对于常驻系统，这种写入模式很不稳。量一上来就开始恶心人。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第二，过程信息会过早结构化</span></h2>
<p data-tool="mdnice编辑器">很多工作过程在当下并不适合写成结论。比如一条外部消息、一次等待中的验证、一个还没确认的假设、某项任务中间状态。这些东西如果过早塞进长期记忆，很容易污染后续上下文。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第三，恢复和追溯会变差</span></h2>
<p data-tool="mdnice编辑器">当你把所有信息即时揉进主题文件里，原始事件流会逐渐丢失。系统后面做蒸馏、回溯、纠错时，材料反而不够。</p>
<p data-tool="mdnice编辑器">daily log 方案就是为了解这些问题。</p>
<p data-tool="mdnice编辑器">它的核心思想很简单：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">白天先 append-only 记录到当日日志</section>
</li>
<li>
<section style="color: #010101;">不急着重组和提炼</section>
</li>
<li>
<section style="color: #010101;">后续再把成熟信息蒸馏成长期 memory</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这是典型的事件流优先设计。先保留工作轨迹，再做结构化提炼。对常驻助手来说，这个方向很稳。</p>
<p data-tool="mdnice编辑器">普通模式里的 memory 是模型的记忆补丁，KAIROS 里的 memory 是产品连续性的基础设施。</p>
<p data-tool="mdnice编辑器">这两者不是一个级别的东西。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">transcript 被纳入记忆蒸馏</span></h1>
<p data-tool="mdnice编辑器">KAIROS 还想做一件更重要的事：把 session transcript 也纳入记忆蒸馏输入。</p>
<p data-tool="mdnice编辑器">这意味着长期记忆的来源，不再只靠模型「当前轮总结出的信息」，而是开始吸收完整工作轨迹。</p>
<p data-tool="mdnice编辑器">你可以把这理解成两种 memory 策略的差异：</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">普通模式</span></h2>
<p data-tool="mdnice编辑器">长期记忆主要存「结论」</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 模式</span></h2>
<p data-tool="mdnice编辑器">长期记忆想从「事件流」中提取结论</p>
<p data-tool="mdnice编辑器">因为一个持续在线的协作者，真正有价值的上下文往往不只在最后结果里，还在过程里：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">谁在什么时候发来过什么消息</section>
</li>
<li>
<section style="color: #010101;">某项任务等待了多久</section>
</li>
<li>
<section style="color: #010101;">哪个验证步骤失败过</section>
</li>
<li>
<section style="color: #010101;">某个方向为什么被放弃</section>
</li>
<li>
<section style="color: #010101;">同一项目在不同日期里怎么演化的</section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果记忆系统拿不到这些过程信息，系统就只能越活越像一个失忆的执行器：只记结论，不记来路。</p>
<p data-tool="mdnice编辑器">当前仓库里这条链还没补齐，session transcript 相关实现还是 stub。当把普通 auto-dream 关闭，改走 KAIROS 专属 dream 路径，那就必须有足够材料来做蒸馏。daily logs 和 transcript 就是这个材料池。</p>
<p data-tool="mdnice编辑器">没有材料池，dream 只是概念。<br />
没有蒸馏，daily log 只是堆积。<br />
没有长期记忆收敛，常驻助手就只剩「一直在线」，没有「越用越像同一个协作者」。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">Brief 不是 UI 花活</span></h1>
<p data-tool="mdnice编辑器">它是异步协作场景里的输出压缩层</p>
<p data-tool="mdnice编辑器">我很喜欢 KAIROS 里对 Brief 的定位，因为这个点很多产品会忽略。</p>
<p data-tool="mdnice编辑器">一旦系统进入长期运行、跨终端、跨渠道、还带移动端通知的场景，传统那种长篇回复会迅速失效。不是模型写不出来，而是用户根本没法消费。</p>
<p data-tool="mdnice编辑器">你想象一下：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">一个后台任务跑了两小时</section>
</li>
<li>
<section style="color: #010101;">外部 webhook 触发了一轮检查</section>
</li>
<li>
<section style="color: #010101;">Slack 里推来一条状态更新</section>
</li>
<li>
<section style="color: #010101;">用户此时在手机上看通知</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这时候如果系统还按终端里的详细答复风格，甩一大段解释文字出去，体验会很差。信息密度低，确认成本高，真正关键的状态反而埋住了。</p>
<p data-tool="mdnice编辑器">Brief 的价值就在这里。它是异步工作场景的输出压缩层。</p>
<p data-tool="mdnice编辑器">它解决的不是「怎么更优雅地显示」，而是「在不同消费界面里，用最低认知成本传递足够状态」。这是一个工程问题，不是文案问题。</p>
<p data-tool="mdnice编辑器">所以我会把 Brief 看成 KAIROS 的必要配套，而不是锦上添花。没有它，常驻助手很容易被自己的输出拖死。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">channels 让 Claude Code 开始脱离终端边界</span></h1>
<p data-tool="mdnice编辑器">KAIROS 另一条很重要的线，是频道消息系统，也是一个很让人期待的逻辑，虽然当前有一些方法已经可以实现。</p>
<p data-tool="mdnice编辑器">当前设计已经允许外部消息通过 channel notification 之类的机制进入会话，被包装成结构化消息再投递给模型处理。这意味着：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">Claude Code 不再只属于一个本地终端</section>
</li>
<li>
<section style="color: #010101;">用户可以在终端外部和同一条工作流继续互动</section>
</li>
<li>
<section style="color: #010101;">AI 可以成为跨渠道的工作代理</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这个变化一旦做成，产品就会从 Developer Tool 往 Agent Platform 滑过去。</p>
<p data-tool="mdnice编辑器">再直白一点。</p>
<p data-tool="mdnice编辑器">终端工具的边界，是「你必须来到我的界面里，我才能帮你做事」。<br />
工作流代理的边界，是「我能在你的工作流里持续存在，你在哪个入口出现，我都能接上」。</p>
<p data-tool="mdnice编辑器">这是两种完全不同的产品位置。</p>
<p data-tool="mdnice编辑器">这样，channels 就很重要了，但不该排在最前面的优先级。因为它解决的是入口扩展问题，不是主闭环问题。一个内部还没站稳的系统，入口接得越多，事故面越大。没有 assistant 激活链、记忆蒸馏链和 proactive 状态机托底，channels 只会让问题更快暴露。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">后台执行是一等能力，不是附属能力</span></h1>
<p data-tool="mdnice编辑器">KAIROS 把后台执行推成一等能力。</p>
<p data-tool="mdnice编辑器">这是常驻助手能否成立的另一条底线。</p>
<p data-tool="mdnice编辑器">如果 Agent 像同事一样持续工作，它就不能被单次命令执行周期绑死。用户离开终端，任务还得继续跑。长任务不能把主线程挂住。等待回调、监听事件、轮询状态这些行为，不能都靠用户盯着终端来维持。</p>
<p data-tool="mdnice编辑器">很多 coding assistant 在 demo 阶段看起来很聪明，一到真实项目就暴露上限：用户一旦离开终端，整个系统的价值密度就迅速下降。长任务没人接。回调没人等。状态没人维持。所谓 Agent，最后还是个问答器。</p>
<p data-tool="mdnice编辑器">KAIROS 显然想突破这个上限。</p>
<p data-tool="mdnice编辑器">后台执行一旦变成一等能力，系统复杂度会明显上升。你要处理：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">阻塞任务后台化</section>
</li>
<li>
<section style="color: #010101;">任务状态跟踪</section>
</li>
<li>
<section style="color: #010101;">唤醒与恢复</section>
</li>
<li>
<section style="color: #010101;">错误回传</section>
</li>
<li>
<section style="color: #010101;">与记忆系统的状态对齐</section>
</li>
<li>
<section style="color: #010101;">与通知节奏的协调</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些东西没有一项是白送的。做不好，后台任务会变成后台事故。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 的产品价值，核心在五个地方</span></h1>
<p data-tool="mdnice编辑器">如果从产品结果看，我会把它的价值拆成五个方面。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">一，留存会显著提升</span></h2>
<p data-tool="mdnice编辑器">一次性问答工具的留存天然一般。因为每轮交互都相对独立，用户用完就走。上下文积累浅，切换成本低。</p>
<p data-tool="mdnice编辑器">常驻会话、跨重启续接、长期记忆、异步回传这些东西组合起来，用户会开始把 Claude Code 当成「当前项目的长期协作体」。一旦心智变成这样，迁移成本就会明显上升。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">二，任务完成度会提升</span></h2>
<p data-tool="mdnice编辑器">很多高价值任务不是一轮 prompt 能做完的。它们需要等待、重试、监听、回调、验证、持续推进。普通模式下，这类任务经常会在用户离开终端时中断。</p>
<p data-tool="mdnice编辑器">KAIROS 提供的后台执行、sleep 唤醒、外部事件驱动，正好在补这个缺口。产品会从「答题器」往「任务执行器」走。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">三，渠道覆盖会扩大</span></h2>
<p data-tool="mdnice编辑器">有了 channels、push、bridge、webhook，Claude Code 的触点会明显增加。对产品运营和组织传播来说，这个价值非常直接。一个只能在终端里使用的工具，天然局限在一小撮高频命令行用户。一个能进入 Slack、移动通知、远程 viewer 的系统，扩散面会大得多。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">四，粘性会增强</span></h2>
<p data-tool="mdnice编辑器">session continuity、daily logs、structured brief、跨渠道接续，这几样东西叠在一起，会形成很强的黏着效应。用户会越来越依赖它手里的上下文。上下文越深，替换成本越高。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">五，产品想象空间会被抬高</span></h2>
<p data-tool="mdnice编辑器">做到这里，Claude Code 的竞争对象就不再只是其它 coding assistant。它会开始接近「开发团队的操作层代理」：监听、执行、回报、沉淀记忆、跨渠道协作。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 的代价非常重</span></h1>
<p data-tool="mdnice编辑器">讲到这里如果只谈价值，那就是宣传稿了。工程里没有这么轻松的事。</p>
<p data-tool="mdnice编辑器">KAIROS 的代价主要有四类。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第一，系统复杂度暴涨</span></h2>
<p data-tool="mdnice编辑器">从一次性 CLI 进入常驻模式后，系统要处理的东西会指数级增加：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">长生命周期会话</section>
</li>
<li>
<section style="color: #010101;">bridge 重连</section>
</li>
<li>
<section style="color: #010101;">会话恢复</section>
</li>
<li>
<section style="color: #010101;">远端消息幂等</section>
</li>
<li>
<section style="color: #010101;">外部事件接入</section>
</li>
<li>
<section style="color: #010101;">后台任务状态</section>
</li>
<li>
<section style="color: #010101;">唤醒节奏</section>
</li>
<li>
<section style="color: #010101;">记忆蒸馏</section>
</li>
<li>
<section style="color: #010101;">多渠道权限边界</section>
</li>
<li>
<section style="color: #010101;">通知频率控制</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些全都会增加测试成本、排障成本、回归成本。</p>
<p data-tool="mdnice编辑器">传统 CLI 很多问题是可复现、可局部调试的。常驻 Agent 的问题常常是跨时间、跨系统、跨状态累积的。定位难度完全不是一个量级。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第二，成本模型会变差</span></h2>
<p data-tool="mdnice编辑器">tick + sleep 这套机制，本质上是在用更多调用换取持续在线行为。架构会更顺，产品体验会更强，API 成本也会上去。</p>
<p data-tool="mdnice编辑器">如果没有很严格的唤醒控制、任务优先级控制和输出压缩策略，系统会非常烧钱。尤其当常驻会话一多，哪怕每个会话只是周期性地「看一眼有没有事」，成本都可能迅速放大。</p>
<p data-tool="mdnice编辑器">很多团队做 Agent 产品，死得最早的往往不是能力不够，是成本失控。KAIROS 这条路如果真要产品化，成本治理必须和功能迭代同步推进，不能等做大了再补。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第三，安全与信任门槛会更高</span></h2>
<p data-tool="mdnice编辑器">一个普通 CLI 助手，最多是「用户下命令，它帮你执行」。<br />
一个 KAIROS 式常驻助手，是「它持续持有上下文，接外部消息，可能在后台自主执行」。</p>
<p data-tool="mdnice编辑器">这两个系统的风险等级完全不同。</p>
<p data-tool="mdnice编辑器">assistant 模式下先检查 trusted directory，再检查 KAIROS gate，这个设计信号已经很明确：作者知道风险在上升。问题是现在 gate 还是 stub，主链路还没完全接上，说明这块还在建设中。</p>
<p data-tool="mdnice编辑器">产品能不能卖进团队、卖进企业，很多时候就看这里。因为企业不会只问「它能做什么」，他们会问得更直接：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">它什么时候能自己执行</section>
</li>
<li>
<section style="color: #010101;">谁能给它发消息</section>
</li>
<li>
<section style="color: #010101;">它能看到哪些目录</section>
</li>
<li>
<section style="color: #010101;">它会把什么记下来</section>
</li>
<li>
<section style="color: #010101;">它错了怎么停</section>
</li>
<li>
<section style="color: #010101;">它怎么审计</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这些都是 KAIROS 必须正面回答的问题。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">第四，产品承诺和实现闭环还没完全对齐</span></h2>
<p data-tool="mdnice编辑器">这是当前最现实的问题。</p>
<p data-tool="mdnice编辑器">外围能力铺得已经不少，主入口和主状态层仍然有 stub。这个状态很典型：战略方向先行，支撑设施先铺，真正的产品主通路还在补。</p>
<p data-tool="mdnice编辑器">这种状态有机会，也有风险。</p>
<p data-tool="mdnice编辑器">机会在于，一旦主入口补齐，成长速度可能很快，因为外围都在等它。<br />
风险在于，如果主闭环补得太慢，外围越多，系统越像「展台很大，地基不稳」。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">为什么我认为 KAIROS 是整个项目里最重要的方向</span></h1>
<p data-tool="mdnice编辑器">因为它决定的不是某个 feature，而是产品类别。</p>
<p data-tool="mdnice编辑器">没有 KAIROS，Claude Code 依然可以是一个很强的 coding assistant CLI。<br />
有了 KAIROS，而且真做成了，它会变成一个持续在线、能跨渠道、能长期记忆、能异步执行的工程助手。</p>
<p data-tool="mdnice编辑器">这两者在商业形态上不是一个东西。<br />
在用户心智上不是一个东西。<br />
在组织采购逻辑上也不是一个东西。</p>
<p data-tool="mdnice编辑器">对个人开发者，这意味着「我下班以后，它还能继续跑」。<br />
对团队协作，这意味着「我们多了一个持续在线的 AI teammate」。<br />
对企业管理者，这意味着「AI 被接入的是工作流，不是单次问答」。</p>
<p data-tool="mdnice编辑器">这三层价值如果连起来，产品天花板会明显抬高。</p>
<p data-tool="mdnice编辑器">我甚至会说，KAIROS 成不成，决定了 Claude Code 最终是一个强工具，还是一个强平台。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">当前仓库里，KAIROS 的真实成熟度如何？</span></h1>
<p data-tool="mdnice编辑器">大概是四点：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">产品意图非常清楚</strong>： 因为从文档、prompt、memory 策略、bridge 设计、channels、brief，到后台任务工具，整个方向是一致的。不是东一块西一块拼起来的。它们都在服务同一个产品叙事：把 Claude Code 推成常驻助手。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">框架布线已经做了很多</strong>： 工具层、提示词层、bridge 层、memory prompt 分叉、channel notification、部分 viewer 路径，这些都已经不是空想。它们证明团队不是在写概念文档，而是在提前铺路。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">关键外围能力有真实实现</strong>：Bridge perpetual session、频道消息接入、Brief 规则、daily-log memory prompt，这些都具备产品骨架。哪怕主入口还没封口，外围支撑已经能看出最终形态。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">主入口和核心状态闭环仍有明显缺口</strong>：assistant 主模块、gate、session discovery、proactive 状态、session transcript 等地方的 stub，说明核心闭环还没完全长实。这个阶段我会把它定义为：接近产品化的战略子系统，而不是一个完整封版的功能包。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">KAIROS 的本质，是把 Claude Code 从「提效工具」推向「责任承接者」</span></h1>
<p data-tool="mdnice编辑器">文章写到这里，其实主已经很清楚了。</p>
<p data-tool="mdnice编辑器">KAIROS 真正改变的，是 Claude Code 的「存在方式」。</p>
<p data-tool="mdnice编辑器">普通 Claude Code 的存在方式是：用户打开终端，它出现；终端关闭，这轮交互的主价值结束。<br />
KAIROS 的存在方式是：用户不在场时，它仍然能持有状态、接收事件、维持节奏、积累记忆、回传结果。</p>
<p data-tool="mdnice编辑器">它在把 Claude Code 从前台交互工具，推向后台协作代理。</p>
<p data-tool="mdnice编辑器">一旦这个方向跑通，产品的护城河也会变。将来真正难被替代的，不只是模型回答质量，而会落在这些更重的东西上：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">上下文沉淀深度</section>
</li>
<li>
<section style="color: #010101;">渠道接入深度</section>
</li>
<li>
<section style="color: #010101;">工作流嵌入程度</section>
</li>
<li>
<section style="color: #010101;">组织内协同能力</section>
</li>
<li>
<section style="color: #010101;">长期责任承接能力</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这才是 KAIROS 值得持续投入的原因。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">小结</span></h1>
<p data-tool="mdnice编辑器">KAIROS 已经完成了产品方向的自洽，完成了相当一部分外围基础设施铺设，正在卡在主入口、记忆蒸馏和自主循环这三条上。只要这三条主链补齐，它很快就会从「战略子系统」变成「真正定义 Claude Code 下一阶段的核心产品」。</p>
<p data-tool="mdnice编辑器">这件事一旦做成，Claude Code 的故事就不再是「一个更强的 coding assistant CLI」。</p>
<p data-tool="mdnice编辑器">它会变成另一个东西。</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">一个持续在线的工程协作者。</section>
</li>
<li>
<section style="color: #010101;">一个能接进团队工作流的后台代理。</section>
</li>
<li>
<section style="color: #010101;">一个真正开始承接持续工作责任的 AI 系统。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这才是 KAIROS 的星辰大海。</p>
<p data-tool="mdnice编辑器">以上。</p>
<p>&nbsp;</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/04/claude-code-ai-kairos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深入 Claude Code 源码了解其记忆系统</title>
		<link>https://www.phppan.com/2026/04/claude-code-source-memory/</link>
		<comments>https://www.phppan.com/2026/04/claude-code-source-memory/#comments</comments>
		<pubDate>Sat, 04 Apr 2026 01:19:58 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[harness engineering]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2484</guid>
		<description><![CDATA[最近做 Agent 的同学应该大部分都有研读 Claude Code 泄漏的源码，网上出了各种 AI 加持下的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">最近做 Agent 的同学应该大部分都有研读 Claude Code 泄漏的源码，网上出了各种 AI 加持下的各种解读，教程，细节分析，甚至包括换了一种语言实现的版本，如 Python，Go，Rust 等等。感觉有点「一鲸落，万物生」的感觉。</p>
<p data-tool="mdnice编辑器">之前学习了 Claude Code 的系统提示词，写了一篇关于记忆系统的提示词。今天我们再深入其源码，看看其实现的细节。</p>
<p data-tool="mdnice编辑器">从其源码来看，</p>
<p data-tool="mdnice编辑器">Claude Code 这套记忆系统把几类完全不同的问题拆开处理了：长期记忆、当前轮相关记忆、会话压缩摘要、子代理独立记忆。和 OpenClaw 不同，OpenClaw 使用了统一的 Memory Service，加上一个向量库做检索，Claude Code 走的是另一条路：<strong style="color: #0e88eb;">文件系统优先，分层清晰，召回时机明确，代价可控</strong>。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">1. Claude Code 到底要记了什么</span></h1>
<p data-tool="mdnice编辑器">在 memoryTypes.ts#L14-L31 里，长期记忆的类型是的四类：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">user</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">feedback</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">project</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">reference</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">这四类东西有一个共同点：它们都<strong style="color: #0e88eb;">不容易从当前代码状态直接推导出来</strong>。用户习惯、项目背景、团队约束、外部系统入口，这些信息不写下来，下次对话就丢了。反过来，代码结构、文件路径、Git 历史、当前临时任务，这些内容源码里明确要求不要进长期记忆，因为它们本来就有权威来源。memoryTypes.ts#L183-L195</p>
<p data-tool="mdnice编辑器">记忆系统只该保存「代码外的信息」和「会跨轮次继续影响决策的信息」。以编程为例，当我们把代码事实也塞进去，后面一定会出现双份真相。你会遇到一个很尴尬的局面：代码说 A，memory 说 B，模型开始摇摆。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2. 四层分工</span></h1>
<p data-tool="mdnice编辑器">第一层是 <code style="color: #0e8aeb;">auto memory / team memory</code>。这是长期记忆，负责跨会话保存信息。目录逻辑在 paths.ts#L79-L259 和 teamMemPaths.ts#L66-L94。</p>
<p data-tool="mdnice编辑器">第二层是 <code style="color: #0e8aeb;">relevant memories</code>。这一层不关心长期存储，它只负责一件事：用户当前这一问，应该把哪几条历史记忆临时塞进上下文。入口在 findRelevantMemories.ts#L39-L141 和 attachments.ts#L2197-L2425。</p>
<p data-tool="mdnice编辑器">第三层是 <code style="color: #0e8aeb;">session memory</code>。这层服务的是长会话压缩，不负责跨会话记忆。位于当前 session 下的 <code style="color: #0e8aeb;">summary.md</code>。sessionMemory.ts#L183-L350</p>
<p data-tool="mdnice编辑器">第四层是 <code style="color: #0e8aeb;">agent memory</code>。子代理如果要持久化自己的经验，可以放 user/project/local 三种 scope 的独立目录。agentMemory.ts#L12-L177</p>
<p data-tool="mdnice编辑器">这四层拆开之后，很多设计选择就顺了：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">长期记忆用文件，便于审计和手工修复</section>
</li>
<li>
<section style="color: #010101;">当前轮召回走轻量检索，减少 prompt 污染</section>
</li>
<li>
<section style="color: #010101;">长会话压缩用单独 summary，避免每次 compact 都从头总结</section>
</li>
<li>
<section style="color: #010101;">子代理隔离状态，减少串味</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3. 长期记忆为什么选文件</span></h1>
<p data-tool="mdnice编辑器">Claude Code 的长期记忆是使用的 Markdown 文件。每条记忆一个文件，外加一个 <code style="color: #0e8aeb;">MEMORY.md</code> 入口索引。这部分规则在 memdir.ts#L199-L316 里写得很明白。</p>
<p data-tool="mdnice编辑器">源码里的写入约束是这样的：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-string" style="color: #98c379;">'## How to save memories'</span>,
<span class="hljs-string" style="color: #98c379;">''</span>,
<span class="hljs-string" style="color: #98c379;">'Saving a memory is a two-step process:'</span>,
<span class="hljs-string" style="color: #98c379;">''</span>,
<span class="hljs-string" style="color: #98c379;">'**Step 1** — write the memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:'</span>,
<span class="hljs-string" style="color: #98c379;">''</span>,
...MEMORY_FRONTMATTER_EXAMPLE,
<span class="hljs-string" style="color: #98c379;">''</span>,
<span class="hljs-string" style="color: #98c379;">`**Step 2** — add a pointer to that file in \`<span class="hljs-subst" style="color: #e06c75;">${ENTRYPOINT_NAME}</span>\`. \`<span class="hljs-subst" style="color: #e06c75;">${ENTRYPOINT_NAME}</span>\` is an index, not a memory — each entry should be one line, under ~150 characters: \`- [Title](file.md) — one-line hook\`. It has no frontmatter. Never write memory content directly into \`<span class="hljs-subst" style="color: #e06c75;">${ENTRYPOINT_NAME}</span>\`.`</span>,
</code></pre>
<p data-tool="mdnice编辑器">实现位置见 memdir.ts#L219-L230。</p>
<p data-tool="mdnice编辑器">还有有三个工程判断。</p>
<p data-tool="mdnice编辑器">第一，<code style="color: #0e8aeb;">MEMORY.md</code> 只是索引，不承载正文。如果把所有记忆都堆到一个大文件里，前期简单，后期灾难。Claude Code 从一开始就做拆分，每条记忆单文件，这样更新一条信息时不会引起全量重写。</p>
<p data-tool="mdnice编辑器">第二，frontmatter 强制有 <code style="color: #0e8aeb;">description</code> 字段，这个字段后面要参与召回。很多团队做知识条目，只写正文，不写检索摘要，最后靠 embedding 硬扛。Claude Code 反过来，它要求记忆写入阶段就产出一条高质量摘要。召回质量在写入那一刻就埋下去了。</p>
<p data-tool="mdnice编辑器">第三，完全基于文件系统，调试成本低。你可以直接去目录里看文件，团队同步时还能走 Git 或远端同步链路。数据库方案最大的问题不在性能，在可观察性。出了问题你要查 schema、查索引、查 embedding 版本、查写入日志，排障很慢。</p>
<p data-tool="mdnice编辑器">文件方案当然也有代价。文件一多，目录扫描成本会上升；<code style="color: #0e8aeb;">MEMORY.md</code> 入口过长也会逼近 prompt token 上限。Claude Code 后面靠动态召回机制兜住了这个问题，这个设计是连起来看的。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4. 写入链路</span></h1>
<p data-tool="mdnice编辑器">它怎么把记忆真正落盘</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.1 主模型直接写</span></h2>
<p data-tool="mdnice编辑器">长期记忆的第一条写入链路，是主模型自己写。系统 prompt 里已经告诉它记忆目录在哪、允许写什么、怎么写文件、什么时候写。</p>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">loadMemoryPrompt()</code> 会把 memory rules 注入系统提示词，入口在 [loadMemoryPrompt] memdir.ts#L419-L507。这一段 prompt 并没有替模型做决策，它只是把写入协议放进脑子里：目录、类型、索引格式、读取时机、失效校验。</p>
<p data-tool="mdnice编辑器">这意味着 Claude Code 对模型的假设很明确：模型可以自己判断「这条信息值不值得保存」，然后调用写文件工具去落盘。写入不是一个外置 API，写入就是普通文件操作。</p>
<p data-tool="mdnice编辑器">这条路有个好处：反馈延迟很低。用户刚说完「记住这个偏好」，主模型当轮就能写，不用等后台任务。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.2 后台抽取器补写</span></h2>
<p data-tool="mdnice编辑器">如果主模型这一轮没动手写，系统会在 turn end 触发后台抽取器。stop hook 在 stopHooks.ts#L141-L156：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">if</span> (
  feature(<span class="hljs-string" style="color: #98c379;">'EXTRACT_MEMORIES'</span>) &amp;&amp;
  !toolUseContext.agentId &amp;&amp;
  isExtractModeActive()
) {
  <span class="hljs-built_in" style="color: #e6c07b;">void</span> extractMemoriesModule!.executeExtractMemories(
    stopHookContext,
    toolUseContext.appendSystemMessage,
  )
}
</code></pre>
<p data-tool="mdnice编辑器">真正逻辑在 extractMemories.ts#L329-L567。</p>
<p data-tool="mdnice编辑器">这条链路里最重要的一段判断是：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">if</span> (hasMemoryWritesSince(messages, lastMemoryMessageUuid)) {
  logForDebugging(
    <span class="hljs-string" style="color: #98c379;">'[extractMemories] skipping — conversation already wrote to memory files'</span>,
  )
  ...
  <span class="hljs-keyword" style="color: #c678dd;">return</span>
}
</code></pre>
<p data-tool="mdnice编辑器">位置见 extractMemories.ts#L345-L360。</p>
<p data-tool="mdnice编辑器">它防的是双写。主模型已经写过，后台抽取器就别再重做一遍。很多系统做异步归档时忘了这件事，最后要么生成重复记忆，要么覆盖用户刚刚确认的内容。</p>
<p data-tool="mdnice编辑器">后台抽取器的权限也非常收敛。<code style="color: #0e8aeb;">createAutoMemCanUseTool()</code> 明确规定，只准：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">Read / Grep / Glob</section>
</li>
<li>
<section style="color: #010101;">只读 Bash</section>
</li>
<li>
<section style="color: #010101;">memory 目录内的 Edit / Write</section>
</li>
</ul>
<p data-tool="mdnice编辑器">实现见 [createAutoMemCanUseTool] extractMemories.ts#L166-L222。</p>
<p data-tool="mdnice编辑器">extractor 的职责：它只做归档，不许顺手验证代码，不许顺手修改业务文件，不许借机跑工具链。权限如果不锁死，后台代理迟早会从归档器膨胀成第二个主代理。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.3 KAIROS 下的写法</span></h2>
<p data-tool="mdnice编辑器">KAIROS 模式更有意思。它不要求模型实时维护 <code style="color: #0e8aeb;">MEMORY.md</code>，新记忆先按天追加到日志文件里。规则在 [buildAssistantDailyLogPrompt] memdir.ts#L318-L370。</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-string" style="color: #98c379;">"This session is long-lived. As you work, record anything worth remembering by **appending** to today's daily log file:"</span>,
<span class="hljs-string" style="color: #98c379;">` \`<span class="hljs-subst" style="color: #e06c75;">${logPathPattern}</span>\` `</span>,
<span class="hljs-string" style="color: #98c379;">'Write each entry as a short timestamped bullet. Create the file (and parent directories) on first write if it does not exist. Do not rewrite or reorganize the log — it is append-only. A separate nightly process distills these logs into `MEMORY.md` and topic files.'</span>,
</code></pre>
<p data-tool="mdnice编辑器">这条策略很适合长驻 Agent。会话存活时间长时，频繁重写 topic files 和索引很贵，冲突也多。先写 append-only 日志，夜间再蒸馏，吞吐更稳，模型也更不容易在白天工作时把记忆目录写乱。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5. 召回链路</span></h1>
<p data-tool="mdnice编辑器">它怎么决定哪段记忆该进来</p>
<p data-tool="mdnice编辑器">Claude Code 的召回要分成两种看。</p>
<p data-tool="mdnice编辑器">一种是静态注入，也就是固定随上下文加载的那些东西。另一种是动态召回，根据当前 query 临时挑选最相关的记忆文件。</p>
<p data-tool="mdnice编辑器">很多系统只做前者，结果上下文越来越肥。很多系统只做后者，结果基本行为约束丢了。Claude Code 两条都做。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.1 静态注入</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">getUserContext()</code> 会构造一个 <code style="color: #0e8aeb;">claudeMd</code> 字段，位置在 context.ts#L155-L188。</p>
<p data-tool="mdnice编辑器">核心调用是：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">const</span> claudeMd = shouldDisableClaudeMd
  ? <span class="hljs-literal" style="color: #56b6c2;">null</span>
  : getClaudeMds(filterInjectedMemoryFiles(<span class="hljs-keyword" style="color: #c678dd;">await</span> getMemoryFiles()))
</code></pre>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">getMemoryFiles()</code> 的实现很长，在 claudemd.ts#L790-L1075。它会统一加载：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">Managed 指令</section>
</li>
<li>
<section style="color: #010101;">User 指令</section>
</li>
<li>
<section style="color: #010101;">Project 指令</section>
</li>
<li>
<section style="color: #010101;">Local 指令</section>
</li>
<li>
<section style="color: #010101;">AutoMem 的 <code style="color: #0e8aeb;">MEMORY.md</code></section>
</li>
<li>
<section style="color: #010101;">TeamMem 的 <code style="color: #0e8aeb;">MEMORY.md</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">然后 <code style="color: #0e8aeb;">getClaudeMds()</code> 把这些文件串成提示词内容，[getClaudeMds] claudemd.ts#L1153-L1195。</p>
<p data-tool="mdnice编辑器">它给模型一个稳定的全局工作框架。它会知道项目规则、用户偏好、团队共享记忆索引。它适合放那些「大方向会持续生效」的内容。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.2 动态召回</span></h2>
<p data-tool="mdnice编辑器">静态注入解决不了所有问题。长期记忆正文一多，全部塞进 prompt 代价太高。Claude Code 的处理方式，是每轮用户发言后启动一个相关记忆预取。</p>
<p data-tool="mdnice编辑器">入口在 query.ts#L297-L304：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">using pendingMemoryPrefetch = startRelevantMemoryPrefetch(
  state.messages,
  state.toolUseContext,
)
</code></pre>
<p data-tool="mdnice编辑器">这个预取不会阻塞主流程。到后面条件满足时再消费，query.ts#L1595-L1617。</p>
<p data-tool="mdnice编辑器">真正检索逻辑在 attachments.ts#L2197-L2425。它会先决定搜索哪个目录：如果用户显式提到某个 agent，就搜 agent memory；否则搜 auto memory。</p>
<p data-tool="mdnice编辑器">然后调用 [findRelevantMemories] findRelevantMemories.ts#L39-L141。</p>
<p data-tool="mdnice编辑器">这里最有意思的点在于，它没有用向量库。它的步骤是：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><code style="color: #0e8aeb;">scanMemoryFiles()</code> 扫 memory 目录里的 <code style="color: #0e8aeb;">.md</code> 文件，读 frontmatter，产出一个 manifest<br />
见 memoryScan.ts#L35-L94</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">把 <code style="color: #0e8aeb;">用户 query + manifest</code> 发给一个 sideQuery 模型<br />
见 findRelevantMemories.ts#L77-L141</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">让这个模型返回最多 5 个文件名</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">再去读取这些文件正文，截断到限定行数和字节数<br />
见 [readMemoriesForSurfacing] attachments.ts#L2280-L2333</p>
</section>
</li>
</ol>
<p data-tool="mdnice编辑器">这套方案的好处：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">没有 embedding 构建成本</section>
</li>
<li>
<section style="color: #010101;">没有索引维护复杂度</section>
</li>
<li>
<section style="color: #010101;">manifest 很小，side query 很快</section>
</li>
<li>
<section style="color: #010101;">召回逻辑对开发者可见，容易调</section>
</li>
</ul>
<p data-tool="mdnice编辑器">缺点也明确。召回质量强依赖 frontmatter 的 <code style="color: #0e8aeb;">description</code>。写入时 description 写差了，后面召回一定差。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6. 记忆怎么进入上下文</span></h1>
<p data-tool="mdnice编辑器">不是一处注入，是四处入口</p>
<p data-tool="mdnice编辑器">很多人看 Agent 源码时老在问「上下文是在什么地方拼进去的」。这个问题本身就有误导性。Claude Code 里记忆的注入入口不止一个。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.1 system prompt 入口</span></h2>
<p data-tool="mdnice编辑器">第一处是 system prompt。这里进来的内容主要是「记忆系统的使用规则」，比如什么时候读、什么时候存、什么时候验证失效。对应实现是 prompts.ts#L492-L527 调 <code style="color: #0e8aeb;">loadMemoryPrompt()</code>。</p>
<p data-tool="mdnice编辑器">这是行为层指令。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.2 user context 入口</span></h2>
<p data-tool="mdnice编辑器">第二处是 <code style="color: #0e8aeb;">getUserContext()</code> 构造的 <code style="color: #0e8aeb;">claudeMd</code>。这里进来的是 <code style="color: #0e8aeb;">CLAUDE.md</code>、rules、<code style="color: #0e8aeb;">MEMORY.md</code> 这种比较稳定的文本。context.ts#L155-L188</p>
<p data-tool="mdnice编辑器">这是稳定背景层。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.3 attachment 入口</span></h2>
<p data-tool="mdnice编辑器">第三处是 relevant memory attachment。被召回的正文不会直接拼到 <code style="color: #0e8aeb;">claudeMd</code>，而是先变成 attachment，再由 messages.ts#L3743-L3756 包装成 <code style="color: #0e8aeb;">&lt;system-reminder&gt;</code>。</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">return</span> wrapMessagesInSystemReminder(
  attachment.memories.map(<span class="hljs-function"><span class="hljs-params">m</span> =&gt;</span> {
    <span class="hljs-keyword" style="color: #c678dd;">const</span> header = m.header ?? memoryHeader(m.path, m.mtimeMs)
    <span class="hljs-keyword" style="color: #c678dd;">return</span> createUserMessage({
      content: <span class="hljs-string" style="color: #98c379;">`<span class="hljs-subst" style="color: #e06c75;">${header}</span>\n\n<span class="hljs-subst" style="color: #e06c75;">${m.content}</span>`</span>,
      isMeta: <span class="hljs-literal" style="color: #56b6c2;">true</span>,
    })
  }),
)
</code></pre>
<p data-tool="mdnice编辑器">这意味着这些记忆是临时的、按轮次加载的、带 freshness header 的系统提醒。它的优先级和普通用户消息不同。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.4 compact summary 入口</span></h2>
<p data-tool="mdnice编辑器">第四处是 session memory compact。上下文过长后，系统会把会话前半段替换为一条 summary message，summary 内容来自 <code style="color: #0e8aeb;">summary.md</code> 的裁剪版。sessionMemoryCompact.ts#L437-L503</p>
<p data-tool="mdnice编辑器">这是上下文续命层。</p>
<p data-tool="mdnice编辑器">四处入口分工以后，就能看明白为什么 Claude Code 的行为相对稳定：规则、稳定背景、临时相关信息、压缩摘要，各走各的通道，互相不抢角色。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7. 真正的压缩发生在哪里</span></h1>
<p data-tool="mdnice编辑器">很多人一听「记忆系统」，第一反应是长期 memory 压缩。Claude Code 里最成熟的压缩逻辑，实际上落在 session memory 上。</p>
<p data-tool="mdnice编辑器">前面说过，session memory 是 <code style="color: #0e8aeb;">summary.md</code>，它本身就是会话结构化摘要。维护逻辑在 sessionMemory.ts#L272-L350。</p>
<p data-tool="mdnice编辑器">当上下文真的不够时，系统优先尝试 <code style="color: #0e8aeb;">trySessionMemoryCompaction()</code>，sessionMemoryCompact.ts#L514-L619。</p>
<p data-tool="mdnice编辑器">它的动作顺序：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">先确认 session memory 功能和 compact 功能都开着</section>
</li>
<li>
<section style="color: #010101;">等待正在进行中的 session memory 抽取结束</section>
</li>
<li>
<section style="color: #010101;">读取 <code style="color: #0e8aeb;">summary.md</code></section>
</li>
<li>
<section style="color: #010101;">如果还是空模板，放弃，退回传统 compact</section>
</li>
<li>
<section style="color: #010101;">计算需要保留的 recent messages 窗口</section>
</li>
<li>
<section style="color: #010101;">用 <code style="color: #0e8aeb;">summary.md</code> 的裁剪版构造 compact summary</section>
</li>
<li>
<section style="color: #010101;">组装 <code style="color: #0e8aeb;">boundary + summary + recent messages + attachments + hooks</code></section>
</li>
</ol>
<p data-tool="mdnice编辑器">这里的「保留 recent messages」特别关键。作者没有图省事把所有旧消息都抹掉，而是保留一段最近窗口。窗口大小由 [DEFAULT_SM_COMPACT_CONFIG] sessionMemoryCompact.ts#L56-L66 定义，默认：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">minTokens = 10000</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">minTextBlockMessages = 5</code></section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">maxTokens = 40000</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">保留窗口的计算在 [calculateMessagesToKeepIndex] sessionMemoryCompact.ts#L324-L397。</p>
<p data-tool="mdnice编辑器">这个策略解决的问题是：<strong style="color: #0e88eb;">摘要永远会损失细节</strong>，最近一段工作现场最好保留原始消息，模型续做时不至于失真。要是所有内容都只剩 summary，模型会失去工具调用上下文、局部错误信息、最近的计划变更。</p>
<p data-tool="mdnice编辑器">更细的一层防御在 <code style="color: #0e8aeb;">adjustIndexToPreserveAPIInvariants()</code>。 sessionMemoryCompact.ts#L232-L314</p>
<p data-tool="mdnice编辑器">它干的事情很硬核，也很必要：如果最近保留窗口里出现了 <code style="color: #0e8aeb;">tool_result</code>，系统必须把匹配的 <code style="color: #0e8aeb;">tool_use</code> 也补进来；如果 assistant 消息因为流式输出被拆成多个共享 <code style="color: #0e8aeb;">message.id</code> 的块，thinking 和 tool_use 也要一起补齐。否则 compact 后发给 API 的消息链会断，直接报错。</p>
<p data-tool="mdnice编辑器">这一段代码说明作者踩过坑，或者至少认真想过 API 侧不变量。很多开源 Agent 框架在消息压缩这里写得很草，最后线上 bug 都长一个样：tool result 找不到 parent，thinking 丢了，message 合并失败。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8. session memory 自己也会被裁剪</span></h1>
<p data-tool="mdnice编辑器">就算 <code style="color: #0e8aeb;">summary.md</code> 已经是摘要，compact 时还会再做一次 section 级裁剪。逻辑在 [truncateSessionMemoryForCompact] prompts.ts#L249-L295。</p>
<p data-tool="mdnice编辑器">过程如下：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">按 <code style="color: #0e8aeb;"># section</code> 拆段</section>
</li>
<li>
<section style="color: #010101;">每个 section 允许的大小用 <code style="color: #0e8aeb;">MAX_SECTION_LENGTH * 4</code> 粗略换算成字符数</section>
</li>
<li>
<section style="color: #010101;">超过就按行保留前半部分</section>
</li>
<li>
<section style="color: #010101;">最后插入 <code style="color: #0e8aeb;">[... section truncated for length ...]</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器">实际截断函数见 [flushSessionSection] prompts.ts#L298-L324。</p>
<p data-tool="mdnice编辑器">这套逻辑谈不上优雅，语义理解也谈不上深入，但它有一个优点：非常稳。系统真的到了上下文极限时，保底截断总比把整个 compact 失败掉强。工程里很多时候要的是「退化可接受」，不是「完美压缩」。</p>
<p data-tool="mdnice编辑器">然后 <code style="color: #0e8aeb;">createCompactionResultFromSessionMemory()</code> 把裁剪后的 session memory 包成 summary message。 sessionMemoryCompact.ts#L437-L503</p>
<p data-tool="mdnice编辑器">这里还有一个细节：如果发生过裁剪，它会额外附一句话，告诉模型和人类完整 session memory 文件路径在哪。排障时你可以直接打开原始 <code style="color: #0e8aeb;">summary.md</code>，不用猜裁掉了什么。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">9. KAIROS 的压缩逻辑和普通模式不一样</span></h1>
<p data-tool="mdnice编辑器">KAIROS 里还有另一种「压缩」，它压的不是当前上下文，而是长期事件流。</p>
<p data-tool="mdnice编辑器">在 memdir.ts#L321-L349 里能看到，KAIROS 模式下白天写的是 append-only daily log。到夜间，<code style="color: #0e8aeb;">/dream</code> 流程会把这些日志蒸馏成 topic files 和 <code style="color: #0e8aeb;">MEMORY.md</code>。</p>
<p data-tool="mdnice编辑器">这是另一类压缩：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">输入是时间顺序日志</section>
</li>
<li>
<section style="color: #010101;">输出是主题化长期记忆</section>
</li>
</ul>
<p data-tool="mdnice编辑器">session memory compact 处理的是「上下文窗口」问题。KAIROS dream 处理的是「长期事件沉淀」问题。这两类压缩混在一起看会非常乱，源码里其实已经把它们分得很开。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">10 小结</span></h2>
<p data-tool="mdnice编辑器">这套设计的工程代价与收益</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">10.1 一些值得学习的点</span></h2>
<p data-tool="mdnice编辑器">第一，分层彻底。长期记忆、当前轮召回、会话摘要、子代理记忆，各自有自己的存储形态和注入入口。系统复杂度是被隔离开的。</p>
<p data-tool="mdnice编辑器">第二，文件优先。排查方便，审计方便，人工纠错方便。很多团队高估了数据库和向量库的必要性，低估了可观察性的重要性。</p>
<p data-tool="mdnice编辑器">第三，动态召回走轻量 manifest + side query。对 CLI Agent 这种高频交互场景，这个方案的性价比很高。它把复杂度留给模型的小规模选择，而不是重型检索基础设施。</p>
<p data-tool="mdnice编辑器">第四，压缩时保 recent window，并修补 tool_use/tool_result 不变量。这一点极少有团队一开始就写对。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">10.2 一些代价</span></h2>
<p data-tool="mdnice编辑器">第一，frontmatter 的 <code style="color: #0e8aeb;">description</code> 质量变成关键依赖。这个字段一旦写烂，召回效果会大幅波动。它省掉了 embedding 的复杂度，也把一部分压力前置给写入质量。</p>
<p data-tool="mdnice编辑器">第二，双通道写入意味着状态机会更复杂。主模型可以写，后台 extractor 也能写。虽然代码里有跳过逻辑，但这类架构天然比单通道更需要小心。</p>
<p data-tool="mdnice编辑器">第三，session memory 的 section 截断是粗粒度的。它靠字符数近似 token，再按行截断，这属于保底工程，不属于精细压缩。能用，谈不上漂亮。</p>
<p data-tool="mdnice编辑器">第四，<code style="color: #0e8aeb;">MEMORY.md</code> 仍然有索引容量压力。即便动态召回已经分担了很大一部分负担，入口索引的组织质量依然重要。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">10.3 我们能用什么</span></h2>
<p data-tool="mdnice编辑器">如果把这套思路迁移到我们自己的 Agent 系统，可以借鉴（抄）：</p>
<p data-tool="mdnice编辑器">第一，先拆问题，再选技术。你要先决定自己在解哪件事：跨会话长期记忆、当前轮检索、超长对话压缩、团队共享经验。不要一上来就建一个统一 Memory API。</p>
<p data-tool="mdnice编辑器">第二，先用文件，再考虑数据库。只要你的系统规模还没逼到那个份上，文件系统几乎总是更划算。它便宜、透明、好调试。很多团队用数据库，是因为觉得那样「更像正经系统」，这个判断没什么含金量。</p>
<p data-tool="mdnice编辑器">第三，把召回质量的责任前移到写入阶段。Claude Code 用 <code style="color: #0e8aeb;">description</code> 做 manifest 检索这件事，给我的启发很大。与其指望后面靠复杂召回算法弥补，不如要求写入时就产出高质量摘要和类型信息。</p>
<p data-tool="mdnice编辑器">如果你们团队正在做本地化的企业内 Agent，我甚至建议先抄一版这种架构原型：<br />
长期记忆用 Markdown + frontmatter，当前轮召回走 manifest + 小模型筛选，会话压缩单独维护结构化 summary。三周内就能跑起来。比起一开始堆向量库、事件总线、关系数据库，这条路短得多。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">11. 其它</span></h1>
<p data-tool="mdnice编辑器">Claude Code 的记忆系统没有神秘技术。它的难点不在某个单点算法，在边界控制和时机设计。什么时候写，写到哪里，什么时候读，读多少，压缩后保留什么，这些问题都比「用什么模型做召回」更重要。</p>
<p data-tool="mdnice编辑器">如果你把这篇文章里的结论压成一句工程建议，那就是：<br />
先把长期记忆、短期相关记忆、会话压缩拆开，再谈检索和存储。</p>
<p data-tool="mdnice编辑器">源码入口可以优先读这几组文件：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">
<p style="color: #000000;">长期记忆规则与路径：<br />
[memdir.ts] [paths.ts] [memoryTypes.ts]</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">记忆静态注入与动态召回：<br />
[claudemd.ts] [attachments.ts] [findRelevantMemories.ts]</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">会话记忆与压缩：<br />
[sessionMemory.ts] [prompts.ts] [sessionMemoryCompact.ts]</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">团队共享记忆：<br />
[teamMemPaths.ts] [teamMemorySync/index.ts]</p>
</section>
</li>
</ul>
<p data-tool="mdnice编辑器">以上。</p>
</section>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/04/claude-code-source-memory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深度拆解 Claude Code 系统提示词中的记忆管理逻辑</title>
		<link>https://www.phppan.com/2026/03/in-depth-analysis-of-the-memory-management-logic-in-claude-code-system-prompts/</link>
		<comments>https://www.phppan.com/2026/03/in-depth-analysis-of-the-memory-management-logic-in-claude-code-system-prompts/#comments</comments>
		<pubDate>Sun, 29 Mar 2026 00:21:43 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[ClaudeCode]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2482</guid>
		<description><![CDATA[最近在做 Agent 相关的工作，研究了 Claude Code 的系统提示词。分享一下看到的东西。 Clau [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">最近在做 Agent 相关的工作，研究了 Claude Code 的系统提示词。分享一下看到的东西。</p>
<p data-tool="mdnice编辑器">Claude Code 这套逻辑最值得学习的部分，不是它有多少类型，也不是它怎么写文件，而是它把「记忆」从聊天历史里剥离成了一个有边界的系统对象。</p>
<p data-tool="mdnice编辑器">其提示词给出了四段闭环：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">类型化存储</section>
</li>
<li>
<section style="color: #010101;">索引化管理</section>
</li>
<li>
<section style="color: #010101;">触发式召回</section>
</li>
<li>
<section style="color: #010101;">使用前校验</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这套闭环的根本目的只有一个：<strong>极度压缩进入大模型上下文的无效 Token</strong>。</p>
<h1 data-tool="mdnice编辑器"><span class="content">类型化存储</span></h1>
<p data-tool="mdnice编辑器">类型化存储解决的是「谁有资格被记住」的问题。</p>
<p data-tool="mdnice编辑器">Claude Code 里把记忆分成 <code style="color: #ef7060;">user / feedback / project / reference</code>。这一步看上去像分类，实际上是在做准入控制。</p>
<p data-tool="mdnice编辑器">很多团队一开始偷懒，做一个统一的 memory 表，字段有 <code style="color: #ef7060;">content</code>、<code style="color: #ef7060;">created_at</code>、<code style="color: #ef7060;">embedding</code>，剩下全靠检索兜底。前期跑 demo 很爽，后期一团糟。因为「用户偏好」「项目约束」「纠错反馈」「外部入口」这几类东西的生命周期、可信度、更新频率和召回优先级完全不同。你把它们混在一起，后面所有策略都要靠额外条件补救。</p>
<p data-tool="mdnice编辑器">Claude Code 这里的好处在于，它先承认记忆不是同质数据。类型不同，保存条件就不同，召回方式也不同。</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #ef7060;">user</code> 影响的是回答风格和交互方式，天然高权重。</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #ef7060;">feedback</code> 代表用户纠正过的内容，这类信息如果不复用，系统会反复踩一个坑。</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #ef7060;">project</code> 带明显时效性，过期不处理就是埋雷。</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #ef7060;">reference</code> 更接近外部入口或指针，重点在可定位，不在长文本本身。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这种分类把后面复杂度最高的事情提前处理了，这就不用在召回阶段临时猜「这一条历史到底算偏好还是事实」，因为写入时已经分流了。</p>
<h1 data-tool="mdnice编辑器"><span class="content">索引化管理</span></h1>
<p data-tool="mdnice编辑器">Claude Code 会「先写独立记忆文件，再更新 <code style="color: #ef7060;">MEMORY.md</code> 索引」。这里把正文存储和索引存储分开了。</p>
<p data-tool="mdnice编辑器">有两个收益。</p>
<p data-tool="mdnice编辑器">第一，索引足够轻。<code style="color: #ef7060;">MEMORY.md</code> 只存索引，不存正文。这样它天然适合作为一个轻量入口，被优先加载、优先扫描、优先过滤。</p>
<p data-tool="mdnice编辑器">第二，正文可以演进。真正的记忆文件有 frontmatter 和正文，这意味着它可以承载更完整的上下文，而不用把所有内容都堆到一个总文件里。总文件一旦既做索引又做正文，后面就很难控制体积，也很难做精细更新。</p>
<p data-tool="mdnice编辑器">在写入时，有两条规则。</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">同主题记忆优先 update，避免重复新增。</section>
</li>
<li>
<section style="color: #010101;">用户明确说 forget，就删除对应记忆。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这两条是在控制系统熵增。记忆只要能无限追加，迟早会出现语义重复、事实冲突、时间污染。只做新增，不做更新和删除，系统很快就会进入「候选很多，但没有一条完全可信」的状态。到了那个阶段，召回层再聪明也救不回来。</p>
<h1 data-tool="mdnice编辑器"><span class="content">触发式召回</span></h1>
<p data-tool="mdnice编辑器">Claude Code 的建议流程是：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">先判断当前请求是否需要记忆；</section>
</li>
<li>
<section style="color: #010101;">按类型和关键词做少量 Top-K 粗召回；</section>
</li>
<li>
<section style="color: #010101;">再按「任务相关性 &gt; 新鲜度 &gt; 可靠性」精筛；</section>
</li>
<li>
<section style="color: #010101;">只注入必要片段；</section>
</li>
<li>
<section style="color: #010101;">如果和当前事实冲突，以当前事实为准并回写修正。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">其逻辑有如下几种：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">强指令触发（显式召回）：当用户明确下达指令（如“查一下”、“回想一下”、“你还记得吗”）时，系统被<strong style="color: #000000;">强制（MUST）</strong>触发召回链路。</section>
</li>
<li>
<section style="color: #010101;">上下文/语义触发（隐式召回）：系统在对话过程中，如果发现当前任务与已有记忆具有强相关性，或者用户提到了“之前的对话/工作”，则隐式触发召回。这要求大模型在理解当前意图时，顺带做一次记忆相关性判定。</section>
</li>
<li>
<section style="color: #010101;">负向门控触发（屏蔽/阻断召回）：当用户明确要求“忽略记忆”或“不要用记忆”时，系统必须直接切断召回链路，假装索引文件 MEMORY.md 是空的，防止历史上下文污染当前的新任务。</section>
</li>
</ol>
<h1 data-tool="mdnice编辑器"><span class="content">使用前校验</span></h1>
<p data-tool="mdnice编辑器">使用前校验，解决的是「记忆不是事实源」</p>
<p data-tool="mdnice编辑器">记忆里如果提到文件、函数、flag，落地前必须重新核验当前状态。</p>
<p data-tool="mdnice编辑器">记忆的本质是「<strong>过去曾经成立过的信息</strong>」。代码仓库、配置开关、函数签名这些东西会变。如果把记忆当事实源，模型越有记忆，出错概率越高。尤其在代码场景里，这种错会放大。因为模型不是只回答一句话，它还会基于过期事实继续生成修改方案、命令、排障路径。</p>
<p data-tool="mdnice编辑器">记忆负责缩小搜索空间，当前状态负责给出最终裁决。</p>
<p data-tool="mdnice编辑器"><strong>做记忆系统时，最警惕的一直是脏记忆。空记忆顶多让模型少一点个性，脏记忆会直接让模型说错话。</strong></p>
<p data-tool="mdnice编辑器">以上。</p>
<p data-tool="mdnice编辑器">附原始提示词（2.1.86 版本）</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">
<span class="hljs-comment" style="font-style: italic; color: #5c6370;">## auto memory</span>

You have a persistent, file-based memory system at `/root/.claude/projects/-tmp-claude-history-1774690103689-avi2cu/memory/`. This directory already exists — write to it directly with the Write tool (<span class="hljs-keyword" style="color: #c678dd;">do</span> not run mkdir or check <span class="hljs-keyword" style="color: #c678dd;">for</span> its existence).

You should build up this memory system over time so that future conversations can have a complete picture of who the user is, how they<span class="hljs-string" style="color: #98c379;">'d like to collaborate with you, what behaviors to avoid or repeat, and the context behind the work the user gives you.

If the user explicitly asks you to remember something, save it immediately as whichever type fits best. If they ask you to forget something, find and remove the relevant entry.

### Types of memory

There are several discrete types of memory that you can store in your memory system:

&lt;types&gt;
&lt;type&gt;
    &lt;name&gt;user&lt;/name&gt;
    &lt;description&gt;Contain information about the user'</span>s role, goals, responsibilities, and knowledge. Great user memories <span class="hljs-built_in" style="color: #e6c07b;">help</span> you tailor your future behavior to the user<span class="hljs-string" style="color: #98c379;">'s preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you'</span>re trying to accomplish together.&lt;/description&gt;
    &lt;when_to_save&gt;When you learn any details about the user<span class="hljs-string" style="color: #98c379;">'s role, preferences, responsibilities, or knowledge&lt;/when_to_save&gt;
    &lt;how_to_use&gt;When your work should be informed by the user'</span>s profile or perspective. For example, <span class="hljs-keyword" style="color: #c678dd;">if</span> the user is asking you to explain a part of the code, you should answer that question <span class="hljs-keyword" style="color: #c678dd;">in</span> a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model <span class="hljs-keyword" style="color: #c678dd;">in</span> relation to domain knowledge they already have.&lt;/how_to_use&gt;
    &lt;examples&gt;
    user: I<span class="hljs-string" style="color: #98c379;">'m a data scientist investigating what logging we have in place
    assistant: [saves user memory: user is a data scientist, currently focused on observability/logging]

    user: I'</span>ve been writing Go <span class="hljs-keyword" style="color: #c678dd;">for</span> ten years but this is my first time touching the React side of this repo
    assistant: [saves user memory: deep Go expertise, new to React and this project<span class="hljs-string" style="color: #98c379;">'s frontend — frame frontend explanations in terms of backend analogues]
    &lt;/examples&gt;
&lt;/type&gt;
&lt;type&gt;
    &lt;name&gt;feedback&lt;/name&gt;
    &lt;description&gt;Guidance the user has given you about how to approach work — both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious.&lt;/description&gt;
    &lt;when_to_save&gt;Any time the user corrects your approach ("no not that", "don'</span>t<span class="hljs-string" style="color: #98c379;">", "</span>stop doing X<span class="hljs-string" style="color: #98c379;">") OR confirms a non-obvious approach worked ("</span>yes exactly<span class="hljs-string" style="color: #98c379;">", "</span>perfect, keep doing that<span class="hljs-string" style="color: #98c379;">", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter — watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later.&lt;/when_to_save&gt;
    &lt;how_to_use&gt;Let these memories guide your behavior so that the user does not need to offer the same guidance twice.&lt;/how_to_use&gt;
    &lt;body_structure&gt;Lead with the rule itself, then a **Why:** line (the reason the user gave — often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule.&lt;/body_structure&gt;
    &lt;examples&gt;
    user: don't mock the database in these tests — we got burned last quarter when mocked tests passed but the prod migration failed
    assistant: [saves feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration]

    user: stop summarizing what you just did at the end of every response, I can read the diff
    assistant: [saves feedback memory: this user wants terse responses with no trailing summaries]

    user: yeah the single bundled PR was the right call here, splitting this one would've just been churn
    assistant: [saves feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach — a validated judgment call, not a correction]
    &lt;/examples&gt;
&lt;/type&gt;
&lt;type&gt;
    &lt;name&gt;project&lt;/name&gt;
    &lt;description&gt;Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work the user is doing within this working directory.&lt;/description&gt;
    &lt;when_to_save&gt;When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "</span>Thursday<span class="hljs-string" style="color: #98c379;">" → "</span>2026-03-05<span class="hljs-string" style="color: #98c379;">"), so the memory remains interpretable after time passes.&lt;/when_to_save&gt;
    &lt;how_to_use&gt;Use these memories to more fully understand the details and nuance behind the user's request and make better informed suggestions.&lt;/how_to_use&gt;
    &lt;body_structure&gt;Lead with the fact or decision, then a **Why:** line (the motivation — often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing.&lt;/body_structure&gt;
    &lt;examples&gt;
    user: we're freezing all non-critical merges after Thursday — mobile team is cutting a release branch
    assistant: [saves project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date]

    user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements
    assistant: [saves project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup — scope decisions should favor compliance over ergonomics]
    &lt;/examples&gt;
&lt;/type&gt;
&lt;type&gt;
    &lt;name&gt;reference&lt;/name&gt;
    &lt;description&gt;Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory.&lt;/description&gt;
    &lt;when_to_save&gt;When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel.&lt;/when_to_save&gt;
    &lt;how_to_use&gt;When the user references an external system or information that may be in an external system.&lt;/how_to_use&gt;
    &lt;examples&gt;
    user: check the Linear project "</span>INGEST<span class="hljs-string" style="color: #98c379;">" if you want context on these tickets, that's where we track all pipeline bugs
    assistant: [saves reference memory: pipeline bugs are tracked in Linear project "</span>INGEST<span class="hljs-string" style="color: #98c379;">"]

    user: the Grafana board at grafana.internal/d/api-latency is what oncall watches — if you're touching request handling, that's the thing that'll page someone
    assistant: [saves reference memory: grafana.internal/d/api-latency is the oncall latency dashboard — check it when editing request-path code]
    &lt;/examples&gt;
&lt;/type&gt;
&lt;/types&gt;

### What NOT to save in memory

- Code patterns, conventions, architecture, file paths, or project structure — these can be derived by reading the current project state.
- Git history, recent changes, or who-changed-what — `git log` / `git blame` are authoritative.
- Debugging solutions or fix recipes — the fix is in the code; the commit message has the context.
- Anything already documented in CLAUDE.md files.
- Ephemeral task details: in-progress work, temporary state, current conversation context.

These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping.

### How to save memories

Saving a memory is a two-step process:

**Step 1** — write the memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:


---
name: {{memory name}}
description: {{one-line description — used to decide relevance in future conversations, so be specific}}
type: {{user, feedback, project, reference}}
---

{{memory content — for feedback/project types, structure as: rule/fact, then **Why:** and **How to apply:** lines}}


**Step 2** — add a pointer to that file in `MEMORY.md`. `MEMORY.md` is an index, not a memory — each entry should be one line, under ~150 characters: `- [Title](file.md) — one-line hook`. It has no frontmatter. Never write memory content directly into `MEMORY.md`.

- `MEMORY.md` is always loaded into your conversation context — lines after 200 will be truncated, so keep the index concise
- Keep the name, description, and type fields in memory files up-to-date with the content
- Organize memory semantically by topic, not chronologically
- Update or remove memories that turn out to be wrong or outdated
- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.

### When to access memories
- When memories seem relevant, or the user references prior-conversation work.
- You MUST access memory when the user explicitly asks you to check, recall, or remember.
- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content.
- Memory records can become stale over time. Use memory as context for what was true at a given point in time. Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources. If a recalled memory conflicts with current information, trust what you observe now — and update or remove the stale memory rather than acting on it.

### Before recommending from memory

A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it:

- If the memory names a file path: check the file exists.
- If the memory names a function or flag: grep for it.
- If the user is about to act on your recommendation (not just asking about history), verify first.

"</span>The memory says X exists<span class="hljs-string" style="color: #98c379;">" is not the same as "</span>X exists now.<span class="hljs-string" style="color: #98c379;">"

A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot.

### Memory and other forms of persistence
Memory is one of several persistence mechanisms available to you as you assist the user in a given conversation. The distinction is often that memory can be recalled in future conversations and should not be used for persisting information that is only useful within the scope of the current conversation.
- When to use or update a plan instead of memory: If you are about to start a non-trivial implementation task and would like to reach alignment with the user on your approach you should use a Plan rather than saving this information to memory. Similarly, if you already have a plan within the conversation and you have changed your approach persist that change by updating the plan rather than saving a memory.
- When to use or update tasks instead of memory: When you need to break your work in current conversation into discrete steps or keep track of your progress use tasks instead of saving to memory. Tasks are great for persisting information about the work that needs to be done in the current conversation, but memory should be reserved for information that will be useful in future conversations.
</span></code></pre>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/03/in-depth-analysis-of-the-memory-management-logic-in-claude-code-system-prompts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
