<?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>潘锦的空间 &#187; Agent</title>
	<atom:link href="https://www.phppan.com/tag/agent/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 25 Apr 2026 00:56:17 +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 落地工程实践的一些想法和思考</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 源码了解其记忆系统</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>聊下 OpenClaw 的记忆系统</title>
		<link>https://www.phppan.com/2026/03/openclaw-memory/</link>
		<comments>https://www.phppan.com/2026/03/openclaw-memory/#comments</comments>
		<pubDate>Sun, 15 Mar 2026 11:37:01 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[Memory System]]></category>
		<category><![CDATA[OpenClaw]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2477</guid>
		<description><![CDATA[OpenClaw 是最近 AI 圈最火的一个开源项目，没有之一。 从去年的 Agent 年，到今年的 AI 个 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">OpenClaw 是最近 AI 圈最火的一个开源项目，没有之一。</p>
<p data-tool="mdnice编辑器">从去年的 Agent 年，到今年的 AI 个人助理，OpenClaw 和去年 Manus 一样的，爆到不行，而且还是开源的版本。</p>
<p data-tool="mdnice编辑器">由于最近自己也在做 Agent，于是也看了 OpenClaw 的代码来了解其记忆系统的实现。有一些觉得可以借鉴学习的地方。</p>
<p data-tool="mdnice编辑器">OpenClaw 的记忆系统其实比较简单：它把「记忆」拆成了<strong>文件</strong>、<strong>索引</strong>、<strong>召回注入</strong>。</p>
<h1 data-tool="mdnice编辑器"><span class="content">1. 「Agent 记忆系统」的定义</span></h1>
<p data-tool="mdnice编辑器">在 Agent 工程里，记忆是一套能力组合：</p>
<ol data-tool="mdnice编辑器">
<li>
<section><strong>持久化</strong>：跨会话保存事实、偏好、决策、未完成事项。</section>
</li>
<li>
<section><strong>可检索</strong>：能在需要时把相关片段拉出来，且可控预算。</section>
</li>
<li>
<section><strong>可注入</strong>：把召回结果以确定的结构进入模型上下文，不靠「它自己想起来」。</section>
</li>
<li>
<section><strong>可审计</strong>：出了错能定位「写入发生在什么时候」「召回命中了什么」「注入了哪些行」。</section>
</li>
<li>
<section><strong>可治理</strong>：能处理泄露风险、过期信息、重复信息、冲突信息。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">OpenClaw 的实现路径非常「工程」：<strong>Markdown 作为事实源</strong>，<strong>SQLite 作为检索索引</strong>，<strong>toolResult 作为注入通道</strong>。</p>
<h1 data-tool="mdnice编辑器"><span class="content">2. OpenClaw 的三层记忆</span></h1>
<p data-tool="mdnice编辑器">OpenClaw 的记忆从存储逻辑上来看可以分为三层：</p>
<h2 data-tool="mdnice编辑器"><span class="content">2.1 会话记忆</span></h2>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>介质</strong>：内存为主，但 OpenClaw 会把 session 打印成类似日志的文件，放到 <code>sessions</code> 目录。</section>
</li>
<li>
<section><strong>内容</strong>：用户消息、OpenClaw 的思考过程、工具调用、skill 调用、最终回复。</section>
</li>
<li>
<section><strong>边界</strong>：会话结束后「可用性」就不可靠了。你能在文件里回放，但模型下一次对话并不会天然带着它。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">会话记忆更像「trace」。我们不能指望它解决跨会话连续性，只用它做排障、复盘、抽取素材（写入短期/长期）。</p>
<h2 data-tool="mdnice编辑器"><span class="content">2.2 短期记忆</span></h2>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>介质</strong>：磁盘，<code>memory/YYYY-MM-DD.md</code> 为主（参考内容给了例子 <code>2026-03-10.md</code>）。</section>
</li>
<li>
<section><strong>内容</strong>：当天重要事件、过程笔记、TODO。关键点是「重要性」由人设与调教决定。</section>
</li>
<li>
<section><strong>边界</strong>：短期记忆是<strong>追加式日志</strong>，质量会漂移。写得越多，噪声越大；但写得太少，又召回不到。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">短期记忆适合承接「会话压缩之前的落盘」和「跨几天的上下文连续性」。它不是最终事实源，别把它当永久协议文档。</p>
<h2 data-tool="mdnice编辑器"><span class="content">2.3 长期记忆</span></h2>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>介质</strong>：工作区根目录 <code>MEMORY.md</code>（参考内容明确）。</section>
</li>
<li>
<section><strong>内容</strong>：核心认知与关系、偏好风格、长期目标、进行中任务、关键事件/教训/决策。</section>
</li>
<li>
<section><strong>边界</strong>：参考内容强调「只在主会话加载」，群聊等任务不加载，避免泄露。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><code>MEMORY.md</code> ==「可执行的组织记忆」。它的价值不在于「写得多」，在于「冲突少、可被召回、能约束后续行为」。这层要治理，要像维护配置一样维护。</p>
<h1 data-tool="mdnice编辑器"><span class="content">3. OpenClaw 的文件布局</span></h1>
<h2 data-tool="mdnice编辑器"><span class="content">3.1 「会话快照」文件</span></h2>
<p data-tool="mdnice编辑器">快照文件主要是解决 <code>/new</code>、<code>/reset</code> 指令的断片</p>
<p data-tool="mdnice编辑器">session-memory 的 Hook 会在你执行 <code>/new</code> 或 <code>/reset</code> 前，把上一会话最近 N 条对话（默认 15 条）抽出来，写成一个 Markdown 文件放到 <code>workspace/memory/</code> 下。</p>
<ul data-tool="mdnice编辑器">
<li>
<section>命名：<code>YYYY-MM-DD-&lt;slug&gt;.md</code>，slug 通常由 LLM 根据主题生成；LLM 不可用就回退成 <code>HHMM</code>。</section>
</li>
<li>
<section>关键点：这种文件也会被检索索引到。原因是 <code>listMemoryFiles()</code> 会递归扫描 <code>workspace/memory/</code> 下所有 <code>.md</code>，并不要求必须是 <code>YYYY-MM-DD.md</code>（ <code>src/memory/internal.ts:115-145</code> ）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这个机制对「人类工作流」很友好。很多团队的真实使用是：今天临时开了个话题，明天又忘了开在另一个会话里。会话快照能把碎片变成可检索素材，后面再沉淀进 <code>MEMORY.md</code>。</p>
<h2 data-tool="mdnice编辑器"><span class="content">3.2 <code>memory/main.sqlite</code>：索引库</span></h2>
<ul data-tool="mdnice编辑器">
<li>
<section><code>memory/main.sqlite</code> 基本可以确定是「记忆搜索（memory_search）」用的 SQLite 索引库。</section>
</li>
<li>
<section>索引对象：<code>MEMORY.md</code>、<code>memory/**/*.md</code>，以及你配置的 <code>extraPaths</code>，可选 session transcripts。</section>
</li>
<li>
<section>检索方式：FTS/BM25 关键词检索 +（可选）向量相似度检索（sqlite-vec）。</section>
</li>
<li>
<section>它存的典型结构：<code>files</code>、<code>chunks</code>、<code>embedding_cache</code>，以及可选的 FTS 表、向量虚表。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong>事实源是文本文件</strong>，<strong>索引是可重建的派生物</strong>。索引坏了你删掉重建就行；事实源坏了才是真的坏。</p>
<h1 data-tool="mdnice编辑器"><span class="content">4. 怎么建索引：</span></h1>
<p data-tool="mdnice编辑器">建索引我们关心的三件事：增量、去重、成本</p>
<p data-tool="mdnice编辑器">OpenClaw 的索引构建不是「每次全量重算」，也不是「精细 diff」：</p>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>更新粒度</strong>：按文件做增量。文件没变直接跳过。</section>
</li>
<li>
<section><strong>分块粒度</strong>：文件变了就重建该文件 chunks。</section>
</li>
<li>
<section><strong>向量成本</strong>：chunk embedding 通过缓存复用，避免重复调用 embedding provider。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content">4.1 扫描哪些文件会进索引</span></h2>
<p data-tool="mdnice编辑器">OpenClaw 会递归扫描 <code>workspace/memory/</code> 下所有 <code>.md</code>，并包含 <code>MEMORY.md</code>/<code>memory.md</code>。对应定位是 <code>src/memory/internal.ts:115-145</code>。</p>
<h2 data-tool="mdnice编辑器"><span class="content">4.2 文件级 hash：没变就跳过</span></h2>
<p data-tool="mdnice编辑器">文件 hash 的策略：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>Markdown：<code>hash = sha256(content)</code>（<code>internal.ts:L245-L263</code>）</section>
</li>
<li>
<section>多模态：buffer 也会 hash，最后把 <code>{path, contentText, mimeType, dataHash}</code> 做 JSON 再 sha256（<code>internal.ts:L204-L243</code>）</section>
</li>
<li>
<section>增量判定：对比 <code>files</code> 表里的 hash，一致就跳过（参考内容列了 memory 文件与 session 文件两条路径）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">hash 是增量的核心。它的意义不止省时间，还省钱：embedding provider 往往是计费点。这种主要是对于使用第三方 embedding 的。</p>
<h2 data-tool="mdnice编辑器"><span class="content">4.3 分块策略：<code>tokens*4</code> 的字符近似</span></h2>
<p data-tool="mdnice编辑器"><code>chunkMarkdown()</code> 的策略：</p>
<ul data-tool="mdnice编辑器">
<li>
<section><code>maxChars = tokens * 4</code>，<code>overlapChars = overlap * 4</code></section>
</li>
<li>
<section>以「行」为主，超长行会被切段</section>
</li>
<li>
<section>每块生成 <code>hash=sha256(text)</code>，并有 <code>embeddingInput</code></section>
</li>
</ul>
<p data-tool="mdnice编辑器"><code>tokens*4</code> 这种近似在工程里挺常见，优点是简单、稳定、跨模型大差不差。缺点也明显：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>语言差异会影响 token/char 比例；中英文混排时 chunk 尺寸会漂。</section>
</li>
<li>
<section>以行切块对 Markdown 友好，但对「一行很长的 JSON 或日志」不友好。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">可以盯两个指标来调 chunking：</p>
<ol data-tool="mdnice编辑器">
<li>
<section>平均召回 snippet 的「可读性」和「自洽性」；</section>
</li>
<li>
<section>SQLite 体积与索引更新耗时。chunk 太小召回碎，太大注入贵。</section>
</li>
</ol>
<h2 data-tool="mdnice编辑器"><span class="content">4.4 chunk 的唯一标识与 upsert</span></h2>
<p data-tool="mdnice编辑器">去重靠 id，chunk 写入策略如下：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>chunk <code>id</code>：<code>sha256("${source}:${path}:${startLine}:${endLine}:${chunk.hash}:${provider.model}")</code></section>
</li>
<li>
<section>同一 id：<code>ON CONFLICT(id) DO UPDATE</code> 覆盖更新</section>
</li>
<li>
<section>文件要重建时会先清旧再写新（参考内容总结了「清旧再写新」语义）</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这里有个很实际的 trade-off：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>它不做「chunk diff」，所以文件变了就重建该文件 chunks，逻辑简单，坏处是 IO 多。</section>
</li>
<li>
<section>但 embedding 通过缓存复用，把最贵的部分压下去了。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">另外，<code>id</code> 里带了 <code>provider.model</code>，这会带来一个工程后果：<strong>embedding 模型换了，chunk id 会变</strong>，索引层面等价于全量重建。 <code>provider/model/providerKey</code> 变化会触发 full reindex。</p>
<h2 data-tool="mdnice编辑器"><span class="content">4.5 embedding_cache</span></h2>
<p data-tool="mdnice编辑器">embedding 缓存的主键设计：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>主键：<code>(provider, model, provider_key, hash)</code></section>
</li>
<li>
<section><code>provider_key</code> 会把 endpoint/headers 等纳入指纹，避免跨配置污染缓存（而且会剔除授权头的细节在参考内容里提到）。</section>
</li>
<li>
<section>批量加载命中就跳过 embed；miss 才请求，成功回写缓存（对应 <code>manager-embedding-ops.ts</code> 的行段）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这是「上线能用」的关键。否则就会遇到一个很尴尬的情况：<br />
索引更新频繁触发 embedding 重算 → 延迟抖动 → API 费暴涨 → 还可能被 provider 限流。 system prompt 的 skills 段落甚至提醒「假设有 rate limits，避免 tight loop」，这就有点被打过之后写进规范的味道。</p>
<h1 data-tool="mdnice编辑器"><span class="content">5. 更新怎么触发</span></h1>
<p data-tool="mdnice编辑器">watch、interval、onSearch、session-delta</p>
<p data-tool="mdnice编辑器">索引更新如果做得「过勤」，会把 CPU 和 IO 吃满；做得「过懒」，召回就是过期的。OpenClaw 在触发上给了多条路径：</p>
<ol data-tool="mdnice编辑器">
<li>
<section><strong>watch</strong>：chokidar 监听 <code>MEMORY.md</code>、<code>memory.md</code>、<code>memory/**/*.md</code>（以及 extraPaths、多模态扩展），变更标记 dirty，debounce 后 <code>sync(reason="watch")</code>。</section>
</li>
<li>
<section><strong>interval</strong>：<code>sync.intervalMinutes&gt;0</code> 就 <code>setInterval</code> 定时跑。</section>
</li>
<li>
<section><strong>onSessionStart</strong>：search 前 <code>warmSession(sessionKey)</code>，若开了 <code>sync.onSessionStart</code>，每个 sessionKey 首次触发后台 sync。</section>
</li>
<li>
<section><strong>onSearch</strong>：search 时如果 dirty 且开了 <code>sync.onSearch</code>，后台触发 sync。</section>
</li>
<li>
<section><strong>session-delta</strong>：监听 transcript 更新，累计新增 bytes/lines，达到阈值后把相关 session 文件标脏，再 sync。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">还有两点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>单飞锁</strong>：同一时刻只跑一个 sync，复用同一个 Promise（参考内容定位 <code>manager.ts:452-467</code>）。</section>
</li>
<li>
<section><strong>全量重建的原子 swap</strong>：写到 <code>.tmp-UUID</code>，完成后 swap（含 <code>wal/-shm</code>），避免半成品索引（参考内容定位 <code>manager-sync-ops.ts:1050-1158</code>）。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content">6. 召回是怎么发生的</span></h1>
<p data-tool="mdnice编辑器">主要看 <code>buildMemorySection()</code> 的代码，因为它把策略写死了：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs">提示词：
<span class="hljs-keyword">function</span> buildSkillsSection(params: { skillsPrompt?: string; readToolName: string }) {
  const trimmed = params.skillsPrompt?.trim();
  <span class="hljs-keyword">if</span> (!trimmed) {
    <span class="hljs-built_in">return</span> [];
  }
  <span class="hljs-built_in">return</span> [
    <span class="hljs-string">"## Skills (mandatory)"</span>,
    <span class="hljs-string">"Before replying: scan &lt;available_skills&gt; &lt;description&gt; entries."</span>,
    `- If exactly one skill clearly applies: <span class="hljs-built_in">read</span> its SKILL.md at &lt;location&gt; with \`<span class="hljs-variable">${params.readToolName}</span>\`, <span class="hljs-keyword">then</span> follow it.`,
    <span class="hljs-string">"- If multiple could apply: choose the most specific one, then read/follow it."</span>,
    <span class="hljs-string">"- If none clearly apply: do not read any SKILL.md."</span>,
    <span class="hljs-string">"Constraints: never read more than one skill up front; only read after selecting."</span>,
    <span class="hljs-string">"- When a skill drives external API writes, assume rate limits: prefer fewer larger writes, avoid tight one-item loops, serialize bursts when possible, and respect 429/Retry-After."</span>,
    trimmed,
    <span class="hljs-string">""</span>,
  ];
}

<span class="hljs-keyword">function</span> buildMemorySection(params: {
  isMinimal: boolean;
  availableTools: Set&lt;string&gt;;
  citationsMode?: MemoryCitationsMode;
}) {
  <span class="hljs-keyword">if</span> (params.isMinimal) {
    <span class="hljs-built_in">return</span> [];
  }
  <span class="hljs-keyword">if</span> (!params.availableTools.has(<span class="hljs-string">"memory_search"</span>) &amp;&amp; !params.availableTools.has(<span class="hljs-string">"memory_get"</span>)) {
    <span class="hljs-built_in">return</span> [];
  }
  const lines = [
    <span class="hljs-string">"## Memory Recall"</span>,
    <span class="hljs-string">"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only the needed lines. If low confidence after search, say you checked."</span>,
  ];
  <span class="hljs-keyword">if</span> (params.citationsMode === <span class="hljs-string">"off"</span>) {
    lines.push(
      <span class="hljs-string">"Citations are disabled: do not mention file paths or line numbers in replies unless the user explicitly asks."</span>,
    );
  } <span class="hljs-keyword">else</span> {
    lines.push(
      <span class="hljs-string">"Citations: include Source: &lt;path#line&gt; when it helps the user verify memory snippets."</span>,
    );
  }
  lines.push(<span class="hljs-string">""</span>);
  <span class="hljs-built_in">return</span> lines;
}

工具描述：
 label: <span class="hljs-string">"Memory Search"</span>,
    name: <span class="hljs-string">"memory_search"</span>,
    description:
      <span class="hljs-string">"Mandatory recall step: semantically search MEMORY.md + memory/*.md (and optional session transcripts) before answering questions about prior work, decisions, dates, people, preferences, or todos; returns top snippets with path + lines. If response has disabled=true, memory retrieval is unavailable and should be surfaced to the user."</span>,
    parameters: MemorySearchSchema,
</code></pre>
<p data-tool="mdnice编辑器">有三点：</p>
<ol data-tool="mdnice编辑器">
<li>
<section><strong>触发条件写得具体</strong>：prior work / decisions / dates / people / preferences / todos。模型不需要猜「算不算记忆相关」。</section>
</li>
<li>
<section><strong>两阶段召回</strong>：先 <code>memory_search</code> 找片段，再 <code>memory_get</code> 精读少量行，控制注入体积。</section>
</li>
<li>
<section><strong>引用策略可控</strong>：<code>citationsMode</code> 可以关掉，避免模型动不动把路径行号甩出来（对产品形态很重要）。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">两阶段召回比「一次性把相关文件 wholefile 塞进去」靠谱太多。</p>
<h1 data-tool="mdnice编辑器"><span class="content">7. 召回结果怎么进上下文</span></h1>
<p data-tool="mdnice编辑器">toolResult 消息是关键通道</p>
<p data-tool="mdnice编辑器">很多人以为「记忆」是把内容写进 system prompt。OpenClaw 不是这么干的。</p>
<p data-tool="mdnice编辑器">召回结果会以工具执行结果的形式进入会话消息列表，后续模型调用自然「看得到」。</p>
<ul data-tool="mdnice编辑器">
<li>
<section>工具返回会被包装成 JSON 文本块（参考内容定位 <code>jsonResult()</code> 在 <code>src/agents/tools/common.ts:230-239</code>）。</section>
</li>
<li>
<section>tool 返回会被标准化成 <code>content[] + details</code>（参考内容定位 <code>src/agents/pi-tool-definition-adapter.ts</code> 的 normalize）。</section>
</li>
<li>
<section>这些 toolResult 会被追加到 session messages，下一次模型调用会携带。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这条通道对排障非常友好：我们可以在 transcript 里看到「这次回答之前它到底召回了什么」。而且 toolResult 天然可控预算、可控格式，比让模型把记忆揉进自由文本稳得多。</p>
<h1 data-tool="mdnice编辑器"><span class="content">8. 写入时机</span></h1>
<h2 data-tool="mdnice编辑器"><span class="content">8.1 会话快照写入</span></h2>
<p data-tool="mdnice编辑器">人为触发的「切会话」</p>
<p data-tool="mdnice编辑器">当执行 <code>/new</code>、<code>/reset</code> 时，上一段会话尾部会被抽取成 <code>YYYY-MM-DD-&lt;slug&gt;.md</code>。这是「防断片」写入，价值是保住最近上下文。</p>
<p data-tool="mdnice编辑器">坑：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>抽取的 N 条对话里可能包含敏感信息。它会落在 <code>memory/</code>，并进入索引。</section>
</li>
<li>
<section>如果你把 workspace 目录同步到团队共享盘或提交到 repo，泄露面会扩大。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">我们的做法：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>明确区分「个人 workspace」和「团队 workspace」。个人的 <code>memory/</code> 默认不进 repo。</section>
</li>
<li>
<section>开启 citations 时，产品侧要想清楚是否允许暴露路径与行号。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content">8.2 短期记忆写入</span></h2>
<p data-tool="mdnice编辑器">「需要我们调教，告诉她哪些重要」。</p>
<p data-tool="mdnice编辑器">短期记忆要走「稀疏高密度」路线：条目少，但每条都能在未来的某个问题上直接复用。写入策略要围绕「将来会搜什么」来定，不要围绕「当下发生了什么」来记流水账。</p>
<h2 data-tool="mdnice编辑器"><span class="content">8.3 长期记忆更新</span></h2>
<p data-tool="mdnice编辑器">心跳 / AGENTS.md / cron</p>
<p data-tool="mdnice编辑器">三种更新机制：心跳、核心流程、cron。</p>
<p data-tool="mdnice编辑器">读最近几天短期记忆 → 选值得长期记住的 → 提炼写入 <code>MEMORY.md</code>。</p>
<p data-tool="mdnice编辑器">观点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section><strong>cron 最稳</strong>，可观测、可控、可回滚。</section>
</li>
<li>
<section>心跳更新很容易在负载高时抖动，或者在你最不想更新的时候更新。</section>
</li>
<li>
<section>把它塞进核心流程（AGENTS.md）要谨慎，一旦每次任务都触发提炼，会把延迟拉上去。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">一般做法：</p>
<ul data-tool="mdnice编辑器">
<li>
<section>工作日每天固定一次 consolidation（cron）。</section>
</li>
<li>
<section>遇到重大决策或事故复盘，当天手动提炼写入 <code>MEMORY.md</code>，不等自动化。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content">9. 怎么「调教」</span></h1>
<p data-tool="mdnice编辑器">把记忆当成协议，不当成日记</p>
<p data-tool="mdnice编辑器">「告诉她哪些重要」。把「重要」拆成几类，每类有明确写入规则，避免模型自由发挥。</p>
<p data-tool="mdnice编辑器">一般的规则：</p>
<ol data-tool="mdnice编辑器">
<li>
<section><strong>稳定偏好</strong>：例如输出格式偏好、技术栈偏好、代码风格偏好。</section>
</li>
<li>
<section><strong>组织事实</strong>：团队结构、系统边界、核心服务依赖、环境约束。</section>
</li>
<li>
<section><strong>关键决策</strong>：ADR 级别的决策，包含时间点与理由。</section>
</li>
<li>
<section><strong>长期目标与在途事项</strong>：能跨周追踪的，不写「今天要做的」。</section>
</li>
<li>
<section><strong>事故教训</strong>：明确到「哪个坑踩过」「如何避免」。</section>
</li>
</ol>
<p data-tool="mdnice编辑器">短期记忆（<code>memory/YYYY-MM-DD.md</code>）会允许更多过程性信息，但要满足一个条件：<strong>未来能被搜索问题命中</strong>。比如你写「今天讨论了 A」，基本没用；你写「决定 A 的原因是 B，后续若出现 C 用 D 回滚」，会有用一些。</p>
<p data-tool="mdnice编辑器">如果希望 <code>memory_search</code> 在关键时刻召回到正确内容，就得用「未来的查询语句」来写记忆。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2026/03/openclaw-memory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何构建行业 Agent 的 RAG</title>
		<link>https://www.phppan.com/2025/12/how-to-build-an-industry-agent-rag/</link>
		<comments>https://www.phppan.com/2025/12/how-to-build-an-industry-agent-rag/#comments</comments>
		<pubDate>Sun, 21 Dec 2025 00:28:37 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[RAG]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2447</guid>
		<description><![CDATA[行业 Agent 和我们常用的「通用聊天 Agent」不是一类东西。 行业 Agent 是要能解决问题的，而查 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #191b1f;" data-first-child="" data-pid="_Fvj3qAo">行业 Agent 和我们常用的「通用聊天 Agent」不是一类东西。</p>
<p style="color: #191b1f;" data-pid="WXgeV_Jl">行业 Agent 是要能解决问题的，而查资料只是其中一步，后面还要做判断、走流程、调用系统、校验结果、留痕、可回放。</p>
<p style="color: #191b1f;" data-pid="sRRsjjmo">RAG 在这里的角色也变了：它不只是给模型喂上下文，而是给 Agent 提供可执行任务所需的依据、约束、参数和证据链。</p>
<p style="color: #191b1f;" data-pid="7G-ev8ms">今天我们聊一下行业 Agent 构建过程中的 RAG 怎么写，从目标、数据，检索以及使用 RAG 等等方面。</p>
<h2 style="font-weight: 500; color: #191b1f;">1. 行业 Agent 的 RAG 要服务什么能力</h2>
<p style="color: #191b1f;" data-pid="uXdOzN2n">行业 Agent 常见的工作方式是「多步闭环」：</p>
<ol style="color: #191b1f;">
<li data-pid="p-6h7mCf">识别问题类型与业务对象（客户、设备、合同、工单、订单、项目、账号等）</li>
<li data-pid="Au8xLHqd">查依据（制度、手册、知识库、历史工单、标准、接口文档）</li>
<li data-pid="N-o99Kui">做动作（查系统、下发指令、开通配置、生成工单、发邮件、写报告、提审批）</li>
<li data-pid="F4mSbwYG">校验与回写（确认变更成功、回填字段、留痕、把引用/证据挂到工单）</li>
<li data-pid="Doi6TI-9">解释（给用户说明依据、影响范围、回滚方案、下一步建议）</li>
</ol>
<p style="color: #191b1f;" data-pid="_N4MGLsp">所以行业 Agent 的 RAG 不只是「问答检索」，而是至少要覆盖这些信息类型：</p>
<ul style="color: #191b1f;">
<li data-pid="vUZWWyko">规则依据：制度、条款、SOP、合规模板、变更规范</li>
<li data-pid="4Q_phUiD">操作依据：系统使用手册、接口文档、参数含义、错误码处理</li>
<li data-pid="tXaTETlZ">对象事实：来自业务系统的实时/准实时数据（用户信息、资源状态、库存、账单、设备状态）</li>
<li data-pid="V1-Otm3r">历史经验：工单处理记录、故障复盘、已知问题（KEDB）</li>
<li data-pid="uHSEdNWt">风险边界：禁用操作清单、权限范围、需要人工复核的条件</li>
</ul>
<p style="color: #191b1f;" data-pid="zea6g6Y_">如果我们只做「文档向量库 + 生成」，Agent 走到第 3 步就会卡：它不知道该调用哪个系统、需要哪些字段、什么情况下要停下来让人确认，也不知道怎么证明自己做对了。</p>
<h2 style="font-weight: 500; color: #191b1f;">2. 指标</h2>
<p style="color: #191b1f;" data-pid="O82pWPQe">行业 Agent 场景里，最好用三类指标描述：</p>
<h2 style="font-weight: 500; color: #191b1f;">2.1 任务完成类指标</h2>
<ul style="color: #191b1f;">
<li data-pid="vDw37fwW">任务成功率（最终动作成功并通过校验）</li>
<li data-pid="BIM5gkqU">平均完成时长（端到端）</li>
<li data-pid="DoybAuEk">人工介入率（需要人确认/补充信息/兜底）</li>
<li data-pid="uWRt7jzd">回滚率（执行后需要撤销/修正）</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">2.2 风险类指标（红线不能过）</h2>
<ul style="color: #191b1f;">
<li data-pid="UkS0Yulz">越权率（检索/执行是否越权，目标是 0）</li>
<li data-pid="23WkdZU3">误执行率（不该执行却执行）</li>
<li data-pid="HaUEWinC">误答导致的错误操作（把“编出来的依据”当成执行依据）</li>
<li data-pid="PWRr1Ece">引用不可追溯率（给不出来源或来源不支持结论）</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">2.3 知识与检索类指标（用于驱动迭代）</h2>
<ul style="color: #191b1f;">
<li data-pid="UimFGQqD">依据命中率（标准依据是否出现在 topK）</li>
<li data-pid="4IFYRJoU">冲突处理正确率（新旧版本/多来源冲突时是否选对）</li>
<li data-pid="VC8kim8H">时效正确率（是否引用过期/废止内容）</li>
<li data-pid="GHbz90Yw">覆盖率（高频问题是否覆盖）</li>
</ul>
<p style="color: #191b1f;" data-pid="0sKYnSs8">行业 Agent 的 RAG 设计，最终要对这些指标负责。否则我们会陷入「答得像那么回事，但不敢让它动系统」的状态。</p>
<h2 style="font-weight: 500; color: #191b1f;">3. 数据层</h2>
<p style="color: #191b1f;" data-pid="5DoZhpBN">行业 Agent 的 RAG，数据比模型更重要。</p>
<h2 style="font-weight: 500; color: #191b1f;">3.1 三类数据</h2>
<ol style="color: #191b1f;">
<li data-pid="tHE3e0ob">静态权威知识：制度、规范、手册、标准、产品文档<br />
目标：可追溯、版本可控、可引用</li>
<li data-pid="v5mHpX-R">动态业务事实：来自业务系统的数据（CRM、工单、CMDB、监控、计费、IAM 等）<br />
目标：可校验、可审计、最好可回放（至少保留查询快照）</li>
<li data-pid="uH14OYOe">过程与经验：历史工单、故障复盘、处理记录、FAQ 演进<br />
目标：可过滤（质量参差）、可分级（权威/经验/猜测）</li>
</ol>
<p style="color: #191b1f;" data-pid="ooh0QN8F">很多项目失败是因为把第 3 类当第 1 类用，把「经验」当「制度」。Agent 一旦据此去执行动作，风险会放大。</p>
<h2 style="font-weight: 500; color: #191b1f;">3.2 每个知识片段必须带的元数据</h2>
<p style="color: #191b1f;" data-pid="BS_nzuoS">行业 Agent 需要的不只是「能搜到」，还要「能用来做动作」。建议每个 chunk 至少包含：</p>
<ul style="color: #191b1f;">
<li data-pid="UrcHCaaQ"><code>doc_id / chunk_id</code></li>
<li data-pid="IMDzWu8U"><code>source</code>（系统/库）</li>
<li data-pid="5I0KQ3ng"><code>source_url</code>（可点击或可定位）</li>
<li data-pid="1T95YkZb"><code>title_path</code>（标题链）</li>
<li data-pid="ob9rQ8Ro"><code>doc_type</code>（制度/手册/接口文档/复盘/工单等）</li>
<li data-pid="HrFHrpVF"><code>version</code>、<code>status</code>（草稿/已发布/已废止）</li>
<li data-pid="zVhGgMLu"><code>effective_from / effective_to</code>（能给就给）</li>
<li data-pid="wQ1KNNdl"><code>owner</code>（维护人/团队）</li>
<li data-pid="-MObHdR0"><code>updated_at</code></li>
<li data-pid="WsMjhehk">适用范围标签：产品线/地区/客户/机型/环境（生产/测试）</li>
<li data-pid="-8_Qkt1y">权限标签：RBAC/ABAC 所需字段</li>
<li data-pid="FyU8NJiA">可执行性标签（建议加）：
<ul>
<li data-pid="U1X2WHsV">是否可作为执行依据（例如制度/已发布 SOP 才能）</li>
<li data-pid="c4cWmBhG">是否需要人工复核（高风险操作）</li>
<li data-pid="YqzlLX0m">是否仅供参考（复盘/经验）</li>
</ul>
</li>
</ul>
<p style="color: #191b1f;" data-pid="EcfVjtBv">这些标签对 Agent 比较关键：它能决定「能不能做、要不要停、怎么解释」。</p>
<h2 style="font-weight: 500; color: #191b1f;">3.3 文档解析与切分</h2>
<p style="color: #191b1f;" data-pid="erJ-fH7-">行业 Agent 的 RAG 的切分策略，优先级一般是：</p>
<ol style="color: #191b1f;">
<li data-pid="Nxb14Xvu">按结构切：章/节/条款/接口字段说明/错误码条目</li>
<li data-pid="9r74YqhK">把“前置条件/限制/例外”跟规则放一起</li>
<li data-pid="yCw_OU03">表格与字段定义要保表头（字段含义脱离表头就没法用）</li>
<li data-pid="JUNyz8m4">把可执行步骤单独成块（SOP、Runbook、变更步骤）</li>
</ol>
<p style="color: #191b1f;" data-pid="h_cZ_tiE">注意：不要把「定义」「适用范围」「例外条款」切碎。Agent 执行动作时，最需要的就是边界条件和限制。</p>
<h2 style="font-weight: 500; color: #191b1f;">4. 索引与检索</h2>
<p style="color: #191b1f;" data-pid="NSbRT0Bg">行业 Agent 和常规的 Agent 不同，其更依赖于「过滤 + 排序 + 证据链」</p>
<h2 style="font-weight: 500; color: #191b1f;">4.1 使用混合检索</h2>
<p style="color: #191b1f;" data-pid="_KRV8TKw">行业 Agent 的查询里会出现大量「硬信息」：</p>
<ul style="color: #191b1f;">
<li data-pid="vACEVG4-">条款号、标准号、型号、错误码、参数名、接口路径、工单号、配置项名</li>
</ul>
<p style="color: #191b1f;" data-pid="p4STkNYM">纯向量在这些场景不稳。工程上更常用的是：</p>
<ul style="color: #191b1f;">
<li data-pid="AkBe0Uxa">关键词/BM25：抓编号、术语、字段名、错误码</li>
<li data-pid="tLpgvb1T">向量召回：抓语义相近、同义表达</li>
<li data-pid="MZFmCh3q">融合 + 重排：把候选集排序成「最能支持动作/结论」的那几段</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">4.2 检索要先过滤，再找相似</h2>
<p style="color: #191b1f;" data-pid="s5g_6N7O">行业 Agent 的过滤通常是强约束，如下：</p>
<ul style="color: #191b1f;">
<li data-pid="hVUIt9Yj">权限过滤（用户/角色/租户/数据域）</li>
<li data-pid="EJN2N9Ra">状态过滤（废止、草稿默认不进）</li>
<li data-pid="5KW723mG">生效时间过滤（尤其制度、计费、合规）</li>
<li data-pid="2z1o3WUL">适用范围过滤（产品/地区/环境）</li>
<li data-pid="bZfdhNZn">数据域隔离（内部/客户侧/合作方）</li>
</ul>
<p style="color: #191b1f;" data-pid="Pf6CepOO">如果我们把这些留到生成阶段「让模型自己注意」，效果不可控，风险也不可控。</p>
<h2 style="font-weight: 500; color: #191b1f;">4.3 Agent 专用检索</h2>
<p style="color: #191b1f;" data-pid="fAn6MGCV">不止检索答案，还要检索工具与参数</p>
<p style="color: #191b1f;" data-pid="ZUDTVbUT">行业 Agent 经常需要两类额外检索：</p>
<ol style="color: #191b1f;">
<li data-pid="14T14GaS">工具检索（Tool RAG）<br />
从「工具说明库/接口文档/SOP」里检索：该用哪个工具、需要哪些参数、有哪些限制、失败怎么处理。</li>
<li data-pid="wGAjt21i">参数与字段检索（Schema RAG）<br />
从「数据字典/字段说明/枚举值」里检索：字段含义、可选值、校验规则、示例格式。</li>
</ol>
<p style="color: #191b1f;" data-pid="KHbJc-N5">这两类检索的结果不一定直接展示给用户，但会决定 Agent 能不能把动作做对。</p>
<h2 style="font-weight: 500; color: #191b1f;">5. 固化 Agent 使用 RAG 的逻辑</h2>
<p style="color: #191b1f;" data-pid="NwwBvADg">行业 Agent 的 RAG 关键是要「把 RAG 插进决策点」</p>
<p style="color: #191b1f;" data-pid="utLREmJs">行业 Agent 常见的内部循环大致是：</p>
<ul style="color: #191b1f;">
<li data-pid="5QP9RazX">Plan（决定下一步做什么）</li>
<li data-pid="HuVTSCsA">Act（调用工具/检索/执行）</li>
<li data-pid="jCCN_5WI">Observe（拿到结果）</li>
<li data-pid="bFfS8rOI">Decide（是否继续、是否需要人确认、是否结束）</li>
<li data-pid="yQis0Ph_">Explain（对外输出）</li>
</ul>
<p style="color: #191b1f;" data-pid="geSOjf6D">RAG 的插入点建议固定成三处：</p>
<h2 style="font-weight: 500; color: #191b1f;">5.1 决策前</h2>
<p style="color: #191b1f;" data-pid="j4rsmM_L">用 RAG 找「规则边界」</p>
<p style="color: #191b1f;" data-pid="TrpGaLJ2">在 Agent 做出关键决策前，先检索：</p>
<ul style="color: #191b1f;">
<li data-pid="jKX832Od">是否允许执行（权限、合规、风险等级）</li>
<li data-pid="tvEGzrOh">前置条件是什么（必须具备哪些信息、哪些系统状态）</li>
<li data-pid="lO28-IyB">需要的审批/确认是什么（是否必须人工确认）</li>
</ul>
<p style="color: #191b1f;" data-pid="YKn704LY">这一步的输出的是「约束」，不是「答案」。它会影响下一步是继续、暂停还是转人工。</p>
<h2 style="font-weight: 500; color: #191b1f;">5.2 执行前</h2>
<p style="color: #191b1f;" data-pid="JEd3Df9z">用 RAG 找「操作步骤与参数」</p>
<p style="color: #191b1f;" data-pid="bV2olvUo">执行某个动作前，检索：</p>
<ul style="color: #191b1f;">
<li data-pid="l4Qrf07r">SOP / Runbook / 接口文档</li>
<li data-pid="lSUYod3J">必填参数、参数来源</li>
<li data-pid="R967qsEd">校验方式（执行后如何确认成功）</li>
<li data-pid="iODcwcjM">回滚方式（失败/异常如何撤销）</li>
</ul>
<p style="color: #191b1f;" data-pid="A4ixm2P9">这一步的输出是「可执行步骤」，不是「解释性段落」。</p>
<h2 style="font-weight: 500; color: #191b1f;">5.3 执行后</h2>
<p style="color: #191b1f;" data-pid="K1dShMjs">用 RAG 做「结果判定与错误处理」</p>
<p style="color: #191b1f;" data-pid="3hCIsVEm">拿到工具返回值后，检索：</p>
<ul style="color: #191b1f;">
<li data-pid="TGhuHlpO">错误码含义与处理建议</li>
<li data-pid="VwLnmXsp">常见失败原因</li>
<li data-pid="wIZQfkwm">是否需要升级/转人工</li>
<li data-pid="5q63MWOx">是否需要二次校验（比如跨系统一致性）</li>
</ul>
<p style="color: #191b1f;" data-pid="QQ-5_La6">这一步的输出是「下一步动作建议 + 证据」。</p>
<h2 style="font-weight: 500; color: #191b1f;">6. 生成与输出</h2>
<p style="color: #191b1f;" data-pid="C2vOXebE">行业 Agent 的输出要分层，不要把所有东西都写给用户</p>
<p style="color: #191b1f;" data-pid="Q1aGGdiT">行业 Agent 的输出建议拆成三层，分别服务不同目标：</p>
<ol style="color: #191b1f;">
<li data-pid="e2okJ3Ur">用户层：结论/进展、需要用户补充什么、下一步怎么走</li>
<li data-pid="S-Dc5Ozd">证据层：引用依据（链接、页码、版本、生效日期）</li>
<li data-pid="ePI1OPwy">执行层（留痕层）：本次调用了什么工具、参数摘要、返回结果摘要、校验结果、回滚点</li>
</ol>
<p style="color: #191b1f;" data-pid="GOh0WCCx">用户不一定要看到执行层细节，但系统必须存储这些内容。只有出了问题能回放，才敢放权。</p>
<p style="color: #191b1f;" data-pid="1N9Qf3GW">同时，行业 Agent 的生成要有硬规则：</p>
<ul style="color: #191b1f;">
<li data-pid="k6x4YRop">没有命中权威依据：不输出肯定结论</li>
<li data-pid="CpZgMUEE">有冲突：必须把冲突来源、版本、生效时间写清楚</li>
<li data-pid="B0ulMWqI">涉及高风险动作：必须停下来请求确认（并把依据与影响范围给出来）</li>
<li data-pid="1t_nnq5B">引用必须来自检索上下文：不允许来虚的，「凭印象补一句」</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">7. 权限、审计、隔离</h2>
<p style="color: #191b1f;" data-pid="_qucgNvg">**行业 Agent 的 RAG 必须「检索前隔离」。</p>
<p style="color: #191b1f;" data-pid="IknS6jPO">行业 Agent 一旦能调用系统，风险比问答高一个量级。权限要分两层：</p>
<h2 style="font-weight: 500; color: #191b1f;">7.1 知识权限</h2>
<p style="color: #191b1f;" data-pid="fddZRlzb">能不能看的问题</p>
<ul style="color: #191b1f;">
<li data-pid="-pqsTCpx">文档/知识片段按 ABAC/RBAC 做过滤</li>
<li data-pid="zKYBTslW">按租户隔离（多客户必做）</li>
<li data-pid="ccEPz6QX">按数据域隔离（内部策略、客户信息、合作方信息）</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">7.2 行为权限</h2>
<p style="color: #191b1f;" data-pid="DrtoS4ke">能不能做的问题</p>
<ul style="color: #191b1f;">
<li data-pid="yf_bGSvS">工具级权限：这个角色能调用哪些工具</li>
<li data-pid="Mg0F7lJP">动作级权限：同一工具下哪些操作允许（例如只读查询 vs 修改/下发）</li>
<li data-pid="e23NNoWJ">参数级权限：同一动作下哪些资源范围允许（例如仅能操作自己负责的项目/客户）</li>
</ul>
<p style="color: #191b1f;" data-pid="W9LkAw1r">很多团队只做了「知识权限」，没做「行为权限」。</p>
<p style="color: #191b1f;" data-pid="LYVNsc-P">这会导致不放心，即使 Agent 能查到 SOP，也能学会「怎么做」，但你又不敢让它真的做。</p>
<h2 style="font-weight: 500; color: #191b1f;">7.3 审计要能回答四个问题</h2>
<ul style="color: #191b1f;">
<li data-pid="2w1BwWh8">为什么这么做（依据是什么）</li>
<li data-pid="lzGpWZeB">做了什么（调用了哪些工具、关键参数是什么）</li>
<li data-pid="2JYfo8uy">得到了什么（返回结果与校验结果）</li>
<li data-pid="V3fDIMvK">谁批准的（如果需要人工确认）</li>
</ul>
<p style="color: #191b1f;" data-pid="MET_gAHK">没有这四个问题的答案，行业 Agent 很难通过安全审查，也很难在出事后定位责任与修复点。</p>
<h2 style="font-weight: 500; color: #191b1f;">8. 灰度上线策略</h2>
<p style="color: #191b1f;" data-pid="vY9VAGHp">先控制风险，再谈覆盖率</p>
<p style="color: #191b1f;" data-pid="wKd4eAcH">行业 Agent 的上线节奏建议按权限逐步放开：</p>
<ol style="color: #191b1f;">
<li data-pid="A2LwivTU">只读 Agent：只检索、只解释、只给建议，不执行任何写操作</li>
<li data-pid="pq15yn4h">半自动 Agent：可以生成“执行计划/工单草稿/变更单草稿”，必须人工确认后执行</li>
<li data-pid="RdecVrZG">受限自动 Agent：只允许低风险、可回滚、可校验的动作自动执行（例如查询、对账、生成报表、创建工单、补全字段）</li>
<li data-pid="Odv0fyay">高风险动作：默认保留人工确认，除非你能做到严格的权限、校验、回滚、审计，并且有明确的责任边界</li>
</ol>
<p style="color: #191b1f;" data-pid="P5l39kjs">上线必须准备三套兜底：</p>
<ul style="color: #191b1f;">
<li data-pid="zU2V0L-7">超时与降级：检索失败/重排失败/模型失败时怎么退化</li>
<li data-pid="DmCAPAhB">失败回滚：执行失败怎么撤销，撤销失败怎么升级</li>
<li data-pid="xG48Jluj">人工接管：在关键节点能一键转人工，并把证据与执行轨迹带过去</li>
</ul>
<h2 style="font-weight: 500; color: #191b1f;">9. 常见坑</h2>
<ol style="color: #191b1f;">
<li data-pid="X26krG5-">把「经验工单」当「标准答案」：Agent 会把偶发处理当成通用规则。必须分级与降权。</li>
<li data-pid="jTOjzyDW">只做知识库，不做数据字典与工具库：Agent 会不知道参数怎么填、字段是什么意思、错误码怎么解。</li>
<li data-pid="dUkG-A76">只做检索，不做执行前校验与执行后校验：敢执行的前提是可校验、可回滚。</li>
<li data-pid="cexlm6HA">权限只管文档，不管工具：最容易在这里翻车。</li>
<li data-pid="E3WwsGTa">没有回放评测：你不知道一次小改动会不会让 Agent 在某个分支上开始乱走。</li>
<li data-pid="gzD5ZMai">把「多轮对话」当「任务编排」：行业 Agent 的关键是状态机与决策点，不是聊得多。</li>
</ol>
<p style="color: #191b1f;" data-pid="m6vPrXqs">最后，行业 Agent 的 RAG 如果要构建，不仅仅是算法的事情，需要更多的业务专家，业务 Owner 来直接参与，他们才是最懂行业的人。需要他们来定义「什么算答对/答错」、哪些文档权威、版本如何取舍、哪些内容不能答。</p>
<p style="color: #191b1f;" data-pid="iKsLaI1L">以上。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/12/how-to-build-an-industry-agent-rag/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从 Claude Code到 Gemini CLI，AI Agent 的上下文管理策略</title>
		<link>https://www.phppan.com/2025/08/claude-code-gemini-cli-ai-agent-manus-openmanus-context/</link>
		<comments>https://www.phppan.com/2025/08/claude-code-gemini-cli-ai-agent-manus-openmanus-context/#comments</comments>
		<pubDate>Sun, 31 Aug 2025 03:04:30 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[context]]></category>
		<category><![CDATA[上下文管理]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2412</guid>
		<description><![CDATA[对于一个与大型语言模型（LLM）打过交道的开发者来说，上下文管理都是一个绕不开的核心问题。它不仅决定了 AI  [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>
<p>对于一个与大型语言模型（LLM）打过交道的开发者来说，上下文管理都是一个绕不开的核心问题。<strong>它不仅决定了 AI 的智能程度，也直接关系到系统的性能和成本。</strong></p>
<p>上周研究了各家 Agent 系统的实现，各家的上下文管理策略都不相同。最简单最傻的策略是一个不断累加对话历史，这种策略很快就会遇到 Token 限制和 API 的成本问题。</p>
<p>如果你是一个技术负责人，或者正在开发 AI Agent 相关的产品，需要在性能和成本之间找到平衡点，这篇文章应该对你有一些帮助。</p>
<p>今天所聊的内容是基于对 Claude Code、Manus、Gemini CLI，OpenManus 等多个项目的分析，以及自己在实践中的一些思考。</p>
<p><strong>为什么要做上下文管理？</strong></p>
<p>最新的 LLM 现在提供 128K Token 或更多的上下文窗口。听起来还挺多的，但在真实世界的 Agent 场景中，这通常远远不够。</p>
<p>尤其是当 Agent 与网页或PDF等非结构化数据交互时，Token 数需求会爆炸。</p>
<p>并且，随着 Token 数的增加，模型性能会在超过一定长度后明显下降，这就像让一个人同时记住一本书的所有细节，理论上可能，实际上很难做好。</p>
<p>就算我们的大模型有更多的窗口上下文支持，成本也是一个需要考虑的问题，就算有前缀缓存这样的优化，但传输和预填充每个 Token 都是要付费的。</p>
<p>为了解决这些问题，许多团队选择了压缩策略。但过度激进的压缩不可避免地导致信息丢失。</p>
<p>这个问题的本质在于 <strong>Agent 必须根据所有先前状态预测下一个动作</strong>——而我们无法可靠地预测哪个观察结果可能在十步之后变得至关重要。从逻辑角度看，任何不可逆的压缩都带有风险。</p>
<p>接下来我们看一看各项目的上下文管理策略，看看从中能否给到各位看官一些启发。</p>
<h2 data-id="heading-0">OpenManus 的上下文管理策略</h2>
<p>OpenManus 采用了一个相对简单直接的上下文管理方案，主要特点是：</p>
<ol>
<li>轻量级消息列表机制</li>
</ol>
<ul>
<li>使用固定长度（默认100条）的消息列表作为内存存储</li>
<li>采用简单的 FIFO（先进先出）策略，超出限制时截断最早的消息</li>
<li>没有智能的上下文压缩或摘要机制</li>
</ul>
<ol start="2">
<li>Token 限制处理</li>
</ol>
<ul>
<li>实施硬性 token 检查，超限直接抛出异常终止</li>
<li>缺乏优雅的降级策略或自适应窗口裁剪</li>
<li>在长对话或工具密集场景中容易触碰上限</li>
</ul>
<p>虽然上下文管理比较简单，但是 OpenManus 为不同使用场景提供了定制化的上下文处理，如浏览器场景会动态注入浏览器状态，截图保存等</p>
<p>总的来说，这是一个原型实现，并不适合作为生产级环境使用，如果要上到生产环境需要自行做精细化的处理和架构。</p>
<h2 data-id="heading-1">Manus 的上下文管理策略</h2>
<p>Manus 没有开源，但是其官方有发一篇文章出来。</p>
<p>Manus 采用了一种创新的方法：<strong>将文件系统作为终极上下文存储</strong>，而不是依赖传统的内存中上下文管理。</p>
<p>文件系统作为存储有如下的核心特性：</p>
<ul>
<li><strong>无限容量</strong>：文件系统大小不受限制</li>
<li><strong>天然持久化</strong>：数据自动保存，不会丢失</li>
<li><strong>直接操作</strong>：智能体可以主动读写文件</li>
<li><strong>结构化记忆</strong>：不仅是存储，更是结构化的外部记忆系统</li>
</ul>
<p>相对于传统的将完整的观察结果保存在上下文中，容易超限，Manus 实现了<strong>可恢复的信息压缩</strong>：</p>
<ul>
<li>观察结果指向外部文件（Document X, File Y）</li>
<li>上下文中只保留引用，不保存完整内容</li>
<li>需要时可以从文件系统恢复完整信息</li>
</ul>
<p><strong>具体实现：</strong></p>
<ul>
<li>网页内容可从上下文移除，只保留 URL</li>
<li>文档内容可省略，只保留文件路径</li>
<li>实现上下文压缩的同时不会永久丢失信息</li>
</ul>
<p>Manus 团队认为，如果状态空间模型能够掌握基于文件的记忆管理：</p>
<ul>
<li>将长期状态外部化而非保存在上下文中</li>
<li>SSM 的速度和效率优势可能开启新型智能体</li>
<li>基于 SSM 的智能体可能成为神经图灵机的真正继任者</li>
</ul>
<p>与 OpenManus 的简单消息列表管理，Manus 的方案更加成熟：</p>
<ul>
<li><strong>OpenManus</strong>：固定长度消息列表，硬性截断，缺乏智能管理</li>
<li><strong>Manus</strong>：文件系统作为无限外部记忆，可恢复压缩，主动记忆管理</li>
</ul>
<h2 data-id="heading-2">Claude Code 的上下文管理</h2>
<p>Claude Code 没有开源代码，但是国外有大神反编译其源码（虽然大神自己说:这并非真正意义上的反编译或逆向工程尝试，而更像是对 Claude 团队杰出工作的致敬。）</p>
<p>地址：<a title="https://southbridge-research.notion.site/claude-code-an-agentic-cleanroom-analysis" href="https://link.juejin.cn?target=https%3A%2F%2Fsouthbridge-research.notion.site%2Fclaude-code-an-agentic-cleanroom-analysis" target="_blank">southbridge-research.notion.site/claude-code…</a></p>
<p>通过反编译内容的分析，可以大概了解一些其策略和比较巧妙的点：</p>
<h3 data-id="heading-3"><strong>TodoWrite 工具</strong></h3>
<p>Claude Code 引入 TodoWrite 工具，支持模型主动维护自己的 To-Do 列表，替代传统的多 Agent 分工策略。</p>
<p><strong>其优势：</strong></p>
<ul>
<li>专注：Prompt 中反复提醒模型参考 ToDo，始终聚焦目标。</li>
<li>灵活：「交错思考」机制使得 ToDo 可动态增删。</li>
<li>透明：用户可实时查看计划与进度，提高信任度。</li>
</ul>
<h3 data-id="heading-4"><strong>Token 统计的反向遍历</strong></h3>
<p>Toke 统计从后往前查找这个细节相当精妙。大部分系统都是傻乎乎地从头遍历，但 Claude Code 意识到了一个关键事实：Token 使用情况的统计信息总是出现在最新的 assistant 回复里。这种&#8221;知道去哪找&#8221;的优化思路，把原本可能的 O(n) 操作优化到了 O(k)，在高频调用场景下，这种优化带来的性能提升是指数级的。</p>
<h3 data-id="heading-5"><strong>92% 阈值</strong></h3>
<p>留 8% 的缓冲区既保证了压缩过程有足够的时间完成，又避免了频繁触发压缩带来的性能开销。更重要的是，这个缓冲区给了系统一个&#8221;反悔&#8221;的机会——如果压缩质量不达标，还有空间执行降级策略。</p>
<h3 data-id="heading-6"><strong>8 段式结构化摘要</strong></h3>
<p>Claude Code 的 8 段式结构特别值得借鉴：</p>
<div class="code-block-extension-header">
<div class="code-block-extension-headerLeft">
<div class="code-block-extension-foldBtn"></div>
<p><span class="code-block-extension-lang">markdown</span></div>
<div class="code-block-extension-headerRight">
<div class="code-tips" data-v-4fdcfe21=""><span data-v-4fdcfe21="">体验AI代码助手</span></div>
<div class="render" data-v-159ebe90=""><span class="txt" data-v-159ebe90="">代码解读</span></div>
<div class="code-block-extension-copyCodeBtn">复制代码</div>
</div>
</div>
<pre><code class="hljs language-markdown code-block-extension-codeShowNum" lang="markdown">
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-bullet">1.</span> Primary Request and Intent - 主要请求和意图</span>

<span class="code-block-extension-codeLine" data-line-num="4"><span class="hljs-bullet">2.</span> Key Technical Concepts - 关键技术概念</span>

<span class="code-block-extension-codeLine" data-line-num="6"><span class="hljs-bullet">3.</span> Files and Code Sections - 文件和代码片段</span>

<span class="code-block-extension-codeLine" data-line-num="8"><span class="hljs-bullet">4.</span> Errors and Fixes - 错误和修复</span>

<span class="code-block-extension-codeLine" data-line-num="10"><span class="hljs-bullet">5.</span> Problem Solving - 问题解决过程</span>

<span class="code-block-extension-codeLine" data-line-num="12"><span class="hljs-bullet">6.</span> All User Messages - 所有用户消息</span>

<span class="code-block-extension-codeLine" data-line-num="14"><span class="hljs-bullet">7.</span> Pending Tasks - 待处理任务</span>

<span class="code-block-extension-codeLine" data-line-num="16"><span class="hljs-bullet">8.</span> Current Work - 当前工作状态</span>

</code></pre>
<h3 data-id="heading-7"><strong>优雅降级</strong></h3>
<p>当压缩失败时，系统不会死板地报错或者强行应用低质量的压缩结果，而是有一整套 Plan B、Plan C。从自适应重压缩，到混合模式保留，再到最后的保守截断——每一步都在努力保护用户体验。这种&#8221;永不放弃&#8221;的设计理念，让系统在各种极端情况下都能稳定运行。</p>
<h3 data-id="heading-8"><strong>向量化搜索</strong></h3>
<p>长期记忆层引入向量搜索，实际上是在为 AI 构建一个&#8221;联想记忆&#8221;系统。当用户提出新问题时，系统不仅能看到当前对话，还能&#8221;回忆&#8221;起过去处理过的类似问题。这种跨会话的知识迁移能力，让 Claude Code 从一个简单的对话工具进化成了一个真正的智能编程助手。</p>
<h2 data-id="heading-9">Gemini-cli 的上下文管理</h2>
<p>Gemini-cli 的上下文管理走了一条和 Claude Code 相似但更加轻量的路线。它的核心理念很简单：<strong>文件系统就是天然的数据库</strong>。</p>
<h3 data-id="heading-10"><strong>三层混合存储架构</strong></h3>
<p>与 Claude Code 类似，Gemini-cli 也采用了分层设计，但实现更加简洁：</p>
<p><strong>第一层：纯内存工作区</strong></p>
<ul>
<li>存储当前会话的聊天历史、工具调用状态、循环检测状态</li>
<li>零延迟访问，不涉及任何 I/O 操作</li>
<li>会话结束即清空，不留痕迹</li>
</ul>
<p><strong>第二层：智能压缩层</strong></p>
<ul>
<li>触发阈值：70%（比 Claude Code 的 92% 更保守）</li>
<li>保留策略：最新 30% 的对话历史</li>
<li>压缩产物：5 段式结构化摘要</li>
</ul>
<p><strong>第三层：文件系统持久化</strong></p>
<ul>
<li>全局记忆：<code>~/.gemini/GEMINI.md</code></li>
<li>项目记忆：向上递归查找直到项目根目录</li>
<li>子目录上下文：向下扫描并尊重忽略规则</li>
</ul>
<h3 data-id="heading-11"><strong>70/30</strong></h3>
<p>Gemini-cli 选择了 70% 作为压缩触发点，30% 作为保留比例。这个比例设计很有讲究：</p>
<p><strong>为什么是 70% 而不是 92%？</strong></p>
<ul>
<li>更早介入，避免紧急压缩导致的卡顿</li>
<li>给压缩过程留出充足的缓冲空间</li>
<li>适合轻量级应用场景，不追求极限性能</li>
</ul>
<p><strong>30% 保留的合理性</strong></p>
<ul>
<li>刚好覆盖最近 5-10 轮对话</li>
<li>足够维持上下文连续性</li>
<li>不会让用户感觉&#8221;突然失忆&#8221;</li>
</ul>
<h3 data-id="heading-12"><strong>5 段式压缩：够用就好</strong></h3>
<p>相比 Claude Code 的 8 段式结构，Gemini-cli 的压缩更简洁：</p>
<div class="code-block-extension-header">
<div class="code-block-extension-headerLeft">
<div class="code-block-extension-foldBtn"></div>
<p><span class="code-block-extension-lang">markdown</span></div>
<div class="code-block-extension-headerRight">
<div class="code-tips" data-v-4fdcfe21=""><span data-v-4fdcfe21="">体验AI代码助手</span></div>
<div class="render" data-v-159ebe90=""><span class="txt" data-v-159ebe90="">代码解读</span></div>
<div class="code-block-extension-copyCodeBtn">复制代码</div>
</div>
</div>
<pre><code class="hljs language-markdown code-block-extension-codeShowNum" lang="markdown">
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-bullet">1.</span> overall<span class="hljs-emphasis">_goal - 用户的主要目标</span></span>

<span class="code-block-extension-codeLine" data-line-num="4">2. key_knowledge - 重要技术知识和决策</span>

<span class="code-block-extension-codeLine" data-line-num="6"><span class="hljs-bullet">3.</span> file<span class="hljs-emphasis">_system_</span>state - 文件系统当前状态</span>

<span class="code-block-extension-codeLine" data-line-num="8"><span class="hljs-bullet">4.</span> recent<span class="hljs-emphasis">_actions - 最近执行的重要操作</span></span>

<span class="code-block-extension-codeLine" data-line-num="10">5. current_plan - 当前执行计划</span>

</code></pre>
<h3 data-id="heading-13"><strong>忽略规则</strong></h3>
<p>Gemini-cli 的 <code>.geminiignore</code> 机制是个亮点：</p>
<p><strong>独立但兼容</strong></p>
<ul>
<li>可以单独在非 git 仓库中生效</li>
<li>与 <code>.gitignore</code> 并行工作，互不干扰</li>
<li>每个工具都有独立的忽略开关</li>
</ul>
<p><strong>明确的约束</strong></p>
<ul>
<li>修改 <code>.geminiignore</code> 需要重启会话才生效</li>
<li>这不是 bug，而是 feature——避免运行时状态混乱</li>
</ul>
<p>Gemini-cli 的设计哲学可以总结为：<strong>不求最优，但求够用</strong>。</p>
<p>它没有追求理论上的完美压缩比，也没有搞复杂的向量检索，而是用最简单的方案解决了 80% 的问题。这种务实的态度在工程实践中往往更受欢迎——系统简单意味着 bug 少，维护容易，用户上手快。</p>
<p>特别是&#8221;文件系统就是数据库&#8221;这个理念，虽然听起来有点&#8221;土&#8221;，但在实际使用中却异常可靠。你不需要担心数据库挂了、连接断了、事务死锁了&#8230;文件就在那里，看得见摸得着，出了问题 <code>cat</code> 一下就知道怎么回事。</p>
<p>这种设计思路值得很多过度工程化的项目学习：有时候，简单就是最好的复杂。</p>
<h2 data-id="heading-14">小结</h2>
<p><strong>上下文是智能的边界，压缩是性能的艺术。</strong></p>
<p>在与大型语言模型打交道的过程中，上下文管理已成为决定智能上限与系统稳健性的关键。虽然现代 LLM 提供了百万级 Token 的窗口，但在实际 Agent 场景中，这远远不够，尤其当涉及非结构化数据（如网页、PDF）时，Token 使用会迅速膨胀。即使有前缀缓存等机制，成本与性能的双重压力仍然存在。因此，上下文压缩成了必选项——但压缩得太激进，又会导致信息丢失，损害 Agent 的决策能力。</p>
<p><strong>聪明的系统不是记住所有，而是记住该记住的。</strong></p>
<p>应对上下文限制的最佳方式不是简单保留或截断历史，而是构建一个具备“记忆力”的智能系统。Claude Code 以三层记忆架构为核心（短期、高速；中期、结构化压缩；长期、跨会话向量化搜索），同时引入 TodoWrite 工具，让模型自我管理计划任务。这使得 Agent 能专注目标、灵活调整、透明运行，形成类人思维般的任务记忆系统。关键机制如 Token 反向遍历、92% 阈值缓冲、8段式摘要结构与优雅降级策略，共同打造了一个稳健又高效的上下文生态。</p>
<p><strong>工程的智慧在于‘够用’，而非‘极致’。</strong></p>
<p>对比 Gemini-cli、OpenManus 与 Manus 的上下文策略，可以看出不同系统在工程实现上的取舍哲学。Gemini-cli 采用实用主义的轻量分层设计，70/30 压缩策略既简单又高效，让用户可控又无需担心性能瓶颈；Manus 则大胆将文件系统作为智能体的“外部大脑”，通过引用而非存储规避 Token 限制；而 OpenManus 则为最小可运行原型提供了基础模板。这些方案展现出一个共识：<strong>上下文不一定要复杂，关键在于是否服务于目标。</strong></p>
<p>以上。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/08/claude-code-gemini-cli-ai-agent-manus-openmanus-context/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multi-Agent 系统的主从架构</title>
		<link>https://www.phppan.com/2025/08/multi-agent-master-slave/</link>
		<comments>https://www.phppan.com/2025/08/multi-agent-master-slave/#comments</comments>
		<pubDate>Sun, 24 Aug 2025 00:20:17 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2410</guid>
		<description><![CDATA[最近一年，Multi-Agent 系统持续发热发烫，融资不断，火了起来。 从 AutoGPT 到 MetaGP [&#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编辑器">最近一年，Multi-Agent 系统持续发热发烫，融资不断，火了起来。</p>
<p data-tool="mdnice编辑器">从 AutoGPT 到 MetaGPT，从 CrewAI 到 LangGraph，各种多代理框架层出不穷。国内现象级的 Manus，以及当天由 MetaGPT 的同学开源的 OpenManus。OpenAI 的 Swarm、微软的 AutoGen，还有 Anthropic 的 Claude Code，每个大厂都在探索自己的 Multi-Agent 方案。GitHub 上相关项目的 Star 数动辄上万，社区讨论热度持续攀升。</p>
<p data-tool="mdnice编辑器">从这股热潮，我们可以看出 AI 应用的一个重要趋势：从单一模型调用走向多智能体协作。就像软件开发从单体应用演进到微服务架构，AI 系统也在探索如何通过多个专门化的 Agent 协同工作，完成更复杂的任务。</p>
<p data-tool="mdnice编辑器">当我们仔细观察这些 Multi-Agent 系统的架构时，会发现一个规律：</p>
<p data-tool="mdnice编辑器">MetaGPT 里有个产品经理角色，负责协调其他工程师角色；AutoGen 支持 Manager-Worker 模式；Claude Code 更是明确采用了<strong style="color: #0e88eb;">主循环引擎加子任务代理</strong>的设计。在 OpenAI Swarm 中，也能看到 Orchestrator Agent 的影子。</p>
<p data-tool="mdnice编辑器">这些系统都采用了某种形式的「主从」架构——有一个 Agent 负责全局协调，其他 Agent 提供专门化支持。</p>
<p data-tool="mdnice编辑器">为什么？巧合吗？</p>
<p data-tool="mdnice编辑器">今天我们就聊聊 Multi-Agent 系统的主从架构。从大模型的底层原理开始。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1 从大模型的原理说起</span></h1>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1.1 大模型的「注意力」机制</span></h2>
<p data-tool="mdnice编辑器">要理解为什么需要主从架构，得先理解大模型是怎么「思考」的。</p>
<p data-tool="mdnice编辑器">大模型的核心是 Transformer 架构，而 Transformer 的灵魂是注意力机制（Attention）。简单来说，模型在生成每个 token 时，会「注意」到输入中的所有相关信息（上下文窗口内），然后综合这些信息做出决策。</p>
<p data-tool="mdnice编辑器">这里有个关键点：<strong style="color: #0e88eb;">大模型的每次决策都是基于它能「看到&#8221;」的全部上下文</strong>。</p>
<p data-tool="mdnice编辑器">这就像你在做一道数学题。如果题目是&#8221;小明有 5 个苹果，给了小红 2 个&#8221;，你要回答&#8221;小明还剩几个&#8221;，你必须同时看到&#8221;5 个&#8221;和&#8221;给了 2 个&#8221;这两个信息。如果你只看到其中一部分，就无法得出正确答案。</p>
<p data-tool="mdnice编辑器">大模型也是如此。它的智能来源于对上下文的完整理解（其智能还来源于预训练时学到的知识，模式识别、知识迁移等能力）。一旦上下文缺失或者矛盾，模型的输出质量就会急剧下降。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1.2 多个模型协作的挑战</span></h2>
<p data-tool="mdnice编辑器">当多个大模型（Agent）需要协作时，如何保证它们都拥有必要的上下文？</p>
<p data-tool="mdnice编辑器">假设我们有三个 Agent 并行进行开发部署工作：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">Agent A：负责前端开发</section>
</li>
<li>
<section style="color: #010101;">Agent B：负责后端开发</section>
</li>
<li>
<section style="color: #010101;">Agent C：负责部署运维</section>
</li>
</ul>
<p data-tool="mdnice编辑器">理想情况下，它们应该像一个经验丰富的全栈工程师一样，时刻知道其他部分的设计决策。但实际上，每个 Agent 都是独立的大模型实例，它们各自维护着自己的上下文。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">这就产生了第一个问题：上下文分裂</strong>。</p>
<p data-tool="mdnice编辑器">Agent A 决定使用 React，Agent B 决定用 Python Flask，这本来没问题。但当 Agent A 后续生成代码时假设后端返回 GraphQL，而 Agent B 实际提供的是 REST API，最终代码就无法正常工作。</p>
<p data-tool="mdnice编辑器">并且大模型有个特性叫「自回归生成」——每个新的输出都依赖于之前的所有输出。这意味着一旦某个 Agent 做出了错误假设，这个错误会在后续生成中不断放大。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2 主从架构的设计哲学</span></h1>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2.1 为什么主从架构有效</span></h2>
<p data-tool="mdnice编辑器">主从架构的核心思想很简单：<strong style="color: #0e88eb;">一个指挥，多个执行。一个主 Agent 掌控全局，其他从 Agent 提供子领域专业化的支持</strong>。</p>
<p data-tool="mdnice编辑器">这个设计直接解决了上下文分裂的问题。主 Agent 始终维护着完整的任务上下文，它知道：</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编辑器">从 Agent 则像是主 Agent 的「外脑」——当主 Agent 需要专门知识时，会调用相应的从 Agent，但最终的决策和执行都由主 Agent 完成。</p>
<p data-tool="mdnice编辑器">在我们做具体实现的时候，每个不同的从 Agent 都有自己的角色和系统 Prompt。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2.2 Claude Code 的实践印证</span></h2>
<p data-tool="mdnice编辑器">Claude Code 的设计诠释了主从的理念。根据 Github 上逆向工程的分析，它的架构中：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">nO 主循环引擎</strong>（主 Agent）负责：</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编辑器"><strong style="color: #0e88eb;">I2A 子任务代理</strong>（从 Agent）负责：</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>
</ul>
<p data-tool="mdnice编辑器">Claude Code 刻意避免了子 Agent 的并行修改。当需要调查多个方向时，主 Agent 会有限范围内并行地咨询不同的子 Agent，确保每个决策都基于最新的完整上下文。</p>
<p data-tool="mdnice编辑器">这种设计看起来「低效」，但实际上避免了大量的错误和重做，总体效率反而更高。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2.3 生物学的启发</span></h2>
<p data-tool="mdnice编辑器">并且，主从架构在生物界也是有比较多例子的。</p>
<p data-tool="mdnice编辑器">人脑就是一个典型的例子。前额叶皮质充当「主 Agent」，负责高级决策和规划。而各个专门脑区（视觉皮层、听觉皮层、运动皮层等）就像「从 Agent」，处理特定类型的信息。</p>
<p data-tool="mdnice编辑器">所有的感官输入最终都会汇聚到前额叶皮质，由它整合信息并做出决策。即使是反射动作，大脑也会在事后「知晓」并可能调整后续行为。</p>
<p data-tool="mdnice编辑器">这种中心化的架构经过了数亿年的进化验证。</p>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3 主从架构的技术实现</span></h1>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.1 上下文管理</span></h2>
<p data-tool="mdnice编辑器">实现主从架构，最核心的是上下文管理。主 Agent 需要：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 维护完整但精简的上下文</strong></p>
<p data-tool="mdnice编辑器">并不是所有信息都同等重要。主 Agent 需要智能地压缩和总结历史信息。Claude Code 使用了一个策略：</p>
<p data-tool="mdnice编辑器">当 token 使用量达到阈值的 92% 时，触发压缩机制。关键决策被保留，而从 Agent 的中间探索过程被压缩或丢弃。这样既保持了决策的连贯性，又避免了上下文爆炸。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 构建结构化的决策记录</strong></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编辑器"><strong style="color: #0e88eb;">3. 动态调整上下文窗口</strong></p>
<p data-tool="mdnice编辑器">根据任务的复杂度和当前阶段，动态调整传递给从 Agent 的上下文量。初期探索阶段可以更开放，后期执行阶段需要更精确。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.2 Agent 的设计原则</span></h2>
<p data-tool="mdnice编辑器">Agent 不是越智能越好，而是要<strong style="color: #0e88eb;">专注</strong>和<strong style="color: #0e88eb;">可控</strong>：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 明确的能力边界</strong></p>
<p data-tool="mdnice编辑器">每个从 Agent 应该有清晰定义的能力范围。比如：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">代码审查 Agent：只负责发现潜在问题</section>
</li>
<li>
<section style="color: #010101;">重构 Agent：只负责改进代码结构</section>
</li>
<li>
<section style="color: #010101;">测试 Agent：只负责生成测试用例</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 标准化的输入输出</strong></p>
<p data-tool="mdnice编辑器">从 Agent 的接口要标准化，这样主 Agent 可以用统一的方式调用它们。输出格式也要规范，便于主 Agent 解析和整合。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 无状态设计</strong></p>
<p data-tool="mdnice编辑器">从 Agent 最好是无状态的，每次调用都是独立的。这样可以避免状态管理的复杂性，也便于并行化（当任务确实独立时）。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.3 协调机制的关键点</span></h2>
<p data-tool="mdnice编辑器">主 Agent 的协调能力决定了整个系统的表现：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 任务分解策略</strong></p>
<p data-tool="mdnice编辑器">并不是所有任务都要分解。主 Agent 需要学会判断：</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>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 冲突检测与解决</strong></p>
<p data-tool="mdnice编辑器">即使在主从架构下，从 Agent 的建议也可能相互矛盾。主 Agent 需要：</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>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 优雅降级</strong></p>
<p data-tool="mdnice编辑器">当从 Agent 失败或不可用时，主 Agent 应该能够：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">尝试从其它从 Agent 获取</section>
</li>
<li>
<section style="color: #010101;">降级到自己处理</section>
</li>
<li>
<section style="color: #010101;">调整任务策略</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4 主从架构的优势与局限</span></h1>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.1 主从架构的核心优势</span></h2>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 全局一致性保证</strong>主 Agent 作为唯一的决策中心，天然保证了架构决策的一致性。不只是技术栈的选择（比如统一使用 REST 还是 GraphQL），更重要的是接口约定、错误处理策略、命名规范等细节都能保持统一。这种一致性在复杂项目中价值巨大。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 清晰的决策链路</strong>每个决策都有明确的来源和依据。你可以在主 Agent 的对话历史中追踪每个架构决定是如何做出的，为什么选择某个方案。这种可追溯性在调试问题或向他人解释系统设计时非常有价值。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 优雅的错误处理</strong>主 Agent 掌握全局状态，当某个子任务失败时，它可以准确判断影响范围并制定恢复策略。比如，如果数据库设计出错，主 Agent 知道哪些 API 设计需要相应调整。而在去中心化系统中，这种级联影响很难追踪和修复。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">4. 上下文利用最大化</strong>看似串行的决策流程，实际上优化了整体效率：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">避免了重复劳动（多个 Agent 不会各自生成相似的代码）</section>
</li>
<li>
<section style="color: #010101;">减少了协调开销（不需要 Agent 间的大量通信）</section>
</li>
<li>
<section style="color: #010101;">上下文复用充分（主 Agent 的决策历史可以直接传递给从 Agent）</section>
</li>
</ul>
<p data-tool="mdnice编辑器">在 Claude Code 的实践中，这种设计让系统能在有限的 token 预算内完成相当复杂的编程任务。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.2 主从架构的局限性</span></h2>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 主 Agent 成为性能瓶颈</strong>所有决策都要经过主 Agent，当需要并行处理多个复杂子任务时，主 Agent 的串行决策会限制整体效率。就像一个项目经理同时管理太多团队，协调成本会急剧上升。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 对主 Agent 能力的高度依赖</strong>系统的智能上限取决于主 Agent 的能力。如果主 Agent 对某个领域理解不深，即使有专业的从 Agent，整体表现也会受限。这就像一个不懂技术的经理，很难充分发挥技术团队的潜力。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 缺乏真正的协作智能</strong>主从架构本质上是&#8221;分解-执行-组合&#8221;的模式，缺少 Agent 之间的平等协商和创造性互动。在需要头脑风暴或多视角探索的任务中，这种层级结构可能限制了解决方案的多样性。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">4. 任务分解的粒度难题</strong>主 Agent 需要准确判断任务分解的粒度。分得太细，协调成本高；分得太粗，从 Agent 可能无法胜任。而且随着任务复杂度增加，找到合适的分解方式越来越难。</p>
<h2 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.3 适用场景分析</span></h2>
<p data-tool="mdnice编辑器">主从架构特别适合：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 工程化任务</strong></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>
</ul>
<p data-tool="mdnice编辑器">这些任务需要高度的一致性和结构化。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 有明确目标的任务</strong></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>
</ul>
<p data-tool="mdnice编辑器">目标明确时，中心化协调更高效。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 需要可控性的场景</strong></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>
</ul>
<p data-tool="mdnice编辑器">这些领域不能接受不可预测的行为。</p>
<p data-tool="mdnice编辑器">不太适合：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 创意生成</strong></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>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 大规模并行处理</strong></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>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">3. 对等协作</strong></p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">多人游戏 AI</section>
</li>
<li>
<section style="color: #010101;">群体仿真</section>
</li>
<li>
<section style="color: #010101;">去中心化系统</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">5 小结</span></h1>
<p data-tool="mdnice编辑器">随着大模型能力的提升，主从架构也在演进：</p>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">更长的上下文窗口</strong>：GPT-4 已经支持 128K 的上下文，Claude 3 甚至到了 200K。这意味着主 Agent 可以维护更完整的历史，减少信息损失。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">更好的指令跟随</strong>：新一代模型在指令跟随上有显著提升，从 Agent 可以更准确地理解和执行主 Agent 的指令。</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">原生的工具调用</strong>：模型开始原生支持函数调用，这让主从 Agent 之间的接口更加标准化和可靠。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果你要实现一个主从架构的 Multi-Agent 系统，以下是一些建议：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">1. 设计清晰的 Agent 角色</strong>：不要让从 Agent 职责过于宽泛。每个从 Agent 应该像 Unix 工具一样——做一件事，并做好。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">2. 实现鲁棒的错误处理</strong></p>
<p data-tool="mdnice编辑器">从 Agent 失败是常态，不是异常。主 Agent 需要：</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编辑器"><strong style="color: #0e88eb;">3. 优化上下文传递</strong>：控制上下文的边界，并不是所有上下文都需要传递给给到 Agent。根据任务类型，精心设计上下文的内容和格式。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">4. 监控和可观测性</strong>：记录所有的决策点和 Agent 交互，后面调试和优化用得上。</p>
<p data-tool="mdnice编辑器">Multi-Agent 的主从架构，本质上是在解决一个古老的问题：<strong style="color: #0e88eb;">如何组织多个智能体高效地完成复杂任务</strong>。</p>
<p data-tool="mdnice编辑器">从生物进化到人类社会，从计算机架构到分布式系统，我们一次次地发现：在需要一致性和可控性的场景下，某种形式的<strong style="color: #0e88eb;">中心化协调</strong>是必要的。</p>
<p data-tool="mdnice编辑器">大模型的出现并没有改变这个规律。相反，由于大模型对上下文的强依赖，主从架构变得更加重要。</p>
<p data-tool="mdnice编辑器">随着大模型能力的提升和 Agent 技术的成熟，我们会看到更多创新的架构出现。但无论如何演进，那些基本的原则——<strong style="color: #0e88eb;">上下文一致性、决策可控性、错误可恢复性</strong>——都是我们在实践中需要谨慎考虑的。</p>
<p data-tool="mdnice编辑器">以上。</p>
<p>&nbsp;</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/08/multi-agent-master-slave/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>聊下 AI Agent 的 上下文工程（Context Engineering)</title>
		<link>https://www.phppan.com/2025/07/ai-agent-context-engineering/</link>
		<comments>https://www.phppan.com/2025/07/ai-agent-context-engineering/#comments</comments>
		<pubDate>Sun, 27 Jul 2025 00:41:03 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[Context Engineering]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2400</guid>
		<description><![CDATA[2025 年确实成了 AI Agent 元年。不只是 Manus 这种通用型 Agent，还有设计领域的 Lo [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>
<p>2025 年确实成了 AI Agent 元年。不只是 Manus 这种通用型 Agent，还有设计领域的 Lovart、编程领域的 Claude Code 和 Cursor 等垂直产品。经常会有人问：这些 Agent 到底是如何工作的？为什么有的 Agent 看着很智能，有的却在掉链子？</p>
<p>大模型大家都可以用，但为啥结果差异比较大呢？</p>
<p>这是由于在大模型能力一致的前提下，<strong>决定 Agent 成败的，我们给它提供了多少有用的信息</strong>。这就是今天要聊的核心话题——从提示词工程到上下文工程的演进。</p>
<h2 data-id="heading-0">1. Agent 的三个核心动作</h2>
<p>要理解上下文工程，我们得先从 AI Agent 的基本工作原理说起。简单来说，Agent 就像一个能自主完成任务的助手，它的工作流程可以概括为三个核心动作：<strong>感知、计划、行动</strong>。</p>
<h3 data-id="heading-1">1.1 感知</h3>
<p>感知是 Agent 的第一步，也是最容易被忽视的一步。就像开车需要看清路况，Agent 需要准确理解当前的情况。</p>
<p>这里有个关键点：感知包含两个层面。</p>
<p><strong>状态感知</strong>是对客观环境的理解。比如你让 Agent 帮你改代码，它需要知道：</p>
<ul>
<li>项目用的是什么语言和框架</li>
<li>现有的代码结构是怎样的</li>
<li>有哪些依赖和限制</li>
</ul>
<p><strong>意图感知</strong>则是理解你真正想要什么。当你说&#8221;优化这段代码&#8221;时，Agent 需要判断：</p>
<ul>
<li>是要提升性能还是改善可读性？</li>
<li>有没有特定的性能指标要求？</li>
<li>是否需要保持向后兼容？</li>
</ul>
<p>很多 Agent 失败的案例，根源就在于感知出了问题。比如用户说&#8221;这个太慢了&#8221;，如果 Agent 只是机械地理解为&#8221;需要优化&#8221;，而没有深入了解具体慢在哪里、期望达到什么速度，那后续的优化很可能南辕北辙。</p>
<h3 data-id="heading-2">1.2 计划</h3>
<p>有了准确的感知，Agent 需要制定行动计划。这就像做菜，你得先想好步骤：先切菜还是先热油，什么时候放调料。</p>
<p>计划能力很大程度上取决于底层的大语言模型。不同模型有不同的&#8221;性格&#8221;：</p>
<ul>
<li>Claude 喜欢深思熟虑，会考虑多种可能性</li>
<li>GPT-4 比较均衡，执行标准任务很稳定</li>
<li>Gemini 更大胆，有时会提出创新方案</li>
</ul>
<p>从技术架构上来说：</p>
<ul>
<li>Gemini 侧重多模态融合和大规模上下文处理</li>
<li>Claude 专注于精确推理和复杂任务执行</li>
</ul>
<p>但光有好模型还不够。Agent 的计划质量很大程度上取决于它掌握的信息是否充分。如果缺少关键信息，再聪明的模型也会做出糟糕的计划。</p>
<h3 data-id="heading-3">1.3 行动</h3>
<p>最后是行动模块，让 Agent 真正能够&#8221;做事&#8221;。目前主流的实现方式是函数调用（Function Calling）——预先定义一系列工具函数，比如搜索网页、读写文件、发送邮件等，然后让模型根据需要调用这些函数。</p>
<p>这里面临的挑战是工具选择。当可用工具很多时，Agent 可能会困惑。就像给你一个装满各种工具的工具箱，如果不清楚每个工具的用途，很容易选错。</p>
<h2 data-id="heading-4">2. Agent 的四大支撑系统</h2>
<p>除了核心的感知-计划-行动循环，现代 Agent 还需要四个重要的支撑系统：</p>
<h3 data-id="heading-5">2.1 记忆系统</h3>
<p>记忆让 Agent 能够积累经验。就像人类一样，Agent 需要不同类型的记忆：</p>
<ul>
<li><strong>工作记忆</strong>：处理当前任务的临时信息</li>
<li><strong>情景记忆</strong>：之前的对话和任务记录</li>
<li><strong>语义记忆</strong>：领域知识和最佳实践</li>
</ul>
<p>但这里有个反直觉的发现：<strong>好的记忆系统不仅要会记住，更要会遗忘</strong>。</p>
<p>为什么？因为不是所有信息都值得保留。过时的信息、错误的尝试、无关的细节，如果都保存下来，反而会干扰 Agent 的判断。这就像你的电脑，定期清理缓存才能保持流畅运行。</p>
<h3 data-id="heading-6">2.2 工具系统</h3>
<p>工具让 Agent 能够与外部世界交互。早期的 Agent 只能回答问题，现在的 Agent 可以：</p>
<ul>
<li>搜索信息</li>
<li>操作文件</li>
<li>调用 API</li>
<li>甚至控制其他软件</li>
</ul>
<p>Anthropic 推出的 MCP（Model Context Protocol）协议，试图为工具调用建立统一标准。这就像 USB 接口的标准化，让不同的工具都能方便地接入 Agent 系统。</p>
<h3 data-id="heading-7">2.3 安全系统</h3>
<p>给 Agent 执行能力就像给孩子一把剪刀，必须要有安全措施。Manus 刚上线时就出现过安全问题，有人通过特殊的提示词，让 Agent 打包了执行环境的所有代码。</p>
<p>现代 Agent 通常采用多层防护：</p>
<ul>
<li>沙箱隔离：所有操作都在受控环境中执行</li>
<li>权限管理：根据用户和场景动态分配权限</li>
<li>审计日志：记录所有操作便于追溯</li>
</ul>
<h3 data-id="heading-8">2.4 评估系统</h3>
<p>如何判断一个 Agent 的表现？这比想象中复杂。不像传统软件有明确的对错，Agent 的输出往往有多种可能的&#8221;正确答案&#8221;。</p>
<p>评估需要考虑多个维度：</p>
<ul>
<li>任务完成度</li>
<li>效率和成本</li>
<li>用户满意度</li>
<li>安全合规性</li>
</ul>
<h2 data-id="heading-9">3. 从提示词工程到上下文工程</h2>
<p>理解了 Agent 的工作原理，我们再来看看工程实践的演进。</p>
<h3 data-id="heading-10">3.1 提示词工程的局限</h3>
<p>去年大家还在研究怎么写提示词。什么&#8221;你是一个专业的XX&#8221;、&#8221;请一步一步思考&#8221;，各种模板层出不穷。但很快我们发现，光靠精心设计的提示词，很难让 Agent 处理复杂任务。</p>
<p>原因很简单：<strong>提示词只是静态的指令，而真实任务需要动态的信息</strong>。</p>
<p>就像你请人帮忙，光说&#8221;帮我订个机票&#8221;是不够的，还需要告诉对方：</p>
<ul>
<li>出发地和目的地</li>
<li>时间和预算</li>
<li>个人偏好</li>
<li>可用的支付方式</li>
</ul>
<h3 data-id="heading-11">3.2 上下文工程的兴起</h3>
<p>上下文工程是一个更宏大的概念。也有人说又是套壳包装了一下。但是我觉得非包装，而是大家对于如何和大模型交互有了更全面的认知。用 Shopify CEO Tobi Lütke 的话说，上下文工程是「提供所有必要上下文，让任务对 LLM 来说变得可解的艺术」。</p>
<p><strong>上下文不只是提示词，而是 Agent 在生成回复前能看到的所有信息</strong>：</p>
<ol>
<li><strong>指令上下文</strong>：系统提示词、任务说明、行为规范</li>
<li><strong>对话上下文</strong>：当前和历史对话记录，包含用户意图，如输出的结构</li>
<li><strong>知识上下文</strong>：相关文档、数据库信息、搜索结果</li>
<li><strong>工具上下文</strong>：可用函数的描述和使用方法</li>
<li><strong>状态上下文</strong>：环境变量、用户偏好、系统状态</li>
</ol>
<p>更重要的是，上下文工程是一个<strong>动态系统</strong>，不是静态模板：</p>
<ul>
<li>它根据任务类型选择相关信息</li>
<li>它随着对话进展更新内容</li>
<li>它平衡信息的全面性和简洁性</li>
</ul>
<p>从终局的逻辑来看，上下文工程的结果还是给到大模型的提示词。</p>
<p>Google DeepMind 的一位工程师写了一篇博客，地址：<a title="https://www.philschmid.de/context-engineering" href="https://link.juejin.cn?target=https%3A%2F%2Fwww.philschmid.de%2Fcontext-engineering" target="_blank">www.philschmid.de/context-eng…</a> 其中有一张图，如下：</p>
<p><img class="medium-zoom-image" src="https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/fb30199a2a1b472ca666cc709fc04d30~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5r2Y6ZSm:q75.awebp?rk3s=f64ab15b&amp;x-expires=1754181331&amp;x-signature=zuu1fZtuGdCTU1ujhHALf8RVsBg%3D" alt="image.png" /></p>
<p>讲了七个部分：</p>
<ul>
<li><strong>指令 / 系统提示词</strong>：定义模型在对话过程中行为的初始指令集，可以/应该包含示例、规则等。</li>
<li><strong>用户提示词</strong>：来自用户的即时任务或问题。</li>
<li><strong>状态 / 历史记录（短期记忆）</strong>：当前对话，包括导致此刻的用户和模型响应。</li>
<li><strong>长期记忆</strong>：持久的知识库，从许多先前的对话中收集而来，包含已学习的用户偏好、过去项目的摘要，或被告知要记住以供将来使用的事实。</li>
<li><strong>检索信息（RAG）</strong>：外部的最新知识，从文档、数据库或API中获取的相关信息，用于回答特定问题。</li>
<li><strong>可用工具</strong>：它可以调用的所有函数或内置工具的定义（例如：check_inventory（检查库存）、send_email（发送邮件））。</li>
<li><strong>结构化输出</strong>：关于模型响应格式的定义，例如JSON对象。</li>
</ul>
<p>这七个部分都包含在上面的 5 个上下文中。</p>
<h2 data-id="heading-12">4. 上下文工程的核心挑战</h2>
<p>Karpathy 把 LLM 比作新型操作系统，上下文窗口就像 RAM。这个比喻很精确——就像内存管理是操作系统的核心功能，上下文管理是 Agent 工程的核心挑战。</p>
<p>主要挑战包括：</p>
<p><strong>1. 容量限制</strong> 即使 Claude 已支持 20 万 token，Gemini 甚至支持 200 万 token，但这些容量在复杂任务面前仍然捉襟见肘。一个长时间运行的 Agent，轻易就能产生海量的交互记录。</p>
<p><strong>2. 注意力分散</strong> 研究发现，当上下文过长时，模型会出现&#8221;迷失在中间&#8221;现象——对开头和结尾的内容记忆较好，但中间部分容易遗漏。这就像看一本太厚的书，中间章节的内容总是记不清。</p>
<p><strong>3. 性能退化</strong> 过长的上下文不仅增加成本和延迟，还会降低模型的推理能力。Drew Breunig 总结了几种典型问题：</p>
<ul>
<li>上下文污染：错误信息影响后续判断</li>
<li>上下文干扰：无关信息分散注意力</li>
<li>上下文冲突：不同部分信息相互矛盾</li>
</ul>
<h2 data-id="heading-13">5. 上下文工程的四种核心策略</h2>
<p>面对这些挑战，有四种核心策略：</p>
<h3 data-id="heading-14">5.1 有选择的保存</h3>
<p>这是把重要信息保存到上下文窗口之外，需要时再取用。</p>
<p><strong>便签本模式</strong>：Agent 在执行任务时记笔记，保存中间结果和重要发现。Anthropic 的研究系统会在开始时制定计划并保存，避免因上下文截断而丢失。</p>
<p><strong>长期记忆</strong>：跨会话保存的信息，包括用户偏好、领域知识、成功案例等。ChatGPT、Cursor 都实现了这种机制。</p>
<p>关键是要有选择地保存。不是什么都值得记住，需要识别真正有价值的信息。</p>
<h3 data-id="heading-15">5.2 选择对的信息</h3>
<p>保存了信息，还需要在合适的时候取出来用。</p>
<p><strong>记忆检索</strong>：当积累了大量记忆后，如何找到相关的部分？简单的关键词匹配往往不够，需要语义理解。ChatGPT 会根据当前对话内容，自动检索相关的历史记忆。</p>
<p><strong>工具筛选</strong>：当可用工具很多时，全部列出会让模型困惑。研究表明，通过语义匹配只提供相关工具，可以将准确率提升 3 倍。</p>
<p><strong>知识召回</strong>：这就是 RAG（检索增强生成）的核心。但实现好的 RAG 系统很有挑战。Windsurf 的工程师分享说，他们结合了多种技术：向量检索、关键词搜索、AST 解析、知识图谱，最后还要重排序。</p>
<h3 data-id="heading-16">5.3 压缩提炼</h3>
<p>当信息太多时，需要压缩和精炼。</p>
<p><strong>轨迹总结</strong>：Claude Code 的&#8221;自动压缩&#8221;功能就是典型例子。当上下文使用超过 95% 时，它会总结整个对话历史，保留关键信息的同时大幅减少 token 使用。</p>
<p><strong>定点压缩</strong>：在特定环节进行信息精炼。比如搜索工具返回大量结果后立即总结，或者在不同 Agent 交接时压缩传递的信息。</p>
<p><strong>智能裁剪</strong>：根据相关性和时效性，自动删除不必要的信息。可以基于时间（删除过早的对话）、频率（删除很少用到的信息）或相关性（删除与当前任务无关的内容）。</p>
<h3 data-id="heading-17">5.4 分而治之</h3>
<p>把不同类型的信息分开管理，避免相互干扰。</p>
<p><strong>多智能体架构</strong>：让不同的 Agent 处理不同的子任务，每个都有自己的上下文空间。Anthropic 的研究显示，多个专门的 Agent 往往比一个通用 Agent 表现更好。</p>
<p><strong>环境隔离</strong>：HuggingFace 的做法很有启发——让 Agent 生成代码，在沙箱中执行，只把必要结果返回。这样可以把大量中间数据隔离在执行环境中。</p>
<p><strong>状态分离</strong>：通过精心设计的状态结构，把不同类型的信息存在不同字段，只在需要时才暴露给模型。</p>
<h2 data-id="heading-18">6 小结</h2>
<p>上下文工程正在成为 AI 工程师的核心技能。随着 Agent 能力的提升，如何管理和优化上下文将变得更加重要。</p>
<p>几个值得关注的发展方向：</p>
<ol>
<li><strong>自适应上下文</strong>：Agent 自己学习什么信息重要，自动调整上下文策略</li>
<li><strong>分布式上下文</strong>：跨多个 Agent 和系统共享和同步上下文</li>
<li><strong>个性化上下文</strong>：根据用户特点和偏好定制上下文管理策略</li>
<li><strong>实时优化</strong>：在运行时动态调整上下文，而不是预先设定</li>
</ol>
<p>当前不再是简单地“调教&#8221;模型说出正确的话，而是构建一个完整的信息系统，让 Agent 真正理解任务、掌握必要信息、做出正确决策。这种转变，预示着 AI Agent 正在从实验室的玩具，变成真正能够解决实际问题的工具。</p>
<p>如 Cognition 所说：&#8221;上下文工程实际上是构建 AI Agent 的工程师的头号工作。&#8221;。</p>
<p>以上。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/07/ai-agent-context-engineering/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于 AI Agent： 从 Manus 聊起</title>
		<link>https://www.phppan.com/2025/07/ai-agent-manus/</link>
		<comments>https://www.phppan.com/2025/07/ai-agent-manus/#comments</comments>
		<pubDate>Sat, 12 Jul 2025 03:39:54 +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[Cursor]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2395</guid>
		<description><![CDATA[最近几天 Manus 的新闻不停的出现在我的信息流中，从 Manus 官方账号清空了微博、小红书上的所有内容， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #000000;" data-tool="mdnice编辑器">最近几天 Manus 的新闻不停的出现在我的信息流中，从 Manus 官方账号清空了微博、小红书上的所有内容，到裁员争议，据说将核心技术人员迁往了新加坡。一个从北京、武汉出发的纯正中国公司，最终选择了离开。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">还记得今年火热的 3 月，Manus 发布当天就引爆了社交网络。邀请码一码难求，甚至在二手平台被炒出天价。创始人肖弘（人称red）带领的这支年轻团队，用一个产品点燃了整个行业对 AI Agent 的热情。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">2025 年是 AI Agent 元年。AI Agent 的发展速度惊人。不只是 Manus 这种通用型 Agent，还有各种垂直领域的，如 设计领域的 lovart，编程领域的 Claude Code Cursor 等等。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">那什么是 AI Agent，它由什么组成？今天我们聊一聊。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">1. 从专家系统说起</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">要说 AI Agent 的历史，得从上世纪 60 年代说起。那时候，计算机科学家们就在琢磨一个事：能不能让机器像人一样，自己去感知环境、做出决策、然后采取行动？</p>
<p style="color: #000000;" data-tool="mdnice编辑器">最早的尝试是专家系统。比如 1970 年代斯坦福大学开发的 MYCIN，这是一个诊断血液感染疾病的系统。它的工作方式很简单：问医生一堆问题，然后根据预设的规则给出诊断建议。虽然现在看来很原始，但在当时，这已经算是「智能」了。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">到了 80 年代，出现了更复杂的系统，比如 R1/XCON，帮助 DEC 公司配置计算机系统。这些系统有个共同特点：它们都是基于规则的。你得事先把所有可能的情况都想到，然后写成 if-then 规则。问题是，现实世界太复杂了，你不可能把所有情况都考虑进去。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">90 年代，研究者们开始尝试新的路子。他们发现，与其让人去编写所有规则，不如让机器自己去学习。于是有了基于机器学习的 Agent，比如强化学习 Agent。这些 Agent 可以通过试错来学习如何完成任务。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">但真正的转折点出现在 2010 年代深度学习兴起之后。特别是 2017 年 Transformer 架构的出现，彻底改变了游戏规则。有了 GPT、BERT 这些大语言模型，AI Agent 突然变得「聪明」了很多。它们不再需要人类事先编写规则，而是可以理解自然语言，根据上下文做出合理的判断。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">2. 现代 AI Agent 的样子</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">要理解现代的 AI Agent，我们得先搞清楚它到底是什么。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">简单来说，<strong style="color: #0e88eb;">AI Agent 就是一个能够自主感知环境、制定计划、采取行动来完成特定目标的系统</strong>。听起来很抽象？我举个例子：</p>
<p style="color: #000000;" data-tool="mdnice编辑器">假设你要让 AI 帮你订一张从北京到上海的机票。一个简单的聊天机器人可能只会回答：&#8221;请您登录航空公司网站自行预订。&#8221;但一个真正的 AI Agent 会这样做：</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;">甚至可能帮你完成预订（如果有相应的接口）</section>
</li>
</ol>
<p style="color: #000000;" data-tool="mdnice编辑器">这就是 AI Agent 和普通 AI 应用的区别：<strong style="color: #0e88eb;">它不是被动地回答问题，而是主动地解决问题</strong>。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3. 核心技术和架构</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">现在咱们来看看 AI Agent 是怎么实现的。核心架构可以分成四个部分：</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.1 感知模块</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这是 Agent 的「眼睛」和「耳朵」。它需要理解用户的输入，同时还要感知环境的状态。比如，当你让 AI 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>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">状态上下文</strong>是环境的客观信息。比如：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">当前项目使用的是 Python 3.9</section>
</li>
<li>
<section style="color: #010101;">代码库里已经有了用户认证模块</section>
</li>
<li>
<section style="color: #010101;">数据库是 MySQL</section>
</li>
<li>
<section style="color: #010101;">使用的是的 FastAPI 框架</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这些信息是确定的、可验证的事实。Agent 需要准确地获取和理解这些信息，因为任何误判都可能导致后续行动的失败。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">意图上下文</strong>则是用户想要达成的目标，这往往更加模糊和主观。比如用户说「帮我优化一下这段代码」，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>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">区分这两种上下文至关重要。很多 AI Agent 的失败，就是因为混淆了状态和意图。比如，用户说「这个函数太慢了」，Agent 需要识别出：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">状态：函数执行时间是 500ms</section>
</li>
<li>
<section style="color: #010101;">意图：用户希望降低到 100ms 以内</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">现代的 AI Agent 通过多种方式来增强感知能力：</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">多模态感知</strong>：不只是文字，还包括图像、语音、甚至视频。Cursor 支持图片上传，代码索引，文档等。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">主动询问</strong>：当信息不充分时，优秀的 Agent 会主动提问。「你提到要优化性能，具体是想优化响应时间还是内存占用？」这种澄清式的对话，能大大提高后续行动的准确性。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">历史记录</strong>：通过分析用户的历史行为，Agent 可以更好地理解当前的意图。如果用户之前多次要求「简洁的代码」，那么在新的任务中，Agent 就会倾向于生成更简洁的解决方案。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">环境探测</strong>：高级的 Agent 还会主动探测环境。比如，在开始写代码前，它可能会先检查项目的配置文件、依赖列表、测试用例等，构建一个完整的环境画像。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">感知的准确性直接决定了 Agent 的表现。一个看不清路的司机，再好的驾驶技术也没用。同样，一个不能准确理解用户意图和环境状态的 Agent，后续的规划和执行必然会出问题。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.2 推理模块</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这是 Agent 的「大脑」，也就是大语言模型。现在主流的做法是使用 GPT-4、Claude、Gemini 这样的大模型。但光有大模型还不够，还需要深入理解不同模型的特性，才能让它们更好地工作。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">模型的性格差异</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">就像人有不同的性格，AI 模型也有各自的「个性」。这不是玄学，而是训练方式和优化目标的差异造成的。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">以 Cursor 编辑器的实践为例，他们把模型分为两大类：</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">思考型模型</strong>（Thinking Models）：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">Claude 3 Opus：喜欢先理解全局，会主动推断你的意图</section>
</li>
<li>
<section style="color: #010101;">Gemini 2.0 Flash：自信果断，经常会做出超出预期的大改动</section>
</li>
<li>
<section style="color: #010101;">o1：专门为复杂推理设计，会花时间深入分析问题</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这类模型就像经验丰富的专家，你给它一个大方向，它会自己规划路径。适合探索性任务、大规模重构、或者当你自己也不太确定最终方案时使用。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">执行型模型</strong>（Non-thinking Models）：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">Claude 3.5 Sonnet：等待明确指令，不会过度推断</section>
</li>
<li>
<section style="color: #010101;">GPT-4 Turbo：行为可预测，适合精确控制</section>
</li>
<li>
<section style="color: #010101;">文心一言 4.0：在中文任务上表现稳定</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">这类模型像是可靠的助手，严格按照你的要求执行。适合明确的任务、需要精确控制的场景、或者当你已经知道要做什么时使用。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">选择模型的艺术</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">选择合适的模型，就像选择合适的工具。你不会用大锤子去拧螺丝，也不会用螺丝刀去砸钉子。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">根据任务类型选择</strong>：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">代码生成：Claude 3.5 Sonnet 和 GPT-4 都很优秀</section>
</li>
<li>
<section style="color: #010101;">代码理解和重构：Gemini 2.0 Flash 的长上下文能力突出</section>
</li>
<li>
<section style="color: #010101;">复杂 Bug 调试：o1 的深度推理能力更适合</section>
</li>
<li>
<section style="color: #010101;">中文文档处理：通义千问、豆包有本土优势</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">根据交互风格选择</strong>：</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;">如果你需要创造性方案：选择更&#8221;活跃&#8221;的模型</section>
</li>
<li>
<section style="color: #010101;">如果你需要稳定输出：选择更&#8221;保守&#8221;的模型</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">提示词工程的进化</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">早期的 AI Agent 严重依赖精心设计的提示词。但随着模型能力的提升，这种情况正在改变。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">从复杂到简单</strong>： 过去我们可能需要这样的提示词：</p>
<pre style="color: #000000;" data-tool="mdnice编辑器"><code style="color: #abb2bf;">你是一个专业的Python开发者。请严格遵循PEP8规范。
在编写代码时，请考虑以下几点：
1. 代码的可读性
2. 性能优化
3. 错误处理
...（还有20条）
</code></pre>
<p style="color: #000000;" data-tool="mdnice编辑器">现在，一句简单的&#8221;帮我优化这段代码&#8221;就能得到不错的结果。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">动态提示词策略</strong>： 现代 AI 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编辑器"><strong style="color: #0e88eb;">混合模型策略</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">越来越多的 AI Agent 开始采用混合模型策略，在一个任务流程中使用多个模型：</p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">理解阶段</strong>：使用 Claude 3 Opus 深入分析需求</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">规划阶段</strong>：使用 o1 制定详细的执行计划</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">执行阶段</strong>：使用 GPT-4 Turbo 快速生成代码</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">优化阶段</strong>：使用专门的代码模型进行微调</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;">3.3 记忆模块</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">人做事情需要记住前面发生了什么，AI 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>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">但实现一个好的记忆系统，比想象中要复杂得多。</p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">3.3.1 记忆的层次结构</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">就像人脑有不同类型的记忆，AI Agent 的记忆系统也需要分层设计：</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">感知记忆（Sensory Memory）</strong>：</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编辑器"><strong style="color: #0e88eb;">工作记忆（Working Memory）</strong>：</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编辑器"><strong style="color: #0e88eb;">情景记忆（Episodic Memory）</strong>：</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编辑器"><strong style="color: #0e88eb;">语义记忆（Semantic Memory）</strong>：</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>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">3.3.2 实现方案-RAG（检索增强生成）</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">这是目前最成熟的方案。基本思路是：当 AI Agent 需要回答问题时，先去知识库里检索相关信息，然后把这些信息作为上下文提供给大模型。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如你问：&#8221;我们公司的年假政策是什么？&#8221;Agent 会先去检索公司的政策文档，找到相关内容，然后基于这些内容生成回答。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">RAG 的进化史</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">第一代 RAG（2020-2022）：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">简单的向量检索</section>
</li>
<li>
<section style="color: #010101;">使用 BERT 或 Sentence-BERT 做编码</section>
</li>
<li>
<section style="color: #010101;">召回 Top-K 相关文档</section>
</li>
<li>
<section style="color: #010101;">效果一般，经常找不到真正相关的内容</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">第二代 RAG（2023-2024）：</p>
<ul class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">引入混合检索（向量+关键词）</section>
</li>
<li>
<section style="color: #010101;">使用更强的编码模型（如 BGE、E5）</section>
</li>
<li>
<section style="color: #010101;">加入重排序（Reranking）步骤</section>
</li>
<li>
<section style="color: #010101;">开始考虑文档结构和语义分块</section>
</li>
</ul>
<p style="color: #000000;" data-tool="mdnice编辑器">第三代 RAG（2024-现在）：</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编辑器"><strong style="color: #0e88eb;">实践中的 RAG 优化技巧</strong></p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">智能分块</strong>：不要机械地按字数切分，而是按语义单元切分</p>
</section>
<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>
</ul>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">多路召回</strong>：同时使用多种检索策略</p>
</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">向量相似度检索</section>
</li>
<li>
<section style="color: #010101;">BM25 关键词检索</section>
</li>
<li>
<section style="color: #010101;">实体链接检索</section>
</li>
<li>
<section style="color: #010101;">基于图的检索</section>
</li>
</ul>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">上下文工程</strong>：检索到的内容需要精心组织</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">增量索引</strong>：新知识的实时更新</p>
</section>
<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>
</ul>
</li>
</ol>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">3.3.3 实现方案-超长上下文</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">最新的趋势是直接增加模型的上下文长度。比如 Claude 3 已经支持 200K token 的上下文，Gemini 1.5 Pro 甚至支持 200 万 token。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">长上下文的真实挑战</strong></p>
<p style="color: #000000;" data-tool="mdnice编辑器">虽然模型号称支持超长上下文，但实际使用中会遇到很多问题：</p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">「迷失在中间」现象</strong>：模型对上下文开头和结尾的内容记忆较好，但中间部分容易遗忘</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">注意力稀释</strong>：上下文越长，模型对每个部分的注意力就越分散</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">推理退化</strong>：在超长上下文中，模型的推理能力会显著下降</p>
</section>
</li>
</ol>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">混合方案：长上下文 + 选择性注意</strong></p>
<h3 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e88eb;">3.3.4 主动遗忘</span></h3>
<p style="color: #000000;" data-tool="mdnice编辑器">这是一个反直觉但很重要的概念：不是记住越多越好，而是要学会遗忘。</p>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">为什么需要遗忘？</strong></p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">降噪</strong>：不是所有信息都值得记住</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">隐私</strong>：某些敏感信息需要及时删除</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">效率</strong>：保持记忆系统的高效运转</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">相关性</strong>：过时的信息可能产生负面影响</section>
</li>
</ol>
<p style="color: #000000;" data-tool="mdnice编辑器"><strong style="color: #0e88eb;">遗忘策略</strong></p>
<ol class="list-paddingleft-1" style="color: #000000;">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">基于时间的遗忘</strong>：</p>
</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">会话结束后 24 小时删除临时信息</section>
</li>
<li>
<section style="color: #010101;">30 天后归档低频访问的记忆</section>
</li>
<li>
<section style="color: #010101;">90 天后删除无用的错误记录</section>
</li>
</ul>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">基于重要性的遗忘</strong>：</p>
</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #010101;">使用 LRU（最近最少使用）策略</section>
</li>
<li>
<section style="color: #010101;">基于访问频率的动态评分</section>
</li>
<li>
<section style="color: #010101;">保留高价值的&#8221;关键时刻&#8221;</section>
</li>
</ul>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">基于相关性的遗忘</strong>：</p>
</section>
<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>
</ul>
</li>
</ol>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">3.4 行动模块</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这是 Agent 的「手脚」，让它能够真正做事情。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">行动模块的核心是 Function Calling（函数调用），这是目前最主流的方式。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">简单来说，就是预先定义好一系列函数，比如搜索网页、查询数据库、发送邮件等，然后告诉大模型这些函数的作用和参数。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">当用户提出需求时，模型会判断需要调用哪个函数，提取相应的参数，执行函数并获取结果。这个过程已经从最初的单次调用，进化到现在可以进行多步骤调用、并行执行、错误重试等复杂操作。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">Anthropic 推出的 MCP（Model Context Protocol）协议，试图建立行动模块的统一标准。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">MCP 采用服务器-客户端架构，工具提供方作为服务器，AI 应用作为客户端，通过标准化的协议进行通信。这种设计的好处是解耦和复用：AI 应用不需要知道工具的具体实现细节，一个 MCP 服务器可以同时服务多个 AI 应用。更重要的是，MCP 提供了统一的安全管理和权限控制机制，让行动模块的集成变得更加简单和安全。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">安全性是行动模块最关键的考虑因素。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">在 Manus 刚上线的时候就爆出一个问题，有人用提示词，让 AI Agent 打包了当前执行的环境的所有代码。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">给 AI 执行能力就像给孩子一把剪刀，必须设置严格的安全边界。现代 Agent 通常采用多层防护：首先是沙箱环境，所有代码执行都在隔离的容器中进行，限制内存、CPU、网络访问；其次是权限管理，基于用户角色和具体场景动态分配权限；最后是审计日志，记录所有行动的执行情况，便于追溯和分析。这些措施确保 Agent 在帮助用户的同时，不会造成安全风险。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">复杂任务往往需要多个行动的协调配合，这就需要工作流引擎来编排。比如&#8221;帮我分析竞品并生成报告&#8221;这个任务，可能需要先搜索竞品信息，然后提取关键数据，接着进行对比分析，最后生成可视化报告。工作流引擎负责管理这些步骤的执行顺序、处理步骤间的数据传递、应对执行失败等情况。高级的引擎还支持条件分支、循环执行、并行处理等复杂逻辑，让 Agent 能够处理更加复杂的任务。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">行动模块的未来发展方向是更强的自主性。目前的 Agent 主要执行预定义的动作，但未来可能会具备动作发现和学习能力，能够自动发现新的 API、学习新的操作模式、甚至创造性地组合已有动作来完成新任务。另一个重要方向是与物理世界的交互，通过机器人或 IoT 设备执行物理动作。随着这些能力的提升，AI Agent 将真正从「会说」进化到「会做」，成为人类在数字世界和物理世界中的得力助手。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4. 当前的局限性</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">AI 有其局限性，了解这些局限性，对于合理使用和未来改进都很重要。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.1 幻觉问题</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">这是所有基于大模型的 AI Agent 都面临的核心挑战。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">Agent 可能会编造不存在的 API、虚构执行结果、或者对自己的能力过度自信。比如，当你要求 Agent 查询某个数据库时，它可能会返回看起来合理但实际上完全虚构的数据。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">这种幻觉在连续多步骤的任务中会被放大，一个小错误可能导致整个任务链的崩溃。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">更危险的是，这些幻觉往往很难被发现，因为它们在表面上看起来完全合理。虽然通过 RAG、工具调用验证等方法可以部分缓解，但彻底解决仍然是个开放性难题。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.2 可靠性不足</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">AI 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编辑器">在一些对可靠性要求高的场景，比如金融交易、医疗诊断、工业控制等，目前的 AI Agent 还远远达不到要求。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">即使加入了重试机制、结果验证等保障措施，也只能提高而非保证可靠性。这导致在关键业务场景中，AI Agent 更多是作为辅助而非主导。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.3 成本与效率</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">运行一个功能完善的 AI Agent 系统成本不菲。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">首先是模型调用成本，特别是使用 GPT-4、Claude 等顶级模型时，每次交互都要花费不少钱。复杂任务可能需要多次模型调用，成本会快速累加。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">其次是延迟问题，每次函数调用、每次推理都需要时间，一个看似简单的任务可能需要等待数十秒甚至几分钟。对于需要实时响应的场景，当前的 AI Agent 往往力不从心。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">虽然可以通过模型蒸馏、缓存优化等手段降低成本，但在性能和成本之间找到平衡点仍然很困难。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.4 安全与隐私挑战</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">AI Agent 需要访问大量数据和系统才能发挥作用，这带来了严重的安全隐私问题。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">首先是数据泄露风险，Agent 可能无意中将敏感信息包含在给大模型的请求中，而这些数据可能被用于模型训练。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">其次是提示注入攻击，恶意用户可能通过精心构造的输入操控 Agent 执行非预期的操作。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">还有权限滥用问题，一个被赋予过多权限的 Agent 可能造成严重损害。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">虽然业界正在开发各种防护措施，如差分隐私、安全沙箱、细粒度权限控制等，但攻防对抗仍在继续。</p>
<h2 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">4.5 理解与推理的局限</span></h2>
<p style="color: #000000;" data-tool="mdnice编辑器">虽然大模型展现出了惊人的能力，但 AI Agent 在深层理解和复杂推理方面仍有明显不足。它们往往只能处理相对直接的任务，面对需要长链条推理、创造性思维或深层次理解的问题时就会暴露短板。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">比如，Agent 可能很擅长执行&#8221;帮我订一张机票&#8221;这样的任务，但如果要求&#8221;帮我规划一个考虑预算、时间、个人兴趣的完整旅行方案&#8221;，效果就会大打折扣。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">此外，Agent 缺乏真正的常识推理能力，可能会提出一些违背基本常识的方案。即使是最新的 o1 模型，虽然推理能力有所提升，但距离人类水平的推理能力还有很大差距。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #0e8aeb;">5. 写在最后</span></h1>
<p style="color: #000000;" data-tool="mdnice编辑器">AI Agent 的发展才刚刚开始。虽然现在的技术还不完美，但进步的速度是惊人的。两年前，我们还在惊叹 ChatGPT 能够进行对话；现在，AI Agent 已经能够帮我们写代码、分析数据、制定计划了。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">对于技术人员来说，现在是最好的时代。我们有机会参与到这场变革中，创造出真正有用的 AI Agent。但同时，我们也要保持清醒：AI Agent 是工具，不是魔法。它能够提高效率，但不能替代人类的创造力和判断力。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">未来的世界，可能每个人都会有自己的 AI Agent 团队。就像现在每个人都有智能手机一样自然。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">而现在，正是这个未来的开端。</p>
<p style="color: #000000;" data-tool="mdnice编辑器">以上。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/07/ai-agent-manus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
