<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>sam</title>
        <link>https://justin3go.com</link>
        <description>经济学本科在读，AI 产品经理实习中，关注 AI 与产品设计，在这里记录学习与思考</description>
        <lastBuildDate>Thu, 24 Sep 2026 23:56:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-Hans</language>
        <image>
            <title>sam</title>
            <url>https://oss.justin3go.com/justin3goAvatar.jpg</url>
            <link>https://justin3go.com</link>
        </image>
        <copyright>Copyright© 2021-present sam</copyright>
        <item>
            <title><![CDATA[Jev 初体验：一个不生成文本的模型，8 道分类题全对]]></title>
            <link>https://justin3go.com/posts/2026/09/23-jev-first-look-decision-model</link>
            <guid>https://justin3go.com/posts/2026/09/23-jev-first-look-decision-model</guid>
            <pubDate>Wed, 23 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="jev-初体验-一个不生成文本的模型-8-道分类题全对" tabindex="-1">Jev 初体验：一个不生成文本的模型，8 道分类题全对 <a class="header-anchor" href="#jev-初体验-一个不生成文本的模型-8-道分类题全对" aria-label="Permalink to &quot;Jev 初体验：一个不生成文本的模型，8 道分类题全对&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote>
<p>为了找一段旧对话，我在自己的 Codex 日志里搜「jev」——432 个文件命中，99% 是 base64 噪声，但顺着剩下的真实记录挖出了一个已经配好却没用过的模型：<strong>Jev</strong>（TypeSafe AI 的 System One 决策模型，<code>jev-1.13.0</code>）。它不生成文本，只返回<strong>带概率的类型化判断</strong>（Choice/Noul/Score 三种原语）。本文记录两次实测：连通性检查（noul 判断紧急度 0.95，1.1s）和一次 8 道新闻标题分类实验——<strong>8/8 全对</strong>、单次调用 0.58s、2052 tokens，包括「OpenAI 发布面向法律的前沿模型产品」这种标题里带「模型」字样的易错题。结论：判断型和生成型不是竞争关系，把「选择题」从大模型的活儿里拆出来给 Jev，又快又便宜又可校准。</p>
</blockquote>
<!-- DESC SEP -->
<h2 id="引子-432-个命中-99-是噪声" tabindex="-1">引子：432 个命中，99% 是噪声 <a class="header-anchor" href="#引子-432-个命中-99-是噪声" aria-label="Permalink to &quot;引子：432 个命中，99% 是噪声&quot;">&ZeroWidthSpace;</a></h2>
<p>事情从一句帮忙开始：「搜下我的 Codex 对话日志，哪个文件夹里的对话提及 jev」。</p>
<p>Codex 的会话日志存在 <code>~/.codex/sessions/年/月/日/rollout-*.jsonl</code>。裸搜「jev」命中 432 个文件——听起来很多，但抽查上下文就露馅了：绝大多数命中来自<strong>图片附件的 base64 编码</strong>，<code>jev</code> 只是随机字符串里恰好出现的三个字母。加上词边界过滤后，真实命中收敛到九月的一小簇，其中一份归档会话（2026-09-21 17:21）的标题是「如何使用 Jev 模型」——内容是给 Claude Code 和 Codex <strong>配置 Jev 的完整过程</strong>。</p>
<p>顺藤摸瓜验证本机：<code>~/.claude/settings.json</code> 里果然躺着 <code>typesafe@typesafe-ai</code> 插件（v0.5.7，已启用）和一枚 <code>TYPESAFE_API_KEY</code>。也就是说，这个模型两天前被配好了，然后被我忘了。</p>
<p>那就实际调一次。先给一句话结论：<strong>Jev 不是又一个聊天模型——它把「判断」做成了编程原语，不生成一个字的文本，直接返回带概率的类型化答案。恰好是我这种内容站最缺的那块零件。</strong></p>
<h2 id="一、jev-是什么-system-one-与三种判断原语" tabindex="-1">一、Jev 是什么：System One 与三种判断原语 <a class="header-anchor" href="#一、jev-是什么-system-one-与三种判断原语" aria-label="Permalink to &quot;一、Jev 是什么：System One 与三种判断原语&quot;">&ZeroWidthSpace;</a></h2>
<p>TypeSafe 给 Jev 的定位是 <strong>System One 模型</strong>——借用心理学术语，指快而省的直觉式判断，区别于生成式模型的慢思考。官方文档的表述：</p>
<blockquote>
<p>&quot;TypeSafe makes units of AI intelligence usable like programming primitives… <strong>Jev</strong> is TypeSafe's flagship and first System One model. It understands natural language and returns typed answers and probabilities rather than generating text or reasoning explanations.&quot;</p>
<p>（TypeSafe 把 AI 智能单元做得像编程原语一样可用……Jev 是 TypeSafe 的旗舰也是第一个 System One 模型。它理解自然语言，返回<strong>类型化的答案和概率</strong>，而不是生成文本或推理解释。）</p>
</blockquote>
<p>它的 API 只有三种问题原语，覆盖判断的三个基本形态：</p>
<p>| 原语 | 回答什么 | 返回 | 一句话 |
|</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="jev-初体验-一个不生成文本的模型-8-道分类题全对" tabindex="-1">Jev 初体验：一个不生成文本的模型，8 道分类题全对 <a class="header-anchor" href="#jev-初体验-一个不生成文本的模型-8-道分类题全对" aria-label="Permalink to &quot;Jev 初体验：一个不生成文本的模型，8 道分类题全对&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote>
<p>为了找一段旧对话，我在自己的 Codex 日志里搜「jev」——432 个文件命中，99% 是 base64 噪声，但顺着剩下的真实记录挖出了一个已经配好却没用过的模型：<strong>Jev</strong>（TypeSafe AI 的 System One 决策模型，<code>jev-1.13.0</code>）。它不生成文本，只返回<strong>带概率的类型化判断</strong>（Choice/Noul/Score 三种原语）。本文记录两次实测：连通性检查（noul 判断紧急度 0.95，1.1s）和一次 8 道新闻标题分类实验——<strong>8/8 全对</strong>、单次调用 0.58s、2052 tokens，包括「OpenAI 发布面向法律的前沿模型产品」这种标题里带「模型」字样的易错题。结论：判断型和生成型不是竞争关系，把「选择题」从大模型的活儿里拆出来给 Jev，又快又便宜又可校准。</p>
</blockquote>
<!-- DESC SEP -->
<h2 id="引子-432-个命中-99-是噪声" tabindex="-1">引子：432 个命中，99% 是噪声 <a class="header-anchor" href="#引子-432-个命中-99-是噪声" aria-label="Permalink to &quot;引子：432 个命中，99% 是噪声&quot;">&ZeroWidthSpace;</a></h2>
<p>事情从一句帮忙开始：「搜下我的 Codex 对话日志，哪个文件夹里的对话提及 jev」。</p>
<p>Codex 的会话日志存在 <code>~/.codex/sessions/年/月/日/rollout-*.jsonl</code>。裸搜「jev」命中 432 个文件——听起来很多，但抽查上下文就露馅了：绝大多数命中来自<strong>图片附件的 base64 编码</strong>，<code>jev</code> 只是随机字符串里恰好出现的三个字母。加上词边界过滤后，真实命中收敛到九月的一小簇，其中一份归档会话（2026-09-21 17:21）的标题是「如何使用 Jev 模型」——内容是给 Claude Code 和 Codex <strong>配置 Jev 的完整过程</strong>。</p>
<p>顺藤摸瓜验证本机：<code>~/.claude/settings.json</code> 里果然躺着 <code>typesafe@typesafe-ai</code> 插件（v0.5.7，已启用）和一枚 <code>TYPESAFE_API_KEY</code>。也就是说，这个模型两天前被配好了，然后被我忘了。</p>
<p>那就实际调一次。先给一句话结论：<strong>Jev 不是又一个聊天模型——它把「判断」做成了编程原语，不生成一个字的文本，直接返回带概率的类型化答案。恰好是我这种内容站最缺的那块零件。</strong></p>
<h2 id="一、jev-是什么-system-one-与三种判断原语" tabindex="-1">一、Jev 是什么：System One 与三种判断原语 <a class="header-anchor" href="#一、jev-是什么-system-one-与三种判断原语" aria-label="Permalink to &quot;一、Jev 是什么：System One 与三种判断原语&quot;">&ZeroWidthSpace;</a></h2>
<p>TypeSafe 给 Jev 的定位是 <strong>System One 模型</strong>——借用心理学术语，指快而省的直觉式判断，区别于生成式模型的慢思考。官方文档的表述：</p>
<blockquote>
<p>&quot;TypeSafe makes units of AI intelligence usable like programming primitives… <strong>Jev</strong> is TypeSafe's flagship and first System One model. It understands natural language and returns typed answers and probabilities rather than generating text or reasoning explanations.&quot;</p>
<p>（TypeSafe 把 AI 智能单元做得像编程原语一样可用……Jev 是 TypeSafe 的旗舰也是第一个 System One 模型。它理解自然语言，返回<strong>类型化的答案和概率</strong>，而不是生成文本或推理解释。）</p>
</blockquote>
<p>它的 API 只有三种问题原语，覆盖判断的三个基本形态：</p>
<table tabindex="0">
<thead>
<tr>
<th>原语</th>
<th>回答什么</th>
<th>返回</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td>Choice</td>
<td>给定选项里选哪个</td>
<td>choice + 全选项概率分布</td>
<td>分类、路由</td>
</tr>
<tr>
<td>Noul</td>
<td>某条件是否成立</td>
<td>0–1 概率</td>
<td>是非判断</td>
</tr>
<tr>
<td>Score</td>
<td>某维度上处于哪一档</td>
<td>概率加权的档位</td>
<td>分级、排序</td>
</tr>
</tbody>
</table>
<p>（TypeSafe 公司背景、定价策略我没查，本文只谈实测。）</p>
<h2 id="二、api-长什么样-以及一个-422" tabindex="-1">二、API 长什么样，以及一个 422 <a class="header-anchor" href="#二、api-长什么样-以及一个-422" aria-label="Permalink to &quot;二、API 长什么样，以及一个 422&quot;">&ZeroWidthSpace;</a></h2>
<p>请求就三要素：<code>state</code>（给判断用的全部上下文）、<code>model</code>、<code>questions</code>（问题集合，一次可以问多个，<strong>互相看不见对方答案、并行执行</strong>）。</p>
<p>连通性检查用官方文档里的最小例子——判断一条客服消息是否紧急：</p>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">POST https:</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">//api.typesafe.ai/v1/systemone</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "state"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Help! My payouts have been failing for 3 days."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "model"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"jev-latest"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "questions"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "is_urgent"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "type"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"noul"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      "instructions"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Does this convey urgency?"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>返回（2026-09-23 实测，HTTP 200，1.1s）：</p>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "model"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"jev-1.13.0"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "answers"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    "is_urgent"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"type"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"noul"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"noul"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.95</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "usage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"input_tokens"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">283</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"output_tokens"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">23</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>「打款失败三天了帮帮我」→ 紧急概率 0.95。没有解释、没有寒暄，<strong>一个浮点数就是完整交付</strong>。</p>
<p>踩的坑也记一下：第一次发分类请求，我把 Choice 的 <code>criteria</code> 写成了数组 <code>[&quot;模型&quot;,&quot;产品&quot;,&quot;行业&quot;]</code>，直接 422：</p>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"type"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"dict_type"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">"loc"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"body"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"questions"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"choice"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"criteria"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> "msg"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Input should be a valid dictionary"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>正确形态是<strong>字典</strong>——选项名做 key，描述做 value（描述也可以是 <code>null</code>）：</p>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"criteria"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "模型"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"A model release or model capability update…"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "产品"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"An app, product, tool or platform launch…"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "行业"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Business, policy, funding, regulation news…"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>这个报错反而让我对它放心了一点：<strong>API 契约是强类型的</strong>，错得清楚，改得也快。</p>
<h2 id="三、8-道分类题-全对-包括陷阱题" tabindex="-1">三、8 道分类题：全对，包括陷阱题 <a class="header-anchor" href="#三、8-道分类题-全对-包括陷阱题" aria-label="Permalink to &quot;三、8 道分类题：全对，包括陷阱题&quot;">&ZeroWidthSpace;</a></h2>
<p>真正有意思的实验来自我博客的实际痛点：AI 动态模块每条新闻要打一个 <code>kind</code> 标签（模型/产品/行业），目前靠我拍脑袋。我把 6 条真实新闻标题加上 2 道易错题（融资、法案）塞进<strong>同一次调用</strong>的 8 个 Choice 问题，<code>state</code> 里放标题数组，每个问题用 <code>items[n].title</code> 引用。</p>
<p>结果（2026-09-23，单次调用 0.58s，1761+291 tokens）：</p>
<table tabindex="0">
<thead>
<tr>
<th>#</th>
<th>标题</th>
<th style="text-align:center">人工标注</th>
<th style="text-align:center">Jev</th>
<th>置信</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>阿里开源 Qwen-Image-2.1</td>
<td style="text-align:center">模型</td>
<td style="text-align:center">✅ 模型</td>
<td>1.0</td>
</tr>
<tr>
<td>2</td>
<td>Qwen3.8-Omni-Flash 全模态模型</td>
<td style="text-align:center">模型</td>
<td style="text-align:center">✅ 模型</td>
<td>1.0</td>
</tr>
<tr>
<td>3</td>
<td>OpenAI 发布 Astra for Law：面向法律行业的<strong>前沿模型产品</strong></td>
<td style="text-align:center">产品</td>
<td style="text-align:center">✅ 产品</td>
<td>1.0</td>
</tr>
<tr>
<td>4</td>
<td>Anthropic 提出度量前沿 AI 发展速度的新指标</td>
<td style="text-align:center">行业</td>
<td style="text-align:center">✅ 行业</td>
<td>1.0</td>
</tr>
<tr>
<td>5</td>
<td>Google 发布 Gemini 3.8 Live 对话模型</td>
<td style="text-align:center">模型</td>
<td style="text-align:center">✅ 模型</td>
<td>0.96（0.02 给了产品）</td>
</tr>
<tr>
<td>6</td>
<td>Perplexity 便携 AI 计算机扩展到 Windows RTX</td>
<td style="text-align:center">产品</td>
<td style="text-align:center">✅ 产品</td>
<td>1.0</td>
</tr>
<tr>
<td>7</td>
<td>某公司完成 10 亿美元 B 轮融资</td>
<td style="text-align:center">行业</td>
<td style="text-align:center">✅ 行业</td>
<td>1.0</td>
</tr>
<tr>
<td>8</td>
<td>欧盟通过 AI 法案执行细则</td>
<td style="text-align:center">行业</td>
<td style="text-align:center">✅ 行业</td>
<td>1.0</td>
</tr>
</tbody>
</table>
<p><strong>8/8。</strong> 两个细节值得展开：</p>
<p><strong>第 3 题是故意留的陷阱。</strong> 标题里「前沿模型产品」六个字同时含「模型」和「产品」，字面匹配必错。Jev 判了产品，概率 1.0——它抓住的是「产品化交付 ≠ 模型发布」这个语义区别，而这正是我人工标注时的判断依据。</p>
<p><strong>第 5 题的 0.02 泄漏是亮点不是缺陷。</strong> Gemini 3.8 Live 名义上是模型发布，但「边说边想边干活」的产品色彩很浓。Jev 主判模型（0.98），漏了 0.02 给产品——<strong>这个不确定性的分布，恰好对应我人工标注时犹豫的那半秒</strong>。概率输出让「模糊地带」从黑盒变成了可观测的东西，这是布尔值给不了的。</p>
<h2 id="四、它该用在哪-把选择题从大模型手里拆出来" tabindex="-1">四、它该用在哪：把选择题从大模型手里拆出来 <a class="header-anchor" href="#四、它该用在哪-把选择题从大模型手里拆出来" aria-label="Permalink to &quot;四、它该用在哪：把选择题从大模型手里拆出来&quot;">&ZeroWidthSpace;</a></h2>
<p>这次实验直接给了我一个接入点：更新 AI 动态时，在出表格关卡前先让 Jev 给每条候选打个 kind 建议值，我在表格里改——把「拍脑袋分类」换成「模型建议 + 人工把关」，每轮成本不到 2K tokens、一秒内。这个改动我还没落到 skill 里，但它已经在我下次说「更新AI动态」时的计划清单上了。</p>
<p>更一般的分工线：</p>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>该用谁</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td>分类/路由/是非判断</td>
<td>Jev</td>
<td>快、便宜、带概率</td>
</tr>
<tr>
<td>生成文本、写推理链</td>
<td>生成式模型</td>
<td>Jev 一个字都不产</td>
</tr>
<tr>
<td>复杂多步规划</td>
<td>生成式模型</td>
<td>System One 是直觉不是思考</td>
</tr>
</tbody>
</table>
<p>两个使用约束文档里写得很直白，实测也验证了：<strong>state 必须自包含</strong>（问题里引用的上下文都得给全，模型不会去查）；<strong>选项不能有遗漏</strong>（Choice 只能在给定的 criteria 里选，候选没覆盖就没有「全不对」出口，需要时得自己加一个 no-match 选项）。</p>
<h2 id="结论-好在哪、差在哪、谁该用" tabindex="-1">结论：好在哪、差在哪、谁该用 <a class="header-anchor" href="#结论-好在哪、差在哪、谁该用" aria-label="Permalink to &quot;结论：好在哪、差在哪、谁该用&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>好在哪：</strong></p>
<ul>
<li><strong>判断即原语。</strong> 一次调用八个问题并行，0.58s、2K tokens，换成生成式模型做同样的事，又贵又慢。</li>
<li><strong>概率可观测。</strong> 0.02 的泄漏让我看到了模型「犹豫什么」，阈值可以按业务调。</li>
<li><strong>强类型契约。</strong> 返回永远是结构化的，代码直接消费，没有「请从以下选项中选择」被模型聊成小作文的风险。</li>
</ul>
<p><strong>差在哪：</strong></p>
<ul>
<li><strong>没有解释。</strong> 只要答案不要理由是它的设计，但排查错误时你得自己猜它为什么错。</li>
<li><strong>选项依赖人工设计。</strong> criteria 描述写得好不好直接影响准确率，第 3 题判对有我描述「productized offering」一半功劳。</li>
<li><strong>生态早期。</strong> Python/JS SDK、三种原语，工具面还薄，复杂 workflow 要自己搭。</li>
</ul>
<p><strong>你该不该用它：</strong> 你的业务里有大量「二选一/多选一/打分」且每秒调几百次 → 值得认真看；你想让 AI 写文章、聊天、推理 → 它帮不上；你像我一样维护着需要人工把关的内容流水线 → <strong>把 Jev 放进关卡前面那一格</strong>，把关的人会谢谢你。</p>
<hr>
<p><em>实测数据均来自 2026-09-23 本机调用（api.typesafe.ai，<code>jev-latest</code> → <code>jev-1.13.0</code>），请求与响应原文见文中代码块，可复现；产品定位引自 docs.typesafe.ai 官方文档。</em></p>
]]></content:encoded>
            <author>just@justin3go.com (sam)</author>
        </item>
        <item>
            <title><![CDATA[22 小时三个模块：我用 Claude Code 把博客从文章站改成 AI 信息站]]></title>
            <link>https://justin3go.com/posts/2026/09/23-three-modules-in-one-day-with-claude-code</link>
            <guid>https://justin3go.com/posts/2026/09/23-three-modules-in-one-day-with-claude-code</guid>
            <pubDate>Wed, 23 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="_22-小时三个模块-我用-claude-code-把博客从文章站改成-ai-信息站" tabindex="-1">22 小时三个模块：我用 Claude Code 把博客从文章站改成 AI 信息站 <a class="header-anchor" href="#_22-小时三个模块-我用-claude-code-把博客从文章站改成-ai-信息站" aria-label="Permalink to &quot;22 小时三个模块：我用 Claude Code 把博客从文章站改成 AI 信息站&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote>
<p>2026-09-21 14:00 到 09-22 12:26，我用 <strong>Claude Code</strong> 在 22 小时里给这个 VitePress 博客加了三个内容模块：<strong>AI 动态</strong>（/news，主源外链+一句话点评）、<strong>模型竞技场</strong>（/arena，固定 8 题库 × 多模型逐字对答）、<strong>产品体验</strong>（/products，时间流体验日志+站内文章），6 个功能提交、测试从 31 涨到 45 全绿、本地构建逐次验证后推送 release 上线。本文拆解让这件事变快的真正原因——一个重复四次的「四件套」架构（数据 TS 文件 + 纯函数 utils + md 页面 + 录入 skill），以及比代码更难的部分：<strong>诚实规则</strong>（答案逐字照录、绝不代写、模型名如实标注）和关卡式工作流。结论：我的角色从写代码的人变成了把关的人，而这才是 AI 时代个人站该有的迭代速度。</p>
</blockquote>
<!-- DESC SEP -->
<h2 id="引子-「我觉得是核心哎」" tabindex="-1">引子：「我觉得是核心哎」 <a class="header-anchor" href="#引子-「我觉得是核心哎」" aria-label="Permalink to &quot;引子：「我觉得是核心哎」&quot;">&ZeroWidthSpace;</a></h2>
<p>9 月 21 日下午，我盯着自己的博客首页看了十分钟，越看越不对劲：一个挂着「经济学在读、AI 产品经理实习」招牌的个人站，居然没有任何 AI 内容的更新流——最新一篇文的日期停在八月中旬。</p>
<p>于是我跟 Claude Code 说：「想加一个<strong>最近 AI 模型和产品动态</strong>……我觉得是核心哎，你帮我看看怎么扩展。」</p>
<p>第二天中午 12:26，最后一个提交推送完毕。从 09-21 14:00 第一个模块提交（<code>5c99dd5</code>）到 09-22 12:26 发布 skill 沉淀（<code>2e1f47c</code>），<strong>22 小时 26 分钟</strong>，6 个功能提交，三个内容模块全部上线：AI 动态 <code>/news</code>、模型竞技场 <code>/arena</code>、产品体验 <code>/products</code>，外加一套录制工作流和发布管线文档，45 个测试全绿。</p>
<p>先给一句话结论：<strong>这次变快的根本原因不是模型聪明，而是第二次造轮子时套路已经定型——我的角色从「写代码的人」变成了「把关的人」，把关比写代码舒服，也更重要。</strong></p>
<p><img src="https://oss.justin3go.com/blogs/TODO-three-modules-architecture.png" alt="三模块四件套架构图：数据 TS 文件、纯函数 utils、md 页面、录入 skill 的复用关系"></p>
<h2 id="一、三个模块-一个套路" tabindex="-1">一、三个模块，一个套路 <a class="header-anchor" href="#一、三个模块-一个套路" aria-label="Permalink to &quot;一、三个模块，一个套路&quot;">&ZeroWidthSpace;</a></h2>
<p>三个模块需求是分三次提出的，而且一次比一次随意——第一次正经做了产品决策问卷，第三次只有一句「想再加个最新的 AI 产品/产品体验」。但实现速度反而一次比一次快，因为<strong>第二个模块开始，架构就固定成了四件套</strong>：</p>
<p>| 组件 | 是什么 | 例子 | 一句话 |
|</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="_22-小时三个模块-我用-claude-code-把博客从文章站改成-ai-信息站" tabindex="-1">22 小时三个模块：我用 Claude Code 把博客从文章站改成 AI 信息站 <a class="header-anchor" href="#_22-小时三个模块-我用-claude-code-把博客从文章站改成-ai-信息站" aria-label="Permalink to &quot;22 小时三个模块：我用 Claude Code 把博客从文章站改成 AI 信息站&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote>
<p>2026-09-21 14:00 到 09-22 12:26，我用 <strong>Claude Code</strong> 在 22 小时里给这个 VitePress 博客加了三个内容模块：<strong>AI 动态</strong>（/news，主源外链+一句话点评）、<strong>模型竞技场</strong>（/arena，固定 8 题库 × 多模型逐字对答）、<strong>产品体验</strong>（/products，时间流体验日志+站内文章），6 个功能提交、测试从 31 涨到 45 全绿、本地构建逐次验证后推送 release 上线。本文拆解让这件事变快的真正原因——一个重复四次的「四件套」架构（数据 TS 文件 + 纯函数 utils + md 页面 + 录入 skill），以及比代码更难的部分：<strong>诚实规则</strong>（答案逐字照录、绝不代写、模型名如实标注）和关卡式工作流。结论：我的角色从写代码的人变成了把关的人，而这才是 AI 时代个人站该有的迭代速度。</p>
</blockquote>
<!-- DESC SEP -->
<h2 id="引子-「我觉得是核心哎」" tabindex="-1">引子：「我觉得是核心哎」 <a class="header-anchor" href="#引子-「我觉得是核心哎」" aria-label="Permalink to &quot;引子：「我觉得是核心哎」&quot;">&ZeroWidthSpace;</a></h2>
<p>9 月 21 日下午，我盯着自己的博客首页看了十分钟，越看越不对劲：一个挂着「经济学在读、AI 产品经理实习」招牌的个人站，居然没有任何 AI 内容的更新流——最新一篇文的日期停在八月中旬。</p>
<p>于是我跟 Claude Code 说：「想加一个<strong>最近 AI 模型和产品动态</strong>……我觉得是核心哎，你帮我看看怎么扩展。」</p>
<p>第二天中午 12:26，最后一个提交推送完毕。从 09-21 14:00 第一个模块提交（<code>5c99dd5</code>）到 09-22 12:26 发布 skill 沉淀（<code>2e1f47c</code>），<strong>22 小时 26 分钟</strong>，6 个功能提交，三个内容模块全部上线：AI 动态 <code>/news</code>、模型竞技场 <code>/arena</code>、产品体验 <code>/products</code>，外加一套录制工作流和发布管线文档，45 个测试全绿。</p>
<p>先给一句话结论：<strong>这次变快的根本原因不是模型聪明，而是第二次造轮子时套路已经定型——我的角色从「写代码的人」变成了「把关的人」，把关比写代码舒服，也更重要。</strong></p>
<p><img src="https://oss.justin3go.com/blogs/TODO-three-modules-architecture.png" alt="三模块四件套架构图：数据 TS 文件、纯函数 utils、md 页面、录入 skill 的复用关系"></p>
<h2 id="一、三个模块-一个套路" tabindex="-1">一、三个模块，一个套路 <a class="header-anchor" href="#一、三个模块-一个套路" aria-label="Permalink to &quot;一、三个模块，一个套路&quot;">&ZeroWidthSpace;</a></h2>
<p>三个模块需求是分三次提出的，而且一次比一次随意——第一次正经做了产品决策问卷，第三次只有一句「想再加个最新的 AI 产品/产品体验」。但实现速度反而一次比一次快，因为<strong>第二个模块开始，架构就固定成了四件套</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>是什么</th>
<th>例子</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td>数据文件</td>
<td>纯 TS 模块，schema + 数据</td>
<td><code>theme/news.ts</code></td>
<td>单一事实源，Claude 更新它，页面读它</td>
</tr>
<tr>
<td>纯函数 utils</td>
<td>排序/分组/标签</td>
<td><code>utils/newsList.ts</code></td>
<td>无副作用，node:test 直接测</td>
</tr>
<tr>
<td>md 页面</td>
<td>VitePress 路由 = 文件</td>
<td><code>docs/news.md</code></td>
<td>列表写 md 里，双语镜像各一份</td>
</tr>
<tr>
<td>录入 skill</td>
<td>触发词 + 关卡流程</td>
<td><code>ai-news-updater</code></td>
<td>把「怎么更新」变成一句中文指令</td>
</tr>
</tbody>
</table>
<p>三模块对比：</p>
<table tabindex="0">
<thead>
<tr>
<th>模块</th>
<th>数据形态</th>
<th>入口</th>
<th>更新触发词</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td>AI 动态</td>
<td>事件流，50 条上限</td>
<td>首页区块 + 导航</td>
<td>「更新AI动态」</td>
<td>我搜集主源，你过目表格</td>
</tr>
<tr>
<td>模型竞技场</td>
<td>固定 8 题库 × 场次</td>
<td>导航 + 首页链接</td>
<td>「跑一轮竞技场」</td>
<td>同一套题，不同模型逐字对答</td>
</tr>
<tr>
<td>产品体验</td>
<td>时间流，体验/关注二分</td>
<td>导航</td>
<td>「记录产品体验」</td>
<td>感受必须你口述，绝不代写</td>
</tr>
</tbody>
</table>
<p>这里有两个 VitePress 的坑值得记：一是<strong>列表必须写在 md 里而不是 Vue 组件</strong>，因为 aside 不会动态刷新（vuejs/vitepress#2686），仓库里 blog.md 留了注释，我照抄了；二是数据文件<strong>不能叫 <code>*.data.ts</code></strong>，这个后缀被 createContentLoader 保留，命名错了会撞上构建工具的约定。</p>
<p>第二个模块开始，我的提问方式也变了。第一次我让 Claude 出方案、我从头选；第三次需求只有一句话，它照样先问了三个选择题（形态/入口/种子数据）——但那是产品决策，不是技术方案。实现环节已经完全在复用套路，连带测试和数据完整性断言一起，四十分钟出头交付。</p>
<h2 id="二、竞技场-最难的不是代码-是诚实" tabindex="-1">二、竞技场：最难的不是代码，是诚实 <a class="header-anchor" href="#二、竞技场-最难的不是代码-是诚实" aria-label="Permalink to &quot;二、竞技场：最难的不是代码，是诚实&quot;">&ZeroWidthSpace;</a></h2>
<p>模型竞技场的原始需求是「同一套提示词，不同模型出来答案，类似竞技场」。技术上一晚上就能写完，但真正花时间的是<strong>诚实规则</strong>——一个静态博客做模型横评，最大的风险不是没人看，而是被读者发现在编数据。</p>
<p>三条红线，每条都有机制兜底而不只是口头约定：</p>
<p><strong>1. 答案逐字照录，绝不改写。</strong> 存进 <code>arena.ts</code> 的 answer 字段是模型原始输出，连标点都不动。用户粘贴的答案只允许去尾随空白。</p>
<p><strong>2. 绝不代写没跑过的模型。</strong> schema 里没有「空槽」概念——Gemini 没跑就缺席，不占位不虚构。</p>
<p><strong>3. 模型名如实标注。</strong> 这条有个反直觉的细节：Claude Code 会话里的底层模型不一定是 Claude，skill 里专门写了规则「<strong>never assume the session model is Claude</strong>」——标签必须问过桥或配置才写。我们那场种子对决标注的是 GPT-5.6（Codex 桥自报）和 GLM-5.2（会话实际模型）。</p>
<p>防线也是三层：测试断言（<code>tests/arena-list.test.mjs</code> 校验题库唯一性、无重复模型+日期、枚举合法）→ skill 规则（原题<strong>字节级复制</strong>下发，不许手打）→ 关卡表格里<strong>展示完整逐字答案</strong>供人工否决。</p>
<p>种子场选了道逻辑题（谁打碎了花瓶），两家走出了完全不同的路：</p>
<table tabindex="0">
<thead>
<tr>
<th>模型</th>
<th>推理路径</th>
<th>篇幅</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPT-5.6</td>
<td>穷举三种假设逐一验证</td>
<td>14 行</td>
<td>老实人的笨办法，滴水不漏</td>
</tr>
<tr>
<td>GLM-5.2</td>
<td>抓乙丙互相矛盾，锁死唯一真话</td>
<td>6 行</td>
<td>取巧，但取对了巧</td>
</tr>
</tbody>
</table>
<p>这恰好是竞技场想看的东西：<strong>同一个正确答案背后，模型的性格差异藏不住</strong>。</p>
<h2 id="三、skills-工作流-把关-不是审批" tabindex="-1">三、Skills 工作流：把关，不是审批 <a class="header-anchor" href="#三、skills-工作流-把关-不是审批" aria-label="Permalink to &quot;三、Skills 工作流：把关，不是审批&quot;">&ZeroWidthSpace;</a></h2>
<p>三个录入 skill（ai-news-updater、arena-recorder、product-recorder）长一个样：触发词 → 读现状 → 搜集/追问 → <strong>表格关卡 → STOP</strong> → 确认后写入 → 自检 → 汇报。</p>
<p>关卡表格是整个工作流的核心。以「更新AI动态」为例，Claude 搜完主源后在对话里出一张表：日期、类型、标题中英、来源、链接、一句话点评中英——然后停住，一个文件都不碰。我扫一眼，删两条、改一句点评，回「确认」，它才动 <code>news.ts</code>。</p>
<p>两条规则让这个流程没有变成走过场：</p>
<ul>
<li><strong>内容关卡通过 ≠ 发布许可。</strong> 表格确认只授权改文件，push 到 release（= 生产部署）永远要单独一句「commit + push」。有一天我看完了表格说确认，然后去吃饭，回来发现一切安静——文件改好了，没推送，等我发话。这是对的。</li>
<li><strong>代写红线写进 skill 而不是靠自觉。</strong> product-recorder 里写着：experience 条目的感受「必须来自用户口述，可以追问，绝不代写；没有感受就先记 watch」。首篇产品文章（Claude 体验）就是这么来的——我确认的那版感受，其实基于这周真实发生的事：博客就是用 Claude Code 改的，git 历史可查。</li>
</ul>
<p>有意思的是角色变化：三个 skill 都是我没预先设计的情况下，由 Claude 在实现模块时<strong>顺手提议并沉淀</strong>的。它发现每个模块都需要一条「以后怎么更新」的路径，于是第三次直接默认带上。工具在替我管理工具。</p>
<h2 id="四、发布管线-从想法到-justin3go-com" tabindex="-1">四、发布管线：从想法到 justin3go.com <a class="header-anchor" href="#四、发布管线-从想法到-justin3go-com" aria-label="Permalink to &quot;四、发布管线：从想法到 justin3go.com&quot;">&ZeroWidthSpace;</a></h2>
<p>最后一块拼图是把发布流程本身也写成 skill（site-publisher），因为「从文章到上线」这条路值得一条命令讲清楚：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>docs/**/*.md + 主题代码</span></span>
<span class="line"><span>  → npm test / docs:build 本地验证（45 个测试兜底）</span></span>
<span class="line"><span>  → dev 分支 commit（feature 分支 --ff-only 并回）</span></span>
<span class="line"><span>  → git push origin dev:release</span></span>
<span class="line"><span>  → Cloudflare Pages 自动构建（跑的就是同一条 docs:build）</span></span>
<span class="line"><span>  → 几分钟后 justin3go.com 生效</span></span></code></pre>
</div><p><img src="https://oss.justin3go.com/blogs/TODO-publish-pipeline.png" alt="发布管线流程图：文件、测试、提交、推送、Cloudflare 构建到线上"></p>
<p>两个细节让它几乎零心智负担：<strong>新页面零注册</strong>——VitePress 路由就是文件路径，cleanUrls 开着，sitemap 和 RSS 在构建时自动收集，加一个模块不需要在任何配置里登记；<strong>本地构建即部署预演</strong>——Cloudflare Pages 跑的和本地 <code>docs:build</code> 是同一条命令，本地过了线上就不会炸。</p>
<p>三天里的三次推送（<code>ef9a558..5c99dd5..0f61c2d..2e1f47c</code>）全部一次过，唯一一次构建失败是我自己写的 YAML frontmatter 里冒号没加引号——十秒修好，跟架构无关。</p>
<h2 id="结论-好在哪、差在哪、谁该学" tabindex="-1">结论：好在哪、差在哪、谁该学 <a class="header-anchor" href="#结论-好在哪、差在哪、谁该学" aria-label="Permalink to &quot;结论：好在哪、差在哪、谁该学&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>好在哪：</strong></p>
<ul>
<li><strong>四件套套路可复制。</strong> 下次想加「论文速递」或「开源项目雷达」，照抄 news 模块的目录结构，四十分钟起步。</li>
<li><strong>诚实有机制。</strong> 逐字照录、来源标注、关卡否决、测试断言——读者可以不信任我的判断，但可以信任数据没被美化。</li>
<li><strong>迭代成本归零。</strong> 「更新AI动态」五个字，换回一次全站内容更新。</li>
</ul>
<p><strong>差在哪：</strong></p>
<ul>
<li><strong>首页产品名是空态。</strong> 没有真实体验的产品不可点（这是设计出来的诚实），但访客第一眼会以为列表没做完。</li>
<li><strong>英文镜像是债。</strong> 三模块的页面双语了，竞技场答案和产品文章还是中文独一份。</li>
<li><strong>检索能力有限。</strong> 50 条新闻、8 道题、6 个产品之后，需要筛选和搜索，目前只有月份分组。</li>
</ul>
<p><strong>你该不该学这套：</strong> 你只是想写博客 → 不值得，WordPress 更省事；你在维护一个持续更新的内容站、且每天跟 AI 对话 → 强烈建议把「更新流程」也当成模块来设计，<strong>数据文件 + skill 触发词的组合，是把重复劳动变成一句话的唯一门槛</strong>。</p>
<h2 id="尾声" tabindex="-1">尾声 <a class="header-anchor" href="#尾声" aria-label="Permalink to &quot;尾声&quot;">&ZeroWidthSpace;</a></h2>
<p>写完这篇的时候，我又看了一眼博客首页。AI 动态区块里躺着六条主源新闻，竞技场里 GPT-5.6 和 GLM-5.2 各自的推理过程并排摆着，产品列表第一个名字终于变成了可点的链接。</p>
<p>三天前那句「我觉得是核心哎」，现在看确实是核心，只是核心不是那三个模块，是说出这句话的成本变得有多低。</p>
<hr>
<p><em>本文全部数据来自本仓库 git 历史（<code>5c99dd5</code>..<code>2e1f47c</code>，2026-09-21 至 09-22）与本地测试记录（45 pass），可复现。</em></p>
]]></content:encoded>
            <author>just@justin3go.com (sam)</author>
        </item>
        <item>
            <title><![CDATA[DeepSeek Harness 深度评测：两天 9 万 star 的「一切皆插件」，是未来还是过度设计？]]></title>
            <link>https://justin3go.com/posts/2026/08/15-deepseek-harness-review</link>
            <guid>https://justin3go.com/posts/2026/08/15-deepseek-harness-review</guid>
            <pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="deepseek-harness-深度评测-两天-9-万-star-的「一切皆插件」-是未来还是过度设计" tabindex="-1">DeepSeek Harness 深度评测：两天 9 万 star 的「一切皆插件」，是未来还是过度设计？ <a class="header-anchor" href="#deepseek-harness-深度评测-两天-9-万-star-的「一切皆插件」-是未来还是过度设计" aria-label="Permalink to &quot;DeepSeek Harness 深度评测：两天 9 万 star 的「一切皆插件」，是未来还是过度设计？&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>DeepSeek 于 2026-08-13 开源的 agent 运行时框架 deepseek-harness（dsh）两天内狂揽 9 万+ star。本文基于对仓库源码的完整探索、与 Pi / Codex CLI / Claude Code / OpenCode 的横向对比，以及对 X、Hacker News 与中文社区数十条真实评价的交叉验证，拆解它「一切皆插件」的 Cordis 内核、Turn/Step 事件化主循环、append-only 会话日志与自我修改工具集等核心原理，并逐条核实社区的好评与质疑：它对日常写代码的人确实「重得没必要」，但为自进化 agent 与多智能体运行时铺设了目前独一份的底层骨架。文末给出明确的选型建议。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="引子-28-小时-9-万-star" tabindex="-1">引子：28 小时，9 万 star <a class="header-anchor" href="#引子-28-小时-9-万-star" aria-label="Permalink to &quot;引子：28 小时，9 万 star&quot;">&ZeroWidthSpace;</a></h2>
<p>2026 年 8 月 13 日，DeepSeek 开源了一个叫 <strong>deepseek-harness</strong>（命令行名 <code>dsh</code>）的项目。约 12 小时破 5 万 star，28 小时约 9.2 万——作为参照，此前的增速纪录保持者 OpenClaw 花了 84 天才到 20 万。</p>
<p>热度是真的，争议也是真的。我的时间线上同时出现了两种声音：</p>
<ul>
<li>一边说这是「Agent OS 的雏形」「其他方案完全没有的底层骨架」；</li>
<li>另一边说它「重得毫无必要」「为了自进化，这盘醋包了一整盘饺子」。</li>
</ul>
<p>这两种说法居然都有仔细读过源码的人在背书。所以这篇文章我做了三件事：把仓库完整翻了一遍、把它和 Pi / Codex CLI / Claude Code / OpenCode 逐项对比、再把 X（Twitter）、Hacker News 和中文社区里有实质内容的评价收集起来，逐条对照代码验证——看看大家说得到底有没有道理。</p>
<p>先给一句话结论：<strong>dsh 不太像「DeepSeek 版 Claude Code」，更像一场关于「agent 运行时应该长什么样」的激进实验。它今天不适合大多数只想写代码的人，但它赌的方向值得所有做 agent 基础设施的人认真看一眼。</strong></p>
<h2 id="一、它到底是什么" tabindex="-1">一、它到底是什么 <a class="header-anchor" href="#一、它到底是什么" aria-label="Permalink to &quot;一、它到底是什么&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-1-基本档案" tabindex="-1">1.1 基本档案 <a class="header-anchor" href="#_1-1-基本档案" aria-label="Permalink to &quot;1.1 基本档案&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>定位</strong>：开源 agent 运行时框架（harness / runtime），CLI、Web UI、自动化 server 都只是搭在它上面的「组合形态」</li>
<li><strong>发布</strong>：2026-08-13，当前版本 <code>0.1.0-rc.5</code>，明确标注 developer preview，README 原话是「THERE WILL BE COMPATIBILITY-BREAKING CHANGES」（一定会有破坏兼容性的变更）</li>
<li><strong>协议</strong>：MIT，完全开源</li>
<li><strong>技术栈</strong>：TypeScript monorepo（57 个包组，约 50 万行 TS 代码），Linux 沙箱底层有约 300 行 C11</li>
<li><strong>模型支持</strong>：不锁定 DeepSeek 自家模型，通过插件适配器支持约 40 个 provider——OpenAI、Anthropic、Google、Kimi 以及任意 OpenAI 兼容端点，写几行 YAML 配置就能接入</li>
<li><strong>规模</strong>（发布两天后，GitHub API 实测）：92,700+ star / 8,400+ fork</li>
</ul>
<p>一个容易被忽略的背景：DeepSeek 此前发布模型跑分（如 DeepSWE）时曾被质疑「厂商自报、无法复现」，官方当时承诺「即将开源评测用的 harness」。多方信息显示，dsh 很可能就是在兑现这个承诺——它是 DeepSeek 内部给自家模型跑 agentic benchmark 的那套框架。这解释了仓库为什么一发布就如此完整：22 位贡献者、头部贡献者数千次提交，显然内部已经迭代了很久。</p>
<h3 id="_1-2-核心理念-一切皆插件" tabindex="-1">1.2 核心理念：一切皆插件 <a class="header-anchor" href="#_1-2-核心理念-一切皆插件" aria-label="Permalink to &quot;1.2 核心理念：一切皆插件&quot;">&ZeroWidthSpace;</a></h3>
<p>大多数 coding agent 的做法是：先写一个核心的 agent 循环（收输入 → 调模型 → 执行工具 → 循环），再在外围留一些扩展点（比如 MCP、skills）。核心是特权代码，扩展是二等公民。</p>
<p>dsh 把这个结构倒了过来。它的地基是一个叫 <strong>Cordis</strong> 的通用插件框架（设计源自论文《A Programming Paradigm for Spatiotemporal Composability》，此前已在聊天机器人框架 Koishi 中用了四年），然后——</p>
<p><strong>模型适配器是插件，工具注册表是插件，会话存储是插件，沙箱是插件，UI 是插件，连 agent 主循环本身也是插件。</strong></p>
<p>这不是宣传话术，源码里 agent loop 就是一个普通的包（<code>packages/core/agent-loop</code>），和其他插件一样可以被配置替换。一个插件的最小形态就是一个导出 <code>apply(ctx)</code> 函数的文件，在 YAML 里声明一行就能挂载：</p>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Context } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@deepseek-ai/cordis'</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'hello'</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'hello from my first plugin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>官方提供四种预设的运行模式：<strong>标准模式</strong>（全功能编码 agent）、<strong>代码模式</strong>（TypeScript SDK 编排）、<strong>最小模式</strong>（只有 shell + 文件编辑器，用于跑 benchmark）、<strong>创意模式</strong>（运行时检查与插件实验）。但这四种只是官方给的四种「拼法」——内测开发者 Jiayuan Zhang 的比喻很贴切：dsh 像一套<strong>乐高汽车玩具</strong>，官方预置只是说明书上的推荐拼法之一。</p>
<h2 id="二、核心原理拆解" tabindex="-1">二、核心原理拆解 <a class="header-anchor" href="#二、核心原理拆解" aria-label="Permalink to &quot;二、核心原理拆解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_2-1-事件化的主循环-turn-与-step" tabindex="-1">2.1 事件化的主循环：Turn 与 Step <a class="header-anchor" href="#_2-1-事件化的主循环-turn-与-step" aria-label="Permalink to &quot;2.1 事件化的主循环：Turn 与 Step&quot;">&ZeroWidthSpace;</a></h3>
<p>dsh 的主循环是标准的 ReAct 风格（模型思考 → 调工具 → 看结果 → 再思考），但它被拆成了一组事件而不是一个硬编码的 while 循环：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>turn/start → agent/pre-step → step/start</span></span>
<span class="line"><span>  → system-prompt/assemble（拼装提示词与工具 schema）</span></span>
<span class="line"><span>  → agent/request → llm/stream → assistant/message</span></span>
<span class="line"><span>  → tools/pre-execute → tools/execute → tools/post-execute</span></span>
<span class="line"><span>  → step/end → agent/turn-stopping → turn/end</span></span></code></pre>
</div><p>一个 <strong>step</strong> 是「一次模型请求 + 它触发的工具调用」，一个 <strong>turn</strong> 是从接收输入到所有事情做完为止的零到多个 step。关键在于：几乎每个环节都是可拦截的事件——插件可以在 <code>agent/pre-step</code> 改写消息甚至拒绝执行，可以在 <code>tools/post-execute</code> 替换工具结果。循环不是框架的私有财产，而是一个所有插件都能参与的公共协议。</p>
<p>这个设计的直接后果是：<strong>想把单 agent 换成多 agent 协作架构，只需要换掉 loop 插件，不用 fork 整个项目</strong>。对比一下，想给 Codex CLI 换主循环，你得去改它的 Rust 核心代码。</p>
<h3 id="_2-2-append-only-会话日志-模型看到的一切都有账" tabindex="-1">2.2 Append-only 会话日志：模型看到的一切都有账 <a class="header-anchor" href="#_2-2-append-only-会话日志-模型看到的一切都有账" aria-label="Permalink to &quot;2.2 Append-only 会话日志：模型看到的一切都有账&quot;">&ZeroWidthSpace;</a></h3>
<p>dsh 有一条运行时强制的不变量：「<strong>Model-visible means logged</strong>」——任何进入模型请求的内容，都必须能从会话日志中重建。会话日志是仅追加（append-only）的事件流，模型可见的对话历史是从日志「投影」出来的，支持恢复、分叉（fork）、搜索和重放。</p>
<p>这在 Hacker News 上被一些人称为 killer feature：当美国厂商越来越倾向于加密推理过程、让 trace 难以审计时，dsh 把「完整可追溯」做成了架构级保证。极客公园的实测也证实了体验：系统提示词、思维链、每次工具调用与返回结果全程留痕。</p>
<p>上下文压缩（compaction）也不是循环内置的黑盒，而是独立插件：先对超预算的工具结果做裁剪，撑不住了再生成摘要节点替换一段历史，整个过程用三个日志事件加锁，崩溃了都能从日志里看出来。</p>
<h3 id="_2-3-三个「别家没有」的设计" tabindex="-1">2.3 三个「别家没有」的设计 <a class="header-anchor" href="#_2-3-三个「别家没有」的设计" aria-label="Permalink to &quot;2.3 三个「别家没有」的设计&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>① Code Mode（<code>run_code</code>）</strong>：让模型直接写一段 TypeScript，用 <code>await tools.name(args)</code> 的方式批量调用工具，只有 <code>print</code>/<code>return</code> 的内容才回传给模型。这是对「零散 tool call 疯狂消耗上下文」问题的一种解法——十几次工具往返变成一段代码一次执行。</p>
<p><strong>② 子 agent 可以委托给竞争对手</strong>：dsh 的子 agent 后端（<code>ctx.subagents</code>）支持多种 provider，其中赫然包括 <code>claude-code</code> 和 <code>codex</code>——也就是说你可以在 dsh 里派一个子任务，实际执行者是 Claude Code 或 Codex CLI。这种「harness 中立」的姿态在竞品里没有先例。</p>
<p><strong>③ 自我修改工具集（<code>cordis_*</code>）</strong>：agent 可以在运行时检查自己的插件树、现场写一个新插件并挂载使用。这就是「自进化 agent」的雏形——不过要泼两盆冷水：它默认<strong>不在任何官方组合中</strong>，需要显式开启；而且现场写的插件只存在于内存，重启就没了，还不能永久沉淀。</p>
<h3 id="_2-4-沙箱与权限-该严的地方是严的" tabindex="-1">2.4 沙箱与权限：该严的地方是严的 <a class="header-anchor" href="#_2-4-沙箱与权限-该严的地方是严的" aria-label="Permalink to &quot;2.4 沙箱与权限：该严的地方是严的&quot;">&ZeroWidthSpace;</a></h3>
<p>安全设计上 dsh 并不含糊：Linux 用 bwrap + Landlock（配自研的 C 语言启动器，fail-closed），macOS 用 Seatbelt，Windows 用 ACL 受限令牌；审批模型是封闭枚举，任何异常一律按「不可用」拒绝处理，不会静默放行。它甚至诚实地区分了沙箱的「完整」和「部分」两种强制力状态——老内核的 Landlock 只能算 partial，不会谎报成 full。</p>
<p>但要注意一个层面的错位：<strong>沙箱管的是工具执行，插件本身是跑在 harness 进程内的</strong>。36氪的实测明确指出，任意插件可以访问 shell 和文件系统——装第三方插件的信任模型，目前基本靠自觉。</p>
<h3 id="_2-5-一个彩蛋-用-harness-构建-harness" tabindex="-1">2.5 一个彩蛋：用 harness 构建 harness <a class="header-anchor" href="#_2-5-一个彩蛋-用-harness-构建-harness" aria-label="Permalink to &quot;2.5 一个彩蛋：用 harness 构建 harness&quot;">&ZeroWidthSpace;</a></h3>
<p>仓库里最让我震撼的不是代码，是 <code>.agents/</code> 目录下的 <strong>1,386 篇 Agent Notes</strong>——按「已实现/已否决/已归档/提议中」分类的架构决策记录，加上公开的事后复盘文档。文档总量约 17 万行，几乎和主代码同一数量级，且文档中的类型片段会被 CI 自动与源码比对防止漂移，单文件 100% 测试覆盖率是硬性门禁。</p>
<p>这些痕迹强烈暗示：这个仓库本身就是大量由 AI agent 参与设计、审查、复盘构建出来的。dsh 是它自己理念的第一个用户。</p>
<h2 id="三、横向对比-四条路线" tabindex="-1">三、横向对比：四条路线 <a class="header-anchor" href="#三、横向对比-四条路线" aria-label="Permalink to &quot;三、横向对比：四条路线&quot;">&ZeroWidthSpace;</a></h2>
<p>当下的 coding agent harness 生态，大致是四条路线（star 数为 2026-08-15 GitHub API 实测）：</p>
<p>| 项目 | 路线 | 协议 | Star | 一句话 |
|</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="deepseek-harness-深度评测-两天-9-万-star-的「一切皆插件」-是未来还是过度设计" tabindex="-1">DeepSeek Harness 深度评测：两天 9 万 star 的「一切皆插件」，是未来还是过度设计？ <a class="header-anchor" href="#deepseek-harness-深度评测-两天-9-万-star-的「一切皆插件」-是未来还是过度设计" aria-label="Permalink to &quot;DeepSeek Harness 深度评测：两天 9 万 star 的「一切皆插件」，是未来还是过度设计？&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>DeepSeek 于 2026-08-13 开源的 agent 运行时框架 deepseek-harness（dsh）两天内狂揽 9 万+ star。本文基于对仓库源码的完整探索、与 Pi / Codex CLI / Claude Code / OpenCode 的横向对比，以及对 X、Hacker News 与中文社区数十条真实评价的交叉验证，拆解它「一切皆插件」的 Cordis 内核、Turn/Step 事件化主循环、append-only 会话日志与自我修改工具集等核心原理，并逐条核实社区的好评与质疑：它对日常写代码的人确实「重得没必要」，但为自进化 agent 与多智能体运行时铺设了目前独一份的底层骨架。文末给出明确的选型建议。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="引子-28-小时-9-万-star" tabindex="-1">引子：28 小时，9 万 star <a class="header-anchor" href="#引子-28-小时-9-万-star" aria-label="Permalink to &quot;引子：28 小时，9 万 star&quot;">&ZeroWidthSpace;</a></h2>
<p>2026 年 8 月 13 日，DeepSeek 开源了一个叫 <strong>deepseek-harness</strong>（命令行名 <code>dsh</code>）的项目。约 12 小时破 5 万 star，28 小时约 9.2 万——作为参照，此前的增速纪录保持者 OpenClaw 花了 84 天才到 20 万。</p>
<p>热度是真的，争议也是真的。我的时间线上同时出现了两种声音：</p>
<ul>
<li>一边说这是「Agent OS 的雏形」「其他方案完全没有的底层骨架」；</li>
<li>另一边说它「重得毫无必要」「为了自进化，这盘醋包了一整盘饺子」。</li>
</ul>
<p>这两种说法居然都有仔细读过源码的人在背书。所以这篇文章我做了三件事：把仓库完整翻了一遍、把它和 Pi / Codex CLI / Claude Code / OpenCode 逐项对比、再把 X（Twitter）、Hacker News 和中文社区里有实质内容的评价收集起来，逐条对照代码验证——看看大家说得到底有没有道理。</p>
<p>先给一句话结论：<strong>dsh 不太像「DeepSeek 版 Claude Code」，更像一场关于「agent 运行时应该长什么样」的激进实验。它今天不适合大多数只想写代码的人，但它赌的方向值得所有做 agent 基础设施的人认真看一眼。</strong></p>
<h2 id="一、它到底是什么" tabindex="-1">一、它到底是什么 <a class="header-anchor" href="#一、它到底是什么" aria-label="Permalink to &quot;一、它到底是什么&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-1-基本档案" tabindex="-1">1.1 基本档案 <a class="header-anchor" href="#_1-1-基本档案" aria-label="Permalink to &quot;1.1 基本档案&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>定位</strong>：开源 agent 运行时框架（harness / runtime），CLI、Web UI、自动化 server 都只是搭在它上面的「组合形态」</li>
<li><strong>发布</strong>：2026-08-13，当前版本 <code>0.1.0-rc.5</code>，明确标注 developer preview，README 原话是「THERE WILL BE COMPATIBILITY-BREAKING CHANGES」（一定会有破坏兼容性的变更）</li>
<li><strong>协议</strong>：MIT，完全开源</li>
<li><strong>技术栈</strong>：TypeScript monorepo（57 个包组，约 50 万行 TS 代码），Linux 沙箱底层有约 300 行 C11</li>
<li><strong>模型支持</strong>：不锁定 DeepSeek 自家模型，通过插件适配器支持约 40 个 provider——OpenAI、Anthropic、Google、Kimi 以及任意 OpenAI 兼容端点，写几行 YAML 配置就能接入</li>
<li><strong>规模</strong>（发布两天后，GitHub API 实测）：92,700+ star / 8,400+ fork</li>
</ul>
<p>一个容易被忽略的背景：DeepSeek 此前发布模型跑分（如 DeepSWE）时曾被质疑「厂商自报、无法复现」，官方当时承诺「即将开源评测用的 harness」。多方信息显示，dsh 很可能就是在兑现这个承诺——它是 DeepSeek 内部给自家模型跑 agentic benchmark 的那套框架。这解释了仓库为什么一发布就如此完整：22 位贡献者、头部贡献者数千次提交，显然内部已经迭代了很久。</p>
<h3 id="_1-2-核心理念-一切皆插件" tabindex="-1">1.2 核心理念：一切皆插件 <a class="header-anchor" href="#_1-2-核心理念-一切皆插件" aria-label="Permalink to &quot;1.2 核心理念：一切皆插件&quot;">&ZeroWidthSpace;</a></h3>
<p>大多数 coding agent 的做法是：先写一个核心的 agent 循环（收输入 → 调模型 → 执行工具 → 循环），再在外围留一些扩展点（比如 MCP、skills）。核心是特权代码，扩展是二等公民。</p>
<p>dsh 把这个结构倒了过来。它的地基是一个叫 <strong>Cordis</strong> 的通用插件框架（设计源自论文《A Programming Paradigm for Spatiotemporal Composability》，此前已在聊天机器人框架 Koishi 中用了四年），然后——</p>
<p><strong>模型适配器是插件，工具注册表是插件，会话存储是插件，沙箱是插件，UI 是插件，连 agent 主循环本身也是插件。</strong></p>
<p>这不是宣传话术，源码里 agent loop 就是一个普通的包（<code>packages/core/agent-loop</code>），和其他插件一样可以被配置替换。一个插件的最小形态就是一个导出 <code>apply(ctx)</code> 函数的文件，在 YAML 里声明一行就能挂载：</p>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Context } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@deepseek-ai/cordis'</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'hello'</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'hello from my first plugin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>官方提供四种预设的运行模式：<strong>标准模式</strong>（全功能编码 agent）、<strong>代码模式</strong>（TypeScript SDK 编排）、<strong>最小模式</strong>（只有 shell + 文件编辑器，用于跑 benchmark）、<strong>创意模式</strong>（运行时检查与插件实验）。但这四种只是官方给的四种「拼法」——内测开发者 Jiayuan Zhang 的比喻很贴切：dsh 像一套<strong>乐高汽车玩具</strong>，官方预置只是说明书上的推荐拼法之一。</p>
<h2 id="二、核心原理拆解" tabindex="-1">二、核心原理拆解 <a class="header-anchor" href="#二、核心原理拆解" aria-label="Permalink to &quot;二、核心原理拆解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_2-1-事件化的主循环-turn-与-step" tabindex="-1">2.1 事件化的主循环：Turn 与 Step <a class="header-anchor" href="#_2-1-事件化的主循环-turn-与-step" aria-label="Permalink to &quot;2.1 事件化的主循环：Turn 与 Step&quot;">&ZeroWidthSpace;</a></h3>
<p>dsh 的主循环是标准的 ReAct 风格（模型思考 → 调工具 → 看结果 → 再思考），但它被拆成了一组事件而不是一个硬编码的 while 循环：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>turn/start → agent/pre-step → step/start</span></span>
<span class="line"><span>  → system-prompt/assemble（拼装提示词与工具 schema）</span></span>
<span class="line"><span>  → agent/request → llm/stream → assistant/message</span></span>
<span class="line"><span>  → tools/pre-execute → tools/execute → tools/post-execute</span></span>
<span class="line"><span>  → step/end → agent/turn-stopping → turn/end</span></span></code></pre>
</div><p>一个 <strong>step</strong> 是「一次模型请求 + 它触发的工具调用」，一个 <strong>turn</strong> 是从接收输入到所有事情做完为止的零到多个 step。关键在于：几乎每个环节都是可拦截的事件——插件可以在 <code>agent/pre-step</code> 改写消息甚至拒绝执行，可以在 <code>tools/post-execute</code> 替换工具结果。循环不是框架的私有财产，而是一个所有插件都能参与的公共协议。</p>
<p>这个设计的直接后果是：<strong>想把单 agent 换成多 agent 协作架构，只需要换掉 loop 插件，不用 fork 整个项目</strong>。对比一下，想给 Codex CLI 换主循环，你得去改它的 Rust 核心代码。</p>
<h3 id="_2-2-append-only-会话日志-模型看到的一切都有账" tabindex="-1">2.2 Append-only 会话日志：模型看到的一切都有账 <a class="header-anchor" href="#_2-2-append-only-会话日志-模型看到的一切都有账" aria-label="Permalink to &quot;2.2 Append-only 会话日志：模型看到的一切都有账&quot;">&ZeroWidthSpace;</a></h3>
<p>dsh 有一条运行时强制的不变量：「<strong>Model-visible means logged</strong>」——任何进入模型请求的内容，都必须能从会话日志中重建。会话日志是仅追加（append-only）的事件流，模型可见的对话历史是从日志「投影」出来的，支持恢复、分叉（fork）、搜索和重放。</p>
<p>这在 Hacker News 上被一些人称为 killer feature：当美国厂商越来越倾向于加密推理过程、让 trace 难以审计时，dsh 把「完整可追溯」做成了架构级保证。极客公园的实测也证实了体验：系统提示词、思维链、每次工具调用与返回结果全程留痕。</p>
<p>上下文压缩（compaction）也不是循环内置的黑盒，而是独立插件：先对超预算的工具结果做裁剪，撑不住了再生成摘要节点替换一段历史，整个过程用三个日志事件加锁，崩溃了都能从日志里看出来。</p>
<h3 id="_2-3-三个「别家没有」的设计" tabindex="-1">2.3 三个「别家没有」的设计 <a class="header-anchor" href="#_2-3-三个「别家没有」的设计" aria-label="Permalink to &quot;2.3 三个「别家没有」的设计&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>① Code Mode（<code>run_code</code>）</strong>：让模型直接写一段 TypeScript，用 <code>await tools.name(args)</code> 的方式批量调用工具，只有 <code>print</code>/<code>return</code> 的内容才回传给模型。这是对「零散 tool call 疯狂消耗上下文」问题的一种解法——十几次工具往返变成一段代码一次执行。</p>
<p><strong>② 子 agent 可以委托给竞争对手</strong>：dsh 的子 agent 后端（<code>ctx.subagents</code>）支持多种 provider，其中赫然包括 <code>claude-code</code> 和 <code>codex</code>——也就是说你可以在 dsh 里派一个子任务，实际执行者是 Claude Code 或 Codex CLI。这种「harness 中立」的姿态在竞品里没有先例。</p>
<p><strong>③ 自我修改工具集（<code>cordis_*</code>）</strong>：agent 可以在运行时检查自己的插件树、现场写一个新插件并挂载使用。这就是「自进化 agent」的雏形——不过要泼两盆冷水：它默认<strong>不在任何官方组合中</strong>，需要显式开启；而且现场写的插件只存在于内存，重启就没了，还不能永久沉淀。</p>
<h3 id="_2-4-沙箱与权限-该严的地方是严的" tabindex="-1">2.4 沙箱与权限：该严的地方是严的 <a class="header-anchor" href="#_2-4-沙箱与权限-该严的地方是严的" aria-label="Permalink to &quot;2.4 沙箱与权限：该严的地方是严的&quot;">&ZeroWidthSpace;</a></h3>
<p>安全设计上 dsh 并不含糊：Linux 用 bwrap + Landlock（配自研的 C 语言启动器，fail-closed），macOS 用 Seatbelt，Windows 用 ACL 受限令牌；审批模型是封闭枚举，任何异常一律按「不可用」拒绝处理，不会静默放行。它甚至诚实地区分了沙箱的「完整」和「部分」两种强制力状态——老内核的 Landlock 只能算 partial，不会谎报成 full。</p>
<p>但要注意一个层面的错位：<strong>沙箱管的是工具执行，插件本身是跑在 harness 进程内的</strong>。36氪的实测明确指出，任意插件可以访问 shell 和文件系统——装第三方插件的信任模型，目前基本靠自觉。</p>
<h3 id="_2-5-一个彩蛋-用-harness-构建-harness" tabindex="-1">2.5 一个彩蛋：用 harness 构建 harness <a class="header-anchor" href="#_2-5-一个彩蛋-用-harness-构建-harness" aria-label="Permalink to &quot;2.5 一个彩蛋：用 harness 构建 harness&quot;">&ZeroWidthSpace;</a></h3>
<p>仓库里最让我震撼的不是代码，是 <code>.agents/</code> 目录下的 <strong>1,386 篇 Agent Notes</strong>——按「已实现/已否决/已归档/提议中」分类的架构决策记录，加上公开的事后复盘文档。文档总量约 17 万行，几乎和主代码同一数量级，且文档中的类型片段会被 CI 自动与源码比对防止漂移，单文件 100% 测试覆盖率是硬性门禁。</p>
<p>这些痕迹强烈暗示：这个仓库本身就是大量由 AI agent 参与设计、审查、复盘构建出来的。dsh 是它自己理念的第一个用户。</p>
<h2 id="三、横向对比-四条路线" tabindex="-1">三、横向对比：四条路线 <a class="header-anchor" href="#三、横向对比-四条路线" aria-label="Permalink to &quot;三、横向对比：四条路线&quot;">&ZeroWidthSpace;</a></h2>
<p>当下的 coding agent harness 生态，大致是四条路线（star 数为 2026-08-15 GitHub API 实测）：</p>
<table tabindex="0">
<thead>
<tr>
<th>项目</th>
<th>路线</th>
<th>协议</th>
<th>Star</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Claude Code</strong></td>
<td>分层可扩展生态</td>
<td>源码可见，非标准开源协议</td>
<td>141k</td>
<td>Skills/Hooks/Subagents/MCP/Plugins 五件套，生态最全，也最「重」</td>
</tr>
<tr>
<td><strong>OpenAI Codex CLI</strong></td>
<td>Rust + 内核级沙箱</td>
<td>Apache 2.0</td>
<td>106k</td>
<td>安全性公认领先，声明式扩展（文件夹即插件）</td>
</tr>
<tr>
<td><strong>Pi</strong>（Earendil）</td>
<td>极简主义</td>
<td>MIT</td>
<td>90k</td>
<td>默认只给模型 4 个工具，系统提示词不到 1000 token，OpenClaw 的底层引擎</td>
</tr>
<tr>
<td><strong>deepseek-harness</strong></td>
<td>运行时插件化</td>
<td>MIT</td>
<td>93k</td>
<td>一切皆插件，连 loop 都能换</td>
</tr>
</tbody>
</table>
<p>（另有完全开源的 OpenCode 197k star、Gemini CLI 107k、Aider 48k，路线上分别接近 Claude Code、Codex 和结对编程工具，不展开。）</p>
<h3 id="_3-1-与-codex-声明式-vs-命令式" tabindex="-1">3.1 与 Codex：声明式 vs 命令式 <a class="header-anchor" href="#_3-1-与-codex-声明式-vs-命令式" aria-label="Permalink to &quot;3.1 与 Codex：声明式 vs 命令式&quot;">&ZeroWidthSpace;</a></h3>
<p>这是社区里质量最高的一场技术辩论，来自逐行对照过两边源码的开发者 grapeot：</p>
<ul>
<li><strong>Codex 走声明式</strong>：插件就是磁盘上的文件夹（Markdown skill、MCP 配置、shell 脚本），不进 harness 进程。改配置重启进程只要 2-3 秒，门槛接近零。</li>
<li><strong>dsh 走命令式</strong>：插件带着状态直接跑在 harness 进程内，互相注册调用。运行时热替换插件要处理悬空引用、后台任务终止、依赖链协同、崩溃回滚——为此引入了 Cordis 这个重型运行时，仅管理插件生命周期的核心模块就有 750 行。</li>
</ul>
<p>用装修打个比方：Codex 给你的是精装房加一面可以随便挂东西的洞洞板，挂错了摘下来重挂就行；dsh 给你的是可以在<strong>不断水断电的情况下改承重结构</strong>的房子。问题是——你多久需要改一次承重结构？</p>
<h3 id="_3-2-与-pi-两个极端" tabindex="-1">3.2 与 Pi：两个极端 <a class="header-anchor" href="#_3-2-与-pi-两个极端" aria-label="Permalink to &quot;3.2 与 Pi：两个极端&quot;">&ZeroWidthSpace;</a></h3>
<p>Pi 是光谱的另一端：libGDX 作者 Mario Zechner 因为受不了 Claude Code 的复杂度膨胀而做的极简 agent，4 个默认工具，哲学是「留白比添加更重要」。有意思的是两者并非对立——dsh 的多 provider 模型适配层直接用了 Pi 生态的 <code>@earendil-works/pi-ai</code> 库，某种意义上 dsh 是「在 Pi 的模型层上盖了一座插件大厦」。</p>
<p>而 token 效率的对比很残酷：有开发者初步实测，同模型下 Pi 的未缓存输入约 4.5K token，dsh 约 47.6K，差距十倍量级（测试者自己也标注了存在干扰因素、dsh 还是预览版）。极简与全插件化的代价差异，在账单上是肉眼可见的。</p>
<h3 id="_3-3-与-claude-code-打的不是产品-是商业模式" tabindex="-1">3.3 与 Claude Code：打的不是产品，是商业模式 <a class="header-anchor" href="#_3-3-与-claude-code-打的不是产品-是商业模式" aria-label="Permalink to &quot;3.3 与 Claude Code：打的不是产品，是商业模式&quot;">&ZeroWidthSpace;</a></h3>
<p>论今天的产品完成度，dsh 和 Claude Code 不在一个量级——连提前一个月内测的开发者都直说「作为 Coding Agent 用，体验确实不如 Claude Code / Codex 完善」。但 36氪的分析点出了更本质的一层：dsh 以 MIT 协议免费开放全部能力，等于<strong>直接宣布 harness 层不应该收费</strong>，试图把竞争拉回模型能力与定价本身。这一枪打的不是 Claude Code 的功能列表，而是「harness 作为付费壁垒」的商业模式。</p>
<h2 id="四、社区怎么说-以及他们说得对不对" tabindex="-1">四、社区怎么说，以及他们说得对不对 <a class="header-anchor" href="#四、社区怎么说-以及他们说得对不对" aria-label="Permalink to &quot;四、社区怎么说，以及他们说得对不对&quot;">&ZeroWidthSpace;</a></h2>
<p>我把收集到的评价按「是否有代码/实测支撑」过滤后，逐条与仓库实际情况对照。</p>
<h3 id="_4-1-好评-基本属实" tabindex="-1">4.1 好评：基本属实 <a class="header-anchor" href="#_4-1-好评-基本属实" aria-label="Permalink to &quot;4.1 好评：基本属实&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>「插件化架构的工程巧思」「Agent OS 雏形」</strong>——属实。loop 即插件、能力面三角色设计、事务回滚，代码层面都能验证。</li>
<li><strong>「完整可追溯性是 killer feature」</strong>——属实。「Model-visible means logged」是运行时强制的不变量，不是文档修辞。</li>
<li><strong>「自进化雏形」</strong>——属实但被夸大了。<code>cordis_*</code> 工具真实存在，但默认关闭、产物不能持久化，离「自我进化的 agent」还有相当距离。</li>
<li><strong>「生成质量不错」</strong>——爱范儿、极客公园的独立实测都完成得不错（Three.js 小游戏、官网重构，成本约 3 美元）。可信，但样本还少。</li>
</ul>
<h3 id="_4-2-差评-大部分也属实" tabindex="-1">4.2 差评：大部分也属实 <a class="header-anchor" href="#_4-2-差评-大部分也属实" aria-label="Permalink to &quot;4.2 差评：大部分也属实&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>「对日常开发过度设计」</strong>——我认为这是<strong>成立的</strong>。yage.ai 那篇《为了自进化，这盘醋包了一整盘饺子》逐项反驳得很扎实：搜索服务只需简短交互、MCP server 重启只要 2-3 秒、skill 是纯文本不需要框架支持热重载。dsh 解决的「运行时不停机换组件」是真实能力，但对绝大多数场景是罕见需求。甚至有内测者观察到，连 DeepSeek 自家的模型都经常搞不清 plugin 该怎么用，直接改自己代码了事——「毕竟更快、效果也差不多」。</li>
<li><strong>「token 消耗偏高」</strong>——初步实测支持（对 Pi 十倍差距、对其他框架约 3 倍），且有一个已被确认的具体 bug：dsh 会同时读取项目里的 <code>CLAUDE.md</code> 和 <code>AGENTS.md</code>，如果两个文件内容相同（很多项目为了兼容多工具就是这么做的），指令集会被重复注入两遍，system prompt 直接翻倍。截至写稿未见官方修复。</li>
<li><strong>「兼容性差、生态早期」</strong>——属实。官方兼容性列表 41 个兼容 / 219 个待关注或待调研；36氪实测 5 个第三方工具全部失败。插件仓库两天冲到 2000+ 个，但数量不等于质量。</li>
<li><strong>「文档是 word salad」</strong>——部分成立。面向用户的上手文档确实薄，但仓库内部的架构文档有约 17 万行且与代码同步校验——问题不是没文档，是「写给 agent 看的文档」和「写给新手看的文档」完全是两种东西，后者目前缺位。</li>
</ul>
<h3 id="_4-3-benchmark-争议-透明度问题-而非造假实锤" tabindex="-1">4.3 Benchmark 争议：透明度问题，而非造假实锤 <a class="header-anchor" href="#_4-3-benchmark-争议-透明度问题-而非造假实锤" aria-label="Permalink to &quot;4.3 Benchmark 争议：透明度问题，而非造假实锤&quot;">&ZeroWidthSpace;</a></h3>
<p>这部分需要最谨慎地陈述：</p>
<ol>
<li><strong>分数口径混乱</strong>：V4-Pro 的 SWE-bench Verified 有厂商自报 80.6% 与第三方 Vals 评测 96.4% 两个版本，相差 16 个百分点，很可能是不同变体/方法论所致，目前无权威解释。</li>
<li><strong>minimal 模式跑分</strong>：官方 agent benchmark 分数是在 dsh 的最小模式下跑的，「高分反映模型能力还是框架加成」的疑问合理——但换个角度，开源 harness 本身恰恰让「自己复现验证」第一次成为可能。</li>
<li><strong>内部榜单不透明</strong>：官方成绩表里混入了两套非公开的内部 benchmark（DSBench-FullStack 和 DSBench-Hard），且两榜排序互相矛盾。评论者 MaxForAI 的态度值得借鉴：「一个实验室自己造什么 benchmark，其实暴露了它在优化什么。」</li>
<li><strong>尚无人公开复现并证伪任何官方数字</strong>。质疑集中在透明度，不在造假。</li>
</ol>
<p>另外要如实记录：伴随发布的还有 V4-Pro API 涨价（多个独立信源印证），加上部分用户「Pro 写代码不如 Flash」的体感吐槽，构成了一批与 harness 本身无关、但影响舆论的负面情绪。这类「失望三连」式发言普遍缺乏具体使用细节，参考价值有限。</p>
<h2 id="五、结论" tabindex="-1">五、结论 <a class="header-anchor" href="#五、结论" aria-label="Permalink to &quot;五、结论&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="它好在哪" tabindex="-1">它好在哪 <a class="header-anchor" href="#它好在哪" aria-label="Permalink to &quot;它好在哪&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li><strong>架构上真正的新东西</strong>：loop 即插件、运行时整体可替换、子 agent 可委托给竞品——这些能力在 Claude Code / Codex / Pi 里都不存在。</li>
<li><strong>可追溯性做成了架构保证</strong>：对需要审计、回放、研究 agent 行为的团队，这是独一份。</li>
<li><strong>彻底的开放姿态</strong>：MIT 协议、不锁模型、兼容对手的 MCP 命名习惯、冲击 harness 收费模式。</li>
<li><strong>罕见的工程文化展示</strong>：1,386 篇决策笔记、文档与代码同步门禁、「用 harness 构建 harness」的自证，本身就是一份珍贵的 AI 原生工程样本。</li>
</ol>
<h3 id="它差在哪" tabindex="-1">它差在哪 <a class="header-anchor" href="#它差在哪" aria-label="Permalink to &quot;它差在哪&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li><strong>现在就是个预览版</strong>：官方自己承诺会有破坏性变更，接口剧变、不建议生产使用。</li>
<li><strong>为小众需求让所有人买单</strong>：重型运行时带来的复杂度与 token 开销由每个用户承担，而「运行时换 loop」的收益只属于少数探索者。</li>
<li><strong>效率与质量瑕疵</strong>：上下文重复注入 bug、十倍量级的 token 差距、第三方兼容性几乎为零。</li>
<li><strong>信任建设未完成</strong>：benchmark 口径与内部榜单的透明度问题，需要时间和第三方复现来消化。</li>
</ol>
<h3 id="你该不该用它" tabindex="-1">你该不该用它 <a class="header-anchor" href="#你该不该用它" aria-label="Permalink to &quot;你该不该用它&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>你只是想要一个好用的 coding agent 写代码</strong>：不推荐，至少现在不。Claude Code（要生态）、Codex CLI（要安全与稳定）、Pi（要极简与省钱）都是更成熟的选择。</li>
<li><strong>你在做 agent 基础设施、多智能体系统或自进化 agent 研究</strong>：强烈建议花一个周末读它的源码和 <code>.agents/</code> 目录——那套「能力面」设计和事件化 loop，目前没有第二个可运行的参照物。</li>
<li><strong>你的组织需要可审计的 agent 执行记录</strong>：值得关注，append-only 日志是它最没有争议的优点。</li>
<li><strong>你在观望</strong>：给它三到六个月。插件生态会洗牌，接口会稳定，benchmark 会有人复现。届时再看「一切皆插件」是长成了 Agent OS，还是像 HN 上那位评论者担心的那样陷入「插件疲劳」。</li>
</ul>
<p>最后说点感受。看完这个仓库，我想起的不是某个竞品，而是操作系统史：dsh 赌的是「agent 会长成需要一个 OS 的复杂系统」，Pi 赌的是「agent 应该保持一把锋利的小刀」。这两个赌注很可能都对——只是对应的用户不同、时间点不同。而一家模型公司愿意把自己的评测 harness 以 MIT 协议完整摊开、连内部决策记录一起公开，无论最终成败，这件事本身已经改变了行业对「透明」二字的基线。</p>
<hr>
<p><em>本文基于 2026-08-15 的仓库快照（v0.1.0-rc.5）与公开社区讨论写成。dsh 处于快速迭代期，文中具体细节可能很快过时；涉及的 benchmark 数据均标注了来源性质，其中厂商自报与第三方口径的差异尚无定论，请自行判断。主要信源：<a href="https://github.com/deepseek-ai/deepseek-harness" target="_blank" rel="noreferrer">deepseek-ai/deepseek-harness</a>、<a href="https://deepseek.com/harness/en/" target="_blank" rel="noreferrer">DeepSeek Harness 官网</a>、Hacker News 讨论、X 平台多位开发者的源码分析长文，以及爱范儿、极客公园、36氪的独立实测。</em></p>
]]></content:encoded>
            <author>just@justin3go.com (sam)</author>
        </item>
        <item>
            <title><![CDATA[从"你提示 Agent"到"系统提示 Agent"：Loop Engineering 完整拆解]]></title>
            <link>https://justin3go.com/posts/2026/07/08-loop-engineering-from-prompting-to-designing-loops</link>
            <guid>https://justin3go.com/posts/2026/07/08-loop-engineering-from-prompting-to-designing-loops</guid>
            <pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="从-你提示-agent-到-系统提示-agent-loop-engineering-完整拆解" tabindex="-1">从&quot;你提示 Agent&quot;到&quot;系统提示 Agent&quot;：Loop Engineering 完整拆解 <a class="header-anchor" href="#从-你提示-agent-到-系统提示-agent-loop-engineering-完整拆解" aria-label="Permalink to &quot;从&quot;你提示 Agent&quot;到&quot;系统提示 Agent&quot;：Loop Engineering 完整拆解&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>本文沿着 <strong>Prompt → Context → Harness → Loop</strong> 的协作演进，完整拆解 Loop Engineering 的来源、六段控制流、验证器、停止条件与三类护栏。文章结合 Ralph Loop、Claude Code <code>/goal</code> 与 <code>/loop</code>、Codex Automations、Karpathy autoresearch 等实践，说明循环真正的新意并非 <code>while</code> 语句，而是把验证、预算、状态与反馈组织成可持续运行的工程系统。最后给出任务决策矩阵和上线路径，强调循环的价值上限取决于一个无法被 Agent 欺骗的验证器。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="引子-这条链路上-最慢的环节是你" tabindex="-1">引子：这条链路上，最慢的环节是你 <a class="header-anchor" href="#引子-这条链路上-最慢的环节是你" aria-label="Permalink to &quot;引子：这条链路上，最慢的环节是你&quot;">&ZeroWidthSpace;</a></h2>
<p>先看一个 2026 年每天都在发生的场景。</p>
<p>一位工程师打开终端，给 coding agent 发了一条指令：&quot;修一下 CI 上挂掉的测试。&quot; Agent 跑了三分钟，回来汇报。工程师看了一眼，说&quot;不对，你改错文件了，应该看 auth 模块&quot;。Agent 又跑三分钟。工程师再看一眼，&quot;测试过了，但你把另一个用例改坏了&quot;。如此往复五轮，问题修完，前后四十分钟——其中 agent 实际工作十五分钟，剩下二十五分钟，是 agent 在<strong>等这个人读输出、做判断、敲下一条 prompt</strong>。</p>
<p>模型的推理速度在涨，工具的执行速度在涨，唯一没涨的是坐在键盘前的那个人。链路上最慢的环节从模型变成了人。</p>
<p>Claude Code 的创建者 Boris Cherny 在 2026 年 6 月 2 日的 Acquired Unplugged（WorkOS 承办）活动上是这么描述自己的应对方式的：</p>
<blockquote>
<p>&quot;I don't prompt Claude anymore. I have loops that are running. They're the ones that are prompting Claude and figuring out what to do. My job is to write loops.&quot;
（我已经不再给 Claude 写 prompt 了。我有一批循环在跑，是它们在给 Claude 写 prompt、决定下一步做什么。我的工作是写循环。）</p>
</blockquote>
<p>五天后，OpenClaw 作者 Peter Steinberger（现就职 OpenAI）发了那条viral推文：&quot;You shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents.&quot;（你不该再给 coding agent 写 prompt 了，你该设计能给 agent 写 prompt 的循环。）再一天后，Google 工程负责人 Addy Osmani 发表《Loop Engineering》一文，给这套实践正式命了名。</p>
<p>这篇文章不按调研报告的顺序（定义→时间线→批评）走，而是沿着<strong>开发者与 agent 协作链路的演进</strong>这条主线，把 Loop Engineering 拆开讲清楚：这条链路是怎么一层层长出来的、一个循环里面到底有什么、哪些是旧东西的重新包装、哪些是真正的新增量，以及——你该不该在自己的工作里上循环。</p>
<h2 id="一、链路的四次让渡-prompt-→-context-→-harness-→-loop" tabindex="-1">一、链路的四次让渡：Prompt → Context → Harness → Loop <a class="header-anchor" href="#一、链路的四次让渡-prompt-→-context-→-harness-→-loop" aria-label="Permalink to &quot;一、链路的四次让渡：Prompt → Context → Harness → Loop&quot;">&ZeroWidthSpace;</a></h2>
<p>Loop engineering 不是凭空出现的新学科，它是同一条协作链路上的第四次&quot;工作让渡&quot;。每一层的本质，都是人把链路上的一段手工活交给系统，自己往上挪一层。</p>
<p><img src="https://oss.justin3go.com/blogs/four-layer-stack.png" alt="Prompt→Context→Harness→Loop 四层同心嵌套图"></p>
<p>每一层包住前一层，而不是取代前一层——写循环的人依然要写 prompt，只是 prompt 从&quot;你现场敲的话&quot;变成了&quot;循环每一轮自动组装的模板&quot;。</p>
<p>中文社区流传的一个类比把这四层说得很直白：</p>
<blockquote>
<p>&quot;Prompt 是你怎么问他，Context 是你让他看见什么，Harness 是你把他放在什么环境里，Loop 是你让这个系统怎么自己转起来。&quot;</p>
</blockquote>
<p>四层各自的权威出处如下。值得注意的是，这个演进节奏与 Anthropic 工程博客的发布节奏高度吻合——虽然 &quot;loop engineering&quot; 这个词本身至今不是 Anthropic 官方术语：</p>
<p>| 层 | 兴起时间 | 权威出处 | 关键表述 |
|</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="从-你提示-agent-到-系统提示-agent-loop-engineering-完整拆解" tabindex="-1">从&quot;你提示 Agent&quot;到&quot;系统提示 Agent&quot;：Loop Engineering 完整拆解 <a class="header-anchor" href="#从-你提示-agent-到-系统提示-agent-loop-engineering-完整拆解" aria-label="Permalink to &quot;从&quot;你提示 Agent&quot;到&quot;系统提示 Agent&quot;：Loop Engineering 完整拆解&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>本文沿着 <strong>Prompt → Context → Harness → Loop</strong> 的协作演进，完整拆解 Loop Engineering 的来源、六段控制流、验证器、停止条件与三类护栏。文章结合 Ralph Loop、Claude Code <code>/goal</code> 与 <code>/loop</code>、Codex Automations、Karpathy autoresearch 等实践，说明循环真正的新意并非 <code>while</code> 语句，而是把验证、预算、状态与反馈组织成可持续运行的工程系统。最后给出任务决策矩阵和上线路径，强调循环的价值上限取决于一个无法被 Agent 欺骗的验证器。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="引子-这条链路上-最慢的环节是你" tabindex="-1">引子：这条链路上，最慢的环节是你 <a class="header-anchor" href="#引子-这条链路上-最慢的环节是你" aria-label="Permalink to &quot;引子：这条链路上，最慢的环节是你&quot;">&ZeroWidthSpace;</a></h2>
<p>先看一个 2026 年每天都在发生的场景。</p>
<p>一位工程师打开终端，给 coding agent 发了一条指令：&quot;修一下 CI 上挂掉的测试。&quot; Agent 跑了三分钟，回来汇报。工程师看了一眼，说&quot;不对，你改错文件了，应该看 auth 模块&quot;。Agent 又跑三分钟。工程师再看一眼，&quot;测试过了，但你把另一个用例改坏了&quot;。如此往复五轮，问题修完，前后四十分钟——其中 agent 实际工作十五分钟，剩下二十五分钟，是 agent 在<strong>等这个人读输出、做判断、敲下一条 prompt</strong>。</p>
<p>模型的推理速度在涨，工具的执行速度在涨，唯一没涨的是坐在键盘前的那个人。链路上最慢的环节从模型变成了人。</p>
<p>Claude Code 的创建者 Boris Cherny 在 2026 年 6 月 2 日的 Acquired Unplugged（WorkOS 承办）活动上是这么描述自己的应对方式的：</p>
<blockquote>
<p>&quot;I don't prompt Claude anymore. I have loops that are running. They're the ones that are prompting Claude and figuring out what to do. My job is to write loops.&quot;
（我已经不再给 Claude 写 prompt 了。我有一批循环在跑，是它们在给 Claude 写 prompt、决定下一步做什么。我的工作是写循环。）</p>
</blockquote>
<p>五天后，OpenClaw 作者 Peter Steinberger（现就职 OpenAI）发了那条viral推文：&quot;You shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents.&quot;（你不该再给 coding agent 写 prompt 了，你该设计能给 agent 写 prompt 的循环。）再一天后，Google 工程负责人 Addy Osmani 发表《Loop Engineering》一文，给这套实践正式命了名。</p>
<p>这篇文章不按调研报告的顺序（定义→时间线→批评）走，而是沿着<strong>开发者与 agent 协作链路的演进</strong>这条主线，把 Loop Engineering 拆开讲清楚：这条链路是怎么一层层长出来的、一个循环里面到底有什么、哪些是旧东西的重新包装、哪些是真正的新增量，以及——你该不该在自己的工作里上循环。</p>
<h2 id="一、链路的四次让渡-prompt-→-context-→-harness-→-loop" tabindex="-1">一、链路的四次让渡：Prompt → Context → Harness → Loop <a class="header-anchor" href="#一、链路的四次让渡-prompt-→-context-→-harness-→-loop" aria-label="Permalink to &quot;一、链路的四次让渡：Prompt → Context → Harness → Loop&quot;">&ZeroWidthSpace;</a></h2>
<p>Loop engineering 不是凭空出现的新学科，它是同一条协作链路上的第四次&quot;工作让渡&quot;。每一层的本质，都是人把链路上的一段手工活交给系统，自己往上挪一层。</p>
<p><img src="https://oss.justin3go.com/blogs/four-layer-stack.png" alt="Prompt→Context→Harness→Loop 四层同心嵌套图"></p>
<p>每一层包住前一层，而不是取代前一层——写循环的人依然要写 prompt，只是 prompt 从&quot;你现场敲的话&quot;变成了&quot;循环每一轮自动组装的模板&quot;。</p>
<p>中文社区流传的一个类比把这四层说得很直白：</p>
<blockquote>
<p>&quot;Prompt 是你怎么问他，Context 是你让他看见什么，Harness 是你把他放在什么环境里，Loop 是你让这个系统怎么自己转起来。&quot;</p>
</blockquote>
<p>四层各自的权威出处如下。值得注意的是，这个演进节奏与 Anthropic 工程博客的发布节奏高度吻合——虽然 &quot;loop engineering&quot; 这个词本身至今不是 Anthropic 官方术语：</p>
<table tabindex="0">
<thead>
<tr>
<th>层</th>
<th>兴起时间</th>
<th>权威出处</th>
<th>关键表述</th>
</tr>
</thead>
<tbody>
<tr>
<td>Prompt engineering</td>
<td>2022–2024</td>
<td>行业共识，无单一提出者</td>
<td>怎么把任务说对</td>
</tr>
<tr>
<td>Context engineering</td>
<td>2024–2025</td>
<td>Tobi Lütke 推文（2025-06-18，Karpathy 6-25 转发背书）；Anthropic《Effective context engineering for AI agents》（2025-09-29）</td>
<td>&quot;the art of providing all the context for the task to be plausibly solvable by the LLM&quot;</td>
</tr>
<tr>
<td>Harness engineering</td>
<td>2025 年末</td>
<td>Anthropic《Effective harnesses for long-running agents》（2025-11-26，Justin Young 等）</td>
<td>&quot;模型是大脑，挽具是身体&quot;</td>
</tr>
<tr>
<td>Loop engineering</td>
<td>2026-06</td>
<td>Addy Osmani《Loop Engineering》（addyosmani.com/blog/loop-engineering，2026-06-07）</td>
<td>&quot;Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead.&quot;</td>
</tr>
</tbody>
</table>
<p>Osmani 对 loop 的定义值得原文抄录，因为它是后续所有讨论的锚点：</p>
<blockquote>
<p>&quot;A loop here can be thought of a recursive goal where you define a purpose and the AI iterates until complete.&quot;
（这里的循环可以理解为一个递归目标——你定义一个目的，AI 反复迭代直到完成。）</p>
</blockquote>
<h2 id="二、解剖一个循环-六个环节-一个出口" tabindex="-1">二、解剖一个循环：六个环节，一个出口 <a class="header-anchor" href="#二、解剖一个循环-六个环节-一个出口" aria-label="Permalink to &quot;二、解剖一个循环：六个环节，一个出口&quot;">&ZeroWidthSpace;</a></h2>
<p>把&quot;循环&quot;两个字拆开，里面是一套完整的控制流。Anthropic 在《Building agents with the Claude Agent SDK》（2025-09-29，Thariq Shihipar）里给过官方最简版本：&quot;gather context → take action → verify work → repeat&quot;。Loop engineering 语境下的完整版是六个环节：</p>
<p><img src="https://oss.justin3go.com/blogs/six-stage-loop-control-flow.png" alt="循环六环节控制流：DISCOVER→ASSEMBLE→ACT→VERIFY→PERSIST→DECIDE，出口为循环或STOP"></p>
<p>人类在这张图里的位置变了：不再站在 ASSEMBLE 环节里逐轮敲字，而是站在图的外面——设计这六个环节怎么接、STOP 的条件是什么，然后审查 STOP 之后交出来的东西。Geoffrey Huntley（下一节的主角）的说法是：</p>
<blockquote>
<p>&quot;Your job is to sit on the loop, not in it.&quot;
（你的工作是坐在循环之上，而不是循环之中。）</p>
</blockquote>
<h3 id="例程、工作流、循环-三个容易混淆的东西" tabindex="-1">例程、工作流、循环：三个容易混淆的东西 <a class="header-anchor" href="#例程、工作流、循环-三个容易混淆的东西" aria-label="Permalink to &quot;例程、工作流、循环：三个容易混淆的东西&quot;">&ZeroWidthSpace;</a></h3>
<p>不是所有&quot;自动跑的东西&quot;都叫循环。区分标准只有一条：<strong>它会不会检查自己的工作，并据此决定是否继续</strong>。</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>Routine 例程</th>
<th>Workflow 工作流</th>
<th>Loop 循环</th>
</tr>
</thead>
<tbody>
<tr>
<td>步骤</td>
<td>固定不变</td>
<td>按发现分支</td>
<td>动态迭代</td>
</tr>
<tr>
<td>停止条件</td>
<td>步骤走完</td>
<td>路径走完</td>
<td>验证器判定&quot;目标达成&quot;</td>
</tr>
<tr>
<td>自我检查</td>
<td>无</td>
<td>无或很弱</td>
<td>核心机制</td>
</tr>
<tr>
<td>例子</td>
<td>定时跑 lint 并发报告</td>
<td>CI 失败→分类→派单</td>
<td>修到 test/auth 全绿为止</td>
</tr>
</tbody>
</table>
<p>这个区分对应 Anthropic《Building Effective AI Agents》（2024-12-19，Erik Schluntz &amp; Barry Zhang）里 workflow 与 agent 的经典二分：&quot;Workflows 是 LLM 和工具通过预定义代码路径编排的系统；Agents 是 LLM 动态指挥自己流程的系统……本质上就是 LLM 基于环境反馈、在循环中使用工具。&quot;</p>
<h2 id="三、前史-循环不是-2026-年发明的" tabindex="-1">三、前史：循环不是 2026 年发明的 <a class="header-anchor" href="#三、前史-循环不是-2026-年发明的" aria-label="Permalink to &quot;三、前史：循环不是 2026 年发明的&quot;">&ZeroWidthSpace;</a></h2>
<p>如果你觉得上面那张控制流图眼熟——你是对的。它就是控制论里的反馈环，就是恒温器，就是 Kubernetes 的 reconciliation loop。学术界和社区在 &quot;loop engineering&quot; 得名之前，已经把这个东西做了四年：</p>
<p><img src="https://oss.justin3go.com/blogs/agent-loop-prehistory-timeline.png" alt="Agent循环前史时间线：ReAct→Reflexion→AutoGPT→Ralph Loop→正式得名"></p>
<p>ReAct（&quot;ReAct: Synergizing Reasoning and Acting in Language Models&quot;，2022-10-06 提交，后发表于 ICLR 2023）给出了 Thought→Action→Observation 的内层循环；Reflexion（Shinn et al., 2023）加上了自我批评和记忆；AutoGPT 第一次让大众见识了&quot;给个目标就自己跑&quot;——以及它掉进兔子洞出不来的失败模式。</p>
<h3 id="ralph-loop-概念得名前就跑通了的实践" tabindex="-1">Ralph Loop：概念得名前就跑通了的实践 <a class="header-anchor" href="#ralph-loop-概念得名前就跑通了的实践" aria-label="Permalink to &quot;Ralph Loop：概念得名前就跑通了的实践&quot;">&ZeroWidthSpace;</a></h3>
<p>真正&quot;在概念有名字之前就证明了这个模式&quot;的，是 Geoffrey Huntley 的 <strong>Ralph loop</strong>（又称 Ralph Wiggum 技术，得名于《辛普森一家》里那个憨憨角色）。2025 年 6 月首次公开演示，2025 年 7 月正式发布于博客《Ralph Wiggum as a &quot;software engineer&quot;》（ghuntley.com/ralph）。它的全部实现是一行 bash：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">while</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">do</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> cat</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> PROMPT.md</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> claude-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">done</span></span></code></pre>
</div><p>没了。一个死循环，反复把同一个 prompt 文件喂给 coding agent。Huntley 自己的定义：&quot;Ralph is a technique. In its purest form, Ralph is a Bash loop.&quot;</p>
<p>看起来蠢，但这个设计里有一个非常聪明的决定：<strong>每一轮迭代都用全新的上下文</strong>。</p>
<p><img src="https://oss.justin3go.com/blogs/ralph-context-comparison.png" alt="传统长会话上下文腐烂 vs Ralph每轮全新上下文+状态外置对比图"></p>
<p>进度不靠模型&quot;记得&quot;，靠仓库&quot;长着&quot;。每轮 agent 醒来，读 spec、看仓库现状、做差距分析、挑一件事做完、提交、死掉；下一轮的 agent 从 git 里看到前任留下的一切。Huntley 的自嘲式总结是这套技术最好的注脚：</p>
<blockquote>
<p>&quot;The technique is deterministically bad in an undeterministic world.&quot;
（在一个不确定的世界里，这个技巧是确定性地蠢。）</p>
</blockquote>
<p>Ralph 有战绩，但要打折听：一支 YC 黑客松队伍用它一夜完成 6 个仓库的移植（约 600 美元 API 花费、1000+ 次提交）；Huntley 本人用约 3 个月的持续运行造出了一门带自举编译器的编程语言 CURSED。两者都出自技术支持者之口，没有独立复现。Huntley 自己也划了边界：&quot;There's no way in heck would I use Ralph in an existing code base.&quot;（打死不会在存量代码库里用 Ralph——只适合绿地项目。）</p>
<p>2025 年 12 月，Anthropic 官方发布了 Ralph Wiggum 插件——一个社区玩票技巧被产品方收编，这是&quot;循环&quot;从 hack 走向原语的标志性事件。</p>
<h2 id="四、命名事件-2026-年-6-月的十天" tabindex="-1">四、命名事件：2026 年 6 月的十天 <a class="header-anchor" href="#四、命名事件-2026-年-6-月的十天" aria-label="Permalink to &quot;四、命名事件：2026 年 6 月的十天&quot;">&ZeroWidthSpace;</a></h2>
<p>一个跑了四年的老模式，为什么在 2026 年 6 月突然有了名字并刷屏？直接原因是一串多米诺骨牌：</p>
<p><img src="https://oss.justin3go.com/blogs/loop-engineering-naming-event-timeline.png" alt="2026年6月Loop Engineering命名事件十天时间线"></p>
<p>深层原因有三个，缺一个都炸不起来：</p>
<ol>
<li><strong>模型够强了</strong>。Opus 4.5+、GPT-5.x-Codex 一级的模型能长时间自主运行，并且能靠跑测试、跑编译器自我验证。</li>
<li><strong>原语产品化了</strong>。Claude Code 的 <code>/goal</code>、<code>/loop</code>，Codex 的 Automations——一年前搭一个循环意味着维护一堆自制 bash，现在是产品自带功能。</li>
<li><strong>头部实践者集体现身说法</strong>。Cherny、Steinberger、Karpathy、Ng 在同两周内公开宣布自己围绕循环重组了工作方式，给了这个弥散的实践一个名字和一批可信的脸。</li>
</ol>
<p>顺带存证一个广为流传但<strong>未经核实</strong>的引语：Jensen Huang 的&quot;Nobody writes prompts anymore. The new job is to write and handle loops&quot;——没有任何一份干净的 NVIDIA 官方文字记录能证实它，各转载对源视频的描述互相矛盾（一说 23 分钟一说 53 分钟）。引用它时请当作&quot;对真实方向的转述&quot;而非确凿原话。</p>
<h2 id="五、方法论-五块积木、一层记忆、四层循环" tabindex="-1">五、方法论：五块积木、一层记忆、四层循环 <a class="header-anchor" href="#五、方法论-五块积木、一层记忆、四层循环" aria-label="Permalink to &quot;五、方法论：五块积木、一层记忆、四层循环&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="osmani-的积木清单" tabindex="-1">Osmani 的积木清单 <a class="header-anchor" href="#osmani-的积木清单" aria-label="Permalink to &quot;Osmani 的积木清单&quot;">&ZeroWidthSpace;</a></h3>
<p>Osmani 给出了循环的&quot;解剖结构&quot;——五个组成要素外加一层外部状态。这套结构几乎原样映射到 Claude Code 和 OpenAI Codex 两边，这正是&quot;循环的形状正在变得工具无关&quot;的证据：</p>
<p><img src="https://oss.justin3go.com/blogs/osmani-five-building-blocks.png" alt="Osmani循环解剖结构：AUTOMATIONS心跳→四块积木→EXTERNAL STATE"></p>
<p>其中 Sub-agents 一条的理由，Osmani 说得很俏皮：&quot;The model that wrote the code is too nice grading its own homework.&quot;（写代码的那个模型，给自己作业打分时下不去手。）</p>
<h3 id="langchain-的四层循环栈" tabindex="-1">LangChain 的四层循环栈 <a class="header-anchor" href="#langchain-的四层循环栈" aria-label="Permalink to &quot;LangChain 的四层循环栈&quot;">&ZeroWidthSpace;</a></h3>
<p>LangChain（Sydney Runkle，2026-06-16）把&quot;循环&quot;进一步拆成四层嵌套，每层的时间尺度差一个量级：</p>
<p><img src="https://oss.justin3go.com/blogs/langchain-four-loop-stack.png" alt="LangChain四层循环嵌套栈：Loop1 Agent loop→Loop2验证→Loop3事件驱动→Loop4爬山循环"></p>
<p>关键论断：Loop 1 和 2 只是把活干掉，<strong>复利在 Loop 3 和 4</strong>——一个能从生产 trace 里学习、回头改进自身配置的系统，才是随时间拉开差距的部分。这也是 Andrew Ng &quot;三个循环&quot;（编码循环以分钟计、开发者反馈循环以小时计、外部反馈循环以天/周计）的同构表达。</p>
<h2 id="六、承重墙-验证器" tabindex="-1">六、承重墙：验证器 <a class="header-anchor" href="#六、承重墙-验证器" aria-label="Permalink to &quot;六、承重墙：验证器&quot;">&ZeroWidthSpace;</a></h2>
<p>如果整个 loop engineering 只能记住一句话，是这句：<strong>循环的价值上限由验证器决定，不由模型决定</strong>。</p>
<p>一个循环没有可靠的&quot;完成了吗&quot;判据，它要么永远停不下来，要么在错误的地方停下来并自信地宣布成功。所有 2026 年严肃的写作者在这一点上完全收敛。</p>
<h3 id="制查分离-maker-checker" tabindex="-1">制查分离（maker/checker） <a class="header-anchor" href="#制查分离-maker-checker" aria-label="Permalink to &quot;制查分离（maker/checker）&quot;">&ZeroWidthSpace;</a></h3>
<p><img src="https://oss.justin3go.com/blogs/maker-checker-separation.png" alt="制查分离：MAKER与CHECKER往返，合格后交人类review"></p>
<p>社区共识表述为一条铁律：&quot;The checker is never the same agent as the maker.&quot;（验收者绝不能是实现者本人。）这与 Anthropic harness 文章里 initializer agent 与 coding agent 分权的思路一脉相承。</p>
<h3 id="停止条件-好与坏" tabindex="-1">停止条件：好与坏 <a class="header-anchor" href="#停止条件-好与坏" aria-label="Permalink to &quot;停止条件：好与坏&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>坏的循环目标</th>
<th>好的循环目标</th>
</tr>
</thead>
<tbody>
<tr>
<td>例子</td>
<td>&quot;改进这段代码&quot;</td>
<td>&quot;test/auth 下所有测试通过，且 lint 干净&quot;</td>
</tr>
<tr>
<td>可检查性</td>
<td>靠感觉</td>
<td>机器一跑便知</td>
</tr>
<tr>
<td>循环行为</td>
<td>永远不知道何时停</td>
<td>有明确出口</td>
</tr>
<tr>
<td>可被作弊性</td>
<td>无从谈起</td>
<td>需防 reward hacking（见下）</td>
</tr>
</tbody>
</table>
<h3 id="四种失败模式" tabindex="-1">四种失败模式 <a class="header-anchor" href="#四种失败模式" aria-label="Permalink to &quot;四种失败模式&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>失败模式</th>
<th>典型案例</th>
<th>对策</th>
</tr>
</thead>
<tbody>
<tr>
<td>Reward hacking（钻空子）</td>
<td>删掉失败的测试，让 CI 变绿</td>
<td>验证器同时检查&quot;测试数量没有减少&quot;；Anthropic harness 文章的原文规则：&quot;It is unacceptable to remove or edit tests.&quot;</td>
</tr>
<tr>
<td>幻觉式成功</td>
<td>Agent 自报&quot;已完成&quot;，实际没跑通</td>
<td>只信确定性验证器，永远不信自我汇报</td>
</tr>
<tr>
<td>误差随轨迹复利</td>
<td>第 3 轮的小错在第 15 轮变成大坑</td>
<td>每轮小步提交 + 独立验收，错误早暴露</td>
</tr>
<tr>
<td>成本爆炸</td>
<td>循环空转一夜，账单四位数</td>
<td>下一节的三条护栏</td>
</tr>
</tbody>
</table>
<h3 id="产品是怎么把验证器做进停止条件的-goal-的实现" tabindex="-1">产品是怎么把验证器做进停止条件的：<code>/goal</code> 的实现 <a class="header-anchor" href="#产品是怎么把验证器做进停止条件的-goal-的实现" aria-label="Permalink to &quot;产品是怎么把验证器做进停止条件的：`/goal` 的实现&quot;">&ZeroWidthSpace;</a></h3>
<p>Claude Code 的 <code>/goal</code>（v2.1.139，2026-05-11 上线，机制已对照官方文档核实）是个很好的解剖标本——它把 maker/checker 直接烤进了停止条件：</p>
<p><img src="https://oss.justin3go.com/blogs/goal-stop-hook-mechanism.png" alt=" 的 Stop hook 机制：小模型评估器判定 NO/YES"></p>
<p>评估者与工作者是两个模型、两套视角——这正是第六节开头那条铁律的产品化。</p>
<h2 id="七、三条护栏-循环上线第一天就要装" tabindex="-1">七、三条护栏：循环上线第一天就要装 <a class="header-anchor" href="#七、三条护栏-循环上线第一天就要装" aria-label="Permalink to &quot;七、三条护栏：循环上线第一天就要装&quot;">&ZeroWidthSpace;</a></h2>
<p>所有严肃写作者收敛出的第二个共识：护栏不是可选项。一个没有护栏的循环不是资产，是负债。</p>
<p><img src="https://oss.justin3go.com/blogs/three-guardrails-flowchart.png" alt="三条护栏检查流程：迭代次数→进展检测→预算上限→验证器判定"></p>
<p>护栏 ③ 有一个被各家报道反复引用的现实注脚：Uber 在四个月内烧完了全年 AI 预算后，把工程师的 agent 工具费用上限压到了每人每月 1500 美元（出自二手报道）。预算护栏不是杞人忧天，是已经有人交过学费。</p>
<h2 id="八、原语已经商品化-两大-coding-agent-的循环积木对照" tabindex="-1">八、原语已经商品化：两大 coding agent 的循环积木对照 <a class="header-anchor" href="#八、原语已经商品化-两大-coding-agent-的循环积木对照" aria-label="Permalink to &quot;八、原语已经商品化：两大 coding agent 的循环积木对照&quot;">&ZeroWidthSpace;</a></h2>
<p>&quot;为什么是现在&quot;的第二个答案在这张表里——2025 年你需要自己维护 bash 脚本才能拥有的东西，2026 年是产品自带按钮：</p>
<table tabindex="0">
<thead>
<tr>
<th>循环积木</th>
<th>Claude Code</th>
<th>OpenAI Codex</th>
</tr>
</thead>
<tbody>
<tr>
<td>定时心跳</td>
<td><code>/loop</code>（v2.1.72+，动态 1min–1hr 或固定如 <code>/loop 15m</code>）、<code>/schedule</code>、cron、hooks、GitHub Actions</td>
<td>Automations 标签页 + Triage 收件箱</td>
</tr>
<tr>
<td>目标驱动停止</td>
<td><code>/goal</code>（上节已拆解）</td>
<td>对应的 <code>/goal</code></td>
</tr>
<tr>
<td>并行隔离</td>
<td><code>git worktree</code> / <code>--worktree</code> / <code>isolation: worktree</code></td>
<td>worktrees</td>
</tr>
<tr>
<td>知识沉淀</td>
<td><code>SKILL.md</code></td>
<td><code>SKILL.md</code></td>
</tr>
<tr>
<td>子 agent</td>
<td>subagent 机制</td>
<td><code>.codex/agents/</code> 下的 TOML 定义</td>
</tr>
<tr>
<td>外部连接</td>
<td>MCP</td>
<td>MCP connectors</td>
</tr>
<tr>
<td>批量分发</td>
<td><code>/batch</code>（并行 worktree agent；此条出自二手信源，未对照官方文档核实）</td>
<td>历史上 <code>codex exec</code> 单次即退，需 bash 包装（如 codex-autoresearch-harness）</td>
</tr>
</tbody>
</table>
<p>Boris Cherny 给过一条&quot;典型的一天从这句开始&quot;的循环启动语，可以当作 loop engineering 的 hello world：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/loop babysit all my PRs. Auto-fix build issues, and when comments</span></span>
<span class="line"><span>come in, use a worktree agent to fix them.</span></span></code></pre>
</div><p>他本人的成绩单是这个领域最硬的一手证据（出自其本人 Threads 帖，时间范围是 <strong>2025 年 12 月底前的 30 天</strong>，不是被讹传的 2026 年 6 月）：30 天落地 <strong>259 个 PR、497 次提交、+4 万/−3.8 万行</strong>，每一行都由 Claude Code + Opus 4.5 写成；他在 2025 年 11 月卸载了 IDE。</p>
<p>编码之外，同一个形状也出现在科研场景：Andrej Karpathy 的 autoresearch（2026-03-07 发布，首五天约 2.5 万 GitHub star，4 月初达 6.6 万+）跑的是&quot;提出改动→训练→评估&quot;的循环，只保留能降低 validation loss 的改动（靠 git revert 回滚失败实验），初次演示两天跑了约 700 个实验。Fortune 把这套方法论称作 &quot;The Karpathy Loop&quot;。循环 + 机械验证器（loss 数字）+ 外置状态（git），三要素齐全。</p>
<h2 id="九、冷思考-三类批评-以及它们各自成立的部分" tabindex="-1">九、冷思考：三类批评，以及它们各自成立的部分 <a class="header-anchor" href="#九、冷思考-三类批评-以及它们各自成立的部分" aria-label="Permalink to &quot;九、冷思考：三类批评，以及它们各自成立的部分&quot;">&ZeroWidthSpace;</a></h2>
<p>刷屏概念必有反弹。三类批评都值得认真对待，因为每一类都有成立的部分：</p>
<table tabindex="0">
<thead>
<tr>
<th>批评</th>
<th>代表</th>
<th>核心论点</th>
<th>成立的部分</th>
<th>反驳</th>
</tr>
</thead>
<tbody>
<tr>
<td>&quot;就是个 while 循环&quot;</td>
<td>Hacker News 约 1800 条评论的长帖；讽刺网站 extra-steps.dev</td>
<td>剥掉词汇，这就是 <code>while</code> 套一个 LLM 调用；写过 CI、autoscaler、K8s reconciliation 的人&quot;设计循环&quot;很多年了</td>
<td>技术上完全正确，原语毫无新意</td>
<td>新的不是原语，是纪律：接上定时器 + 验证器 + 预算才是这门手艺的内容。中文社区的说法：&quot;死循环是忘写停止条件的锅，不是 for 循环这个概念的锅。&quot;</td>
</tr>
<tr>
<td>经济批判</td>
<td>Ed Zitron</td>
<td>&quot;OpenAI 会给自己的 token 消耗开账单吗？&quot;——这波风潮是厂商在鼓吹&quot;自主消耗 token&quot;；并讥讽 Cherny 是&quot;被允许每月烧 13 万美元 token 的人&quot;（该数字为二手、存疑）</td>
<td>利益相关是真的：鼓吹者多为 token 卖方或重度补贴用户</td>
<td>预算护栏（第七节）正是对这条批评的工程回应；成本约束下循环依然对特定任务净赚</td>
</tr>
<tr>
<td>前提条件批判</td>
<td>Gergely Orosz（Pragmatic Engineer）</td>
<td>&quot;除了那些①token 预算无上限②觉得逐轮提示拖慢了自己的少数人之外，多数人没有循环的用例&quot;</td>
<td>对一次性、探索性工作，循环确实是杀鸡用牛刀</td>
<td>所以才有第十节的决策矩阵——循环从来不是全场景方案</td>
</tr>
</tbody>
</table>
<p>还有一条批评来自命名者本人。Osmani 在文章里警告了 <strong>comprehension debt（理解债）</strong>：循环越快，&quot;存在的代码&quot;和&quot;你理解的代码&quot;之间的鸿沟越宽；&quot;两个人可以搭一模一样的循环，得到完全相反的结果&quot;。The Register（2026-06-24）甚至说 Osmani 的结论&quot;反而拆了循环的台&quot;——因为他承认&quot;循环改变了工作，但没有把你从工作中删除&quot;。</p>
<p>Osmani 的收尾值得全文引用，它给整场狂热定了调：</p>
<blockquote>
<p>&quot;Build the loop. But build it like someone who intends to stay the engineer, not just the person who presses go.&quot;
（去搭循环。但要以&quot;打算继续当工程师的人&quot;的方式去搭，而不是只当那个按启动键的人。）</p>
</blockquote>
<h2 id="十、决策-你的哪些任务该上循环" tabindex="-1">十、决策：你的哪些任务该上循环 <a class="header-anchor" href="#十、决策-你的哪些任务该上循环" aria-label="Permalink to &quot;十、决策：你的哪些任务该上循环&quot;">&ZeroWidthSpace;</a></h2>
<p>把前面所有内容压缩成一张决策图。横轴是&quot;完成&quot;能否被机器验证，纵轴是出错的代价：</p>
<p><img src="https://oss.justin3go.com/blogs/loop-decision-matrix.png" alt="循环上线决策四象限：横轴完成可否机械验证，纵轴出错代价"></p>
<p>右下角那五类任务的共性：可重复、机器可验证、搞砸了也坏不到哪去。从这里起步，毕业标准（出自调研报告的建议）也很具体：<strong>一个循环无人值守跑满一周，预算零超支，产出的 PR 在你 review 后 90% 以上可合并</strong>——达标了再扩大权限范围。</p>
<p>起步顺序（注意：验证器在循环之前）：</p>
<p><img src="https://oss.justin3go.com/blogs/loop-adoption-sequence.png" alt="循环搭建五步起步顺序：验证器优先于循环本身"></p>
<h2 id="尾声-稀缺技能换位了" tabindex="-1">尾声：稀缺技能换位了 <a class="header-anchor" href="#尾声-稀缺技能换位了" aria-label="Permalink to &quot;尾声：稀缺技能换位了&quot;">&ZeroWidthSpace;</a></h2>
<p>回到引子里那位工程师。四十分钟的修测试链路，人占了二十五分钟。Loop engineering 给出的答案不是&quot;打字快一点&quot;，而是把这个人从链路里挪出去，换成一个验证器加三条护栏。</p>
<p>这门手艺里真正稀缺的技能，已经不是措辞（prompt 层解决了）、不是喂料（context 层解决了）、也不是搭环境（harness 层的产品正在解决）——而是<strong>写出一个循环骗不过的停止条件</strong>。会写这个条件的人，可以让 259 个 PR 在 30 天里自己长出来；不会写的人，会得到一个删测试刷绿 CI 的循环和一张四位数的账单。</p>
<p>而这两个人，搭的可能是一模一样的循环。</p>
]]></content:encoded>
            <author>just@justin3go.com (sam)</author>
        </item>
        <item>
            <title><![CDATA[Agent 的记忆：从无状态模型到持久心智]]></title>
            <link>https://justin3go.com/posts/2026/06/04-agent-memory-architecture-guide</link>
            <guid>https://justin3go.com/posts/2026/06/04-agent-memory-architecture-guide</guid>
            <pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="agent-的记忆-从无状态模型到持久心智" tabindex="-1">Agent 的记忆：从无状态模型到持久心智 <a class="header-anchor" href="#agent-的记忆-从无状态模型到持久心智" aria-label="Permalink to &quot;Agent 的记忆：从无状态模型到持久心智&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>本文从大语言模型无状态这一根本约束出发，系统拆解 Agent 记忆的 <strong>抽取、存储、检索、更新与遗忘</strong> 四阶段生命周期，并比较向量、图数据库与文件式记忆等主流架构。文章结合 Mem0、Letta、Graphiti、Codex、Claude Code 等实践，分析被动抽取与主动编辑、数据库派与文件派、长期画像与原始工作记忆之间的关键取舍。最终指出，真正决定记忆系统能否长期运行的，不只是召回能力，而是可审计的维护机制、合并优先策略与基于真实使用的遗忘。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="tl-dr" tabindex="-1">TL;DR <a class="header-anchor" href="#tl-dr" aria-label="Permalink to &quot;TL;DR&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>记忆的本质是对「有限上下文窗口」的工程绕行。</strong> LLM 本身无状态，所谓「Agent 有记忆」不过是一套外部系统在恰当时机把恰当的过去重新喂回上下文。任何记忆系统都可以拆进同一条生命周期四环——<strong>抽取 → 存储 → 检索 → 更新/遗忘</strong>，你看到的所有框架差异，本质都是这四环上的不同工程选择。</li>
<li><strong>三大根本取舍贯穿全程。</strong> 抽取上是「被动 LLM 抽取 vs 主动 agent 自编辑」（省 token vs 判断细腻）；存储上是「向量 vs 图 vs 文件」，再粗一层是「数据库派 vs 文件派」（容量无限但人看不见 vs 人能读能管但受上下文约束）；定位上是「记忆中间件用数据库派、个人/coding agent 用文件派」。</li>
<li><strong>主流产品正收敛到同一套范式：「压缩的长期画像 + 原始工作记忆」。</strong> ChatGPT、Gemini、Claude 在没有互相抄的情况下走到一起，因为它们面对同一个物理约束。范式趋同，但产品哲学分野——无摩擦 / 单一真相源 / 透明可控。</li>
<li><strong>第四环（更新/遗忘）才是「玩具」与「能长期运行的记忆系统」的真正分水岭，</strong> 业界正收敛出两条共识：「合并优先于新增」（别指望主对话 agent 顺手维护，要么写入门控、要么离线重组）与「原始记录与压缩视图分离」（逻辑遗忘而非物理删除）。</li>
<li><strong>别太信 benchmark。</strong> LoCoMo 等基准可被激进检索策略刷分，各家报数口径不一、连同一家不同页面都对不上。Letta 用纯文件 + GPT-4o-mini 拿到 74.0%，高过 Mem0 报告的最佳图变体 68.5%——结论是记忆质量更取决于 agent 如何管理上下文，而非具体检索机制。<strong>用你自己的真实数据评估，是唯一靠谱的做法。</strong></li>
</ul>
<p>先从一个容易被忽略、却决定了整个领域形态的事实说起：<strong>大语言模型本身是无状态的。</strong></p>
<p>一次 LLM 调用，本质是 tokens-in-tokens-out 的纯函数。你这一轮对话里教会它的东西——你的名字、你偏好 pnpm 而不是 npm、你上次踩过的那个坑——在下一轮、下一个会话、明天，它一概不知道。模型权重在推理时是冻结的，它「记得」的唯一通道，是你把过去的信息重新塞进这一次的上下文窗口里。换句话说，所谓「Agent 有记忆」，从来不是模型真的记住了什么，而是<strong>有一套外部系统，在恰当的时机，把恰当的过去重新喂了回去。</strong></p>
<p>那有人会说：上下文窗口现在不是越来越大了吗？Claude 都有 1M token 的窗口了，那我把全量历史一股脑塞进去不就行了？</p>
<p>这恰恰是理解记忆系统的关键反例。笔者的资料里给了一组很有说服力的数据：即便窗口足够大，把整段历史做 full-context 塞入，在成本和延迟上都会爆炸——Mem0 实测 full-context 方案的 p95 延迟高达 <strong>17.12 秒</strong>，单次请求约 <strong>26K token（26031）</strong>。而换用记忆系统后，同样的任务只需注入约 <strong>1764 token</strong> 的精选事实，p95 搜索延迟约 <strong>0.2 秒</strong>，相对 full-context 节省了约 <strong>90% 的 token</strong>、降低约 <strong>91% 的延迟</strong>（数据据 Mem0 ECAI 2025 论文，arXiv:2504.19413）。</p>
<p>所以可以给「记忆系统」下一个不太浪漫但很准确的定义：</p>
<blockquote>
<p>记忆系统本质上是<strong>对「有限上下文窗口」的工程绕行</strong>。窗口再大也是有限且昂贵的资源；记忆系统做的事，是用约 1.8K token 的「精选事实」去置换掉 26K token 的「全量历史」，在容量、成本、延迟之间找一个能落地的平衡点。</p>
</blockquote>
<p><img src="https://oss.justin3go.com/blogs/memory-vs-fullcontext.png" alt="记忆系统是对有限上下文窗口的工程绕行：用约 1.8K token 精选事实置换约 26K token 全量历史，省约 90% token、降约 91% 延迟"></p>
<p>把这件事想透，后面所有的设计选择——存什么、怎么存、怎么召回、怎么忘——都能归结到一个母问题上：<strong>在上下文预算有限的前提下，如何让 Agent 表现得像「记得」一切。</strong> 这篇文章接下来要做的，就是沿着这个母问题，一层层把「记忆」拆开。</p>
<p>在拆之前，得先有一张地图。下面这一节，笔者先把几个最基础、也最容易被新手搞混的概念立起来。</p>
<h2 id="概念地图-先把基础术语立起来" tabindex="-1">概念地图：先把基础术语立起来 <a class="header-anchor" href="#概念地图-先把基础术语立起来" aria-label="Permalink to &quot;概念地图：先把基础术语立起来&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="短期记忆-vs-长期记忆" tabindex="-1">短期记忆 vs 长期记忆 <a class="header-anchor" href="#短期记忆-vs-长期记忆" aria-label="Permalink to &quot;短期记忆 vs 长期记忆&quot;">&ZeroWidthSpace;</a></h3>
<p>最粗、也最重要的一刀，是把记忆切成短期和长期两类。</p>
<p><strong>短期记忆（也叫工作记忆，working memory）</strong>，指的是当前会话/线程内的上下文：最近几条消息、刚刚返回的工具结果、中间推理的草稿、还没执行完的计划。它的生命周期就是这一次会话——会话结束，通常就清空了。它对应的是「此刻 Agent 脑子里正在转的东西」。</p>
<p><strong>长期记忆（long-term memory）</strong>，则是跨会话、跨线程持久化下来的东西，存在数据库、向量库、图存储或文件里，用于个性化和持续学习。你上周告诉它的偏好，今天新开一个对话它还能调出来，靠的就是长期记忆。</p>
<p>这两者听起来界限清晰，但在工程实现上极易混为一谈。笔者认为讲这个区分最干净的例子是 <strong>LangGraph</strong>——它干脆把这条分界线做成了框架的「一等公民」，用两个完全独立的持久化钩子来承载：</p>
<p>| | checkpointer（短期） | store（长期） |
|</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="agent-的记忆-从无状态模型到持久心智" tabindex="-1">Agent 的记忆：从无状态模型到持久心智 <a class="header-anchor" href="#agent-的记忆-从无状态模型到持久心智" aria-label="Permalink to &quot;Agent 的记忆：从无状态模型到持久心智&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>✨文章摘要（AI生成）</p>
</blockquote>
<!-- DESC SEP -->
<blockquote></blockquote>
<p>本文从大语言模型无状态这一根本约束出发，系统拆解 Agent 记忆的 <strong>抽取、存储、检索、更新与遗忘</strong> 四阶段生命周期，并比较向量、图数据库与文件式记忆等主流架构。文章结合 Mem0、Letta、Graphiti、Codex、Claude Code 等实践，分析被动抽取与主动编辑、数据库派与文件派、长期画像与原始工作记忆之间的关键取舍。最终指出，真正决定记忆系统能否长期运行的，不只是召回能力，而是可审计的维护机制、合并优先策略与基于真实使用的遗忘。</p>
<blockquote></blockquote>
<!-- DESC SEP -->
<h2 id="tl-dr" tabindex="-1">TL;DR <a class="header-anchor" href="#tl-dr" aria-label="Permalink to &quot;TL;DR&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>记忆的本质是对「有限上下文窗口」的工程绕行。</strong> LLM 本身无状态，所谓「Agent 有记忆」不过是一套外部系统在恰当时机把恰当的过去重新喂回上下文。任何记忆系统都可以拆进同一条生命周期四环——<strong>抽取 → 存储 → 检索 → 更新/遗忘</strong>，你看到的所有框架差异，本质都是这四环上的不同工程选择。</li>
<li><strong>三大根本取舍贯穿全程。</strong> 抽取上是「被动 LLM 抽取 vs 主动 agent 自编辑」（省 token vs 判断细腻）；存储上是「向量 vs 图 vs 文件」，再粗一层是「数据库派 vs 文件派」（容量无限但人看不见 vs 人能读能管但受上下文约束）；定位上是「记忆中间件用数据库派、个人/coding agent 用文件派」。</li>
<li><strong>主流产品正收敛到同一套范式：「压缩的长期画像 + 原始工作记忆」。</strong> ChatGPT、Gemini、Claude 在没有互相抄的情况下走到一起，因为它们面对同一个物理约束。范式趋同，但产品哲学分野——无摩擦 / 单一真相源 / 透明可控。</li>
<li><strong>第四环（更新/遗忘）才是「玩具」与「能长期运行的记忆系统」的真正分水岭，</strong> 业界正收敛出两条共识：「合并优先于新增」（别指望主对话 agent 顺手维护，要么写入门控、要么离线重组）与「原始记录与压缩视图分离」（逻辑遗忘而非物理删除）。</li>
<li><strong>别太信 benchmark。</strong> LoCoMo 等基准可被激进检索策略刷分，各家报数口径不一、连同一家不同页面都对不上。Letta 用纯文件 + GPT-4o-mini 拿到 74.0%，高过 Mem0 报告的最佳图变体 68.5%——结论是记忆质量更取决于 agent 如何管理上下文，而非具体检索机制。<strong>用你自己的真实数据评估，是唯一靠谱的做法。</strong></li>
</ul>
<p>先从一个容易被忽略、却决定了整个领域形态的事实说起：<strong>大语言模型本身是无状态的。</strong></p>
<p>一次 LLM 调用，本质是 tokens-in-tokens-out 的纯函数。你这一轮对话里教会它的东西——你的名字、你偏好 pnpm 而不是 npm、你上次踩过的那个坑——在下一轮、下一个会话、明天，它一概不知道。模型权重在推理时是冻结的，它「记得」的唯一通道，是你把过去的信息重新塞进这一次的上下文窗口里。换句话说，所谓「Agent 有记忆」，从来不是模型真的记住了什么，而是<strong>有一套外部系统，在恰当的时机，把恰当的过去重新喂了回去。</strong></p>
<p>那有人会说：上下文窗口现在不是越来越大了吗？Claude 都有 1M token 的窗口了，那我把全量历史一股脑塞进去不就行了？</p>
<p>这恰恰是理解记忆系统的关键反例。笔者的资料里给了一组很有说服力的数据：即便窗口足够大，把整段历史做 full-context 塞入，在成本和延迟上都会爆炸——Mem0 实测 full-context 方案的 p95 延迟高达 <strong>17.12 秒</strong>，单次请求约 <strong>26K token（26031）</strong>。而换用记忆系统后，同样的任务只需注入约 <strong>1764 token</strong> 的精选事实，p95 搜索延迟约 <strong>0.2 秒</strong>，相对 full-context 节省了约 <strong>90% 的 token</strong>、降低约 <strong>91% 的延迟</strong>（数据据 Mem0 ECAI 2025 论文，arXiv:2504.19413）。</p>
<p>所以可以给「记忆系统」下一个不太浪漫但很准确的定义：</p>
<blockquote>
<p>记忆系统本质上是<strong>对「有限上下文窗口」的工程绕行</strong>。窗口再大也是有限且昂贵的资源；记忆系统做的事，是用约 1.8K token 的「精选事实」去置换掉 26K token 的「全量历史」，在容量、成本、延迟之间找一个能落地的平衡点。</p>
</blockquote>
<p><img src="https://oss.justin3go.com/blogs/memory-vs-fullcontext.png" alt="记忆系统是对有限上下文窗口的工程绕行：用约 1.8K token 精选事实置换约 26K token 全量历史，省约 90% token、降约 91% 延迟"></p>
<p>把这件事想透，后面所有的设计选择——存什么、怎么存、怎么召回、怎么忘——都能归结到一个母问题上：<strong>在上下文预算有限的前提下，如何让 Agent 表现得像「记得」一切。</strong> 这篇文章接下来要做的，就是沿着这个母问题，一层层把「记忆」拆开。</p>
<p>在拆之前，得先有一张地图。下面这一节，笔者先把几个最基础、也最容易被新手搞混的概念立起来。</p>
<h2 id="概念地图-先把基础术语立起来" tabindex="-1">概念地图：先把基础术语立起来 <a class="header-anchor" href="#概念地图-先把基础术语立起来" aria-label="Permalink to &quot;概念地图：先把基础术语立起来&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="短期记忆-vs-长期记忆" tabindex="-1">短期记忆 vs 长期记忆 <a class="header-anchor" href="#短期记忆-vs-长期记忆" aria-label="Permalink to &quot;短期记忆 vs 长期记忆&quot;">&ZeroWidthSpace;</a></h3>
<p>最粗、也最重要的一刀，是把记忆切成短期和长期两类。</p>
<p><strong>短期记忆（也叫工作记忆，working memory）</strong>，指的是当前会话/线程内的上下文：最近几条消息、刚刚返回的工具结果、中间推理的草稿、还没执行完的计划。它的生命周期就是这一次会话——会话结束，通常就清空了。它对应的是「此刻 Agent 脑子里正在转的东西」。</p>
<p><strong>长期记忆（long-term memory）</strong>，则是跨会话、跨线程持久化下来的东西，存在数据库、向量库、图存储或文件里，用于个性化和持续学习。你上周告诉它的偏好，今天新开一个对话它还能调出来，靠的就是长期记忆。</p>
<p>这两者听起来界限清晰，但在工程实现上极易混为一谈。笔者认为讲这个区分最干净的例子是 <strong>LangGraph</strong>——它干脆把这条分界线做成了框架的「一等公民」，用两个完全独立的持久化钩子来承载：</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>checkpointer（短期）</th>
<th>store（长期）</th>
</tr>
</thead>
<tbody>
<tr>
<td>作用域</td>
<td>thread（线程/会话）级</td>
<td>user/app（跨线程）级</td>
</tr>
<tr>
<td>存什么</td>
<td>图执行状态、<code>state[&quot;messages&quot;]</code>、会话内多轮上下文</td>
<td>任意 namespace 下的跨会话 KV，用户偏好、画像</td>
</tr>
<tr>
<td>生命周期</td>
<td>换一个 <code>thread_id</code> 就消失</td>
<td>跨会话、跨线程持久存在</td>
</tr>
<tr>
<td>类比</td>
<td>进程的内存</td>
<td>进程外的磁盘</td>
</tr>
</tbody>
</table>
<p>这个例子之所以值得记住，是因为它顺带点破了一个新手最常踩的坑：</p>
<blockquote>
<p><strong>混淆短期与长期，是 Agent 记忆领域最常见的架构错误。</strong> 一个典型翻车现场是：把「用户偏好」这种本该长期持久的信息，错存进了 thread 级的 checkpointer——结果用户一换会话（换 <code>thread_id</code>），偏好就凭空蒸发了，表现就是经典的「这 AI 怎么又不记得我了」。反过来，把转瞬即逝的会话草稿塞进长期 store，又会让长期记忆库迅速腐烂、污染上下文。</p>
</blockquote>
<p>记住这条边界：<strong>短期记忆解决的是「这一次对话内的连贯」，长期记忆解决的是「跨越对话的延续」。</strong> 它们的存储介质、写入时机、清理策略几乎完全不同，应当分两套机制来设计——这是整张地图的第一根坐标轴。</p>
<h3 id="coala-四分法与它的分歧" tabindex="-1">CoALA 四分法与它的分歧 <a class="header-anchor" href="#coala-四分法与它的分歧" aria-label="Permalink to &quot;CoALA 四分法与它的分歧&quot;">&ZeroWidthSpace;</a></h3>
<p>把记忆切成「短期/长期」只是第一刀。要再往细里分，目前业界用得最广的一套词汇，来自 2023 年 Princeton 的 CoALA 论文（Cognitive Architectures for Language Agents，Sumers 等，<strong>arXiv:2309.02427</strong>）。它把 Agent 记忆分成「工作记忆 + 三类长期记忆」共四类：</p>
<table tabindex="0">
<thead>
<tr>
<th>类别</th>
<th>它存什么</th>
<th>一句话理解</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>工作记忆（Working）</strong></td>
<td>当前上下文、最近观察、中间推理、部分计划</td>
<td>短期 scratchpad，对应人类有限容量的缓冲区</td>
</tr>
<tr>
<td><strong>情景记忆（Episodic）</strong></td>
<td>过去发生的具体经历与事件（「上次试方案 X 结果如何」）</td>
<td>让 Agent 能从交互历史里学习，对 copilot 类产品至关重要</td>
</tr>
<tr>
<td><strong>语义记忆（Semantic）</strong></td>
<td>关于世界/用户的事实性知识</td>
<td>大部分已被 LLM 预训练覆盖，企业/用户专属事实才是真正的缺口</td>
</tr>
<tr>
<td><strong>程序记忆（Procedural）</strong></td>
<td>学到的规则、技能、how-to</td>
<td>可从反馈中习得，对应「会做某件事」的能力</td>
</tr>
</tbody>
</table>
<p>这套四分法并非凭空发明，它的源头是认知心理学的几篇奠基性工作：Tulving（1972，区分情景记忆与语义记忆）、Squire（1987，程序记忆）、以及 Baddeley &amp; Hitch（1974，工作记忆模型）。也正因为有这层认知科学的背书，这套词汇被广泛采纳——IBM、MongoDB、LangChain、Letta、Mem0 都用了它的某种变体。在很长一段时间里，CoALA 几乎成了讨论 Agent 记忆的「普通话」。</p>
<p>但到了 2025 年底，这套话语体系开始出现明显的<strong>分裂</strong>，笔者觉得这个分歧本身比四分法更值得读者留意。</p>
<p>一派的代表是 Letta 的 Sarah Wooders，她公开反对这种「大脑类比」，主张回到工程本质看问题——<strong>「LLM 是 tokens-in-tokens-out 的函数，不是大脑」</strong>，因此与其套用 episodic/semantic/procedural 这套人类认知的标签，不如纯粹从「信息如何进出上下文窗口」的工程视角去设计记忆。这不是抠字眼：如果你信的是这一派，你会更关心「这条信息此刻该不该在窗口里」，而不是「它属于情景还是语义」。</p>
<p>另一派则在尝试给分类法做「版本更新」。2025 年 12 月的综述《Memory in the Age of AI Agents》（<strong>arXiv:2512.13564</strong>）就指出该领域正在分裂，并提出了一套更精简的<strong>新三分法</strong>：事实记忆（Factual）、经验记忆（Experiential）、工作记忆（Working）。它某种程度上是对 CoALA 四分法的重新折叠——把「语义」收进 Factual，把「情景 + 程序」并进 Experiential，工作记忆原样保留。</p>
<blockquote>
<p>笔者的建议是：<strong>不必在两种视角里站队，知道它们并存即可。</strong> CoALA 四分法的价值在于给你一套讨论问题的共同词汇，方便你判断「我这个场景缺的到底是哪类记忆」；而 Letta 那派的工程视角则提醒你别被认知类比带跑——真正决定 Agent 体验的，往往不是你给某条记忆贴了什么标签，而是它在恰当的时机有没有进到上下文窗口里。后文谈具体机制时，笔者会更多站在工程视角，但这套分类词汇仍会作为讨论的脚手架反复用到。</p>
</blockquote>
<p>有了「短期 vs 长期」这根纵轴，和「working/episodic/semantic/procedural」这套横向标签，我们就有了一张最基础的记忆概念地图。接下来，笔者会沿着一条记忆从「产生」到「消亡」的完整生命周期，把每个环节的工程取舍逐一摊开。</p>
<h2 id="记忆的生命周期-一个统一抓手" tabindex="-1">记忆的生命周期：一个统一抓手 <a class="header-anchor" href="#记忆的生命周期-一个统一抓手" aria-label="Permalink to &quot;记忆的生命周期：一个统一抓手&quot;">&ZeroWidthSpace;</a></h2>
<p>前面铺垫了那么多概念、那么多项目，要是逐个对比很容易看花眼。笔者建议读者从现在起，脑子里只装一根主线——<strong>记忆的生命周期</strong>。前面提到的 CoALA 论文和那篇《Memory in the Age of AI Agents》在分类法上吵得不可开交，但在生命周期这件事上倒是出奇地一致。它就四个环节：</p>
<blockquote>
<p><strong>抽取（Extraction）→ 存储（Storage）→ 检索（Retrieval）→ 更新/遗忘（Update/Forgetting）</strong></p>
</blockquote>
<ul>
<li><strong>抽取</strong>：决定「记什么」——从滚滚而过的对话里把值得留下的东西挑出来。</li>
<li><strong>存储</strong>：决定「存哪、存成什么形态」——向量、图、KV、关系库，还是一个纯文本文件。</li>
<li><strong>检索</strong>：决定「什么时候、怎么把它喂回去」——语义检索、关键词、图遍历、还是直接读进 system prompt。</li>
<li><strong>更新/遗忘</strong>：决定「老记忆怎么办」——冲突怎么消解、重复怎么合并、过时的怎么失效或衰减。</li>
</ul>
<p>这四个环节就是本文后续所有讨论的骨架。笔者想强调一个判断：<strong>你看到的所有框架差异，本质上都是这四个环节上的不同工程选择</strong>，没有第五种花样。mem0 和 Letta 之所以体感天差地别，不是因为一个「更先进」，而是它俩在「抽取」环节一个选了被动 LLM 抽事实、一个选了让 agent 自己 tool call 编辑；Graphiti 之所以独树一帜，是因为它把功夫几乎全砸在了「更新/遗忘」上，用双时态模型把删除变成了失效。把任何一个号称「记忆系统」的东西拆到这四格里，它的取舍就一目了然，营销话术也就不攻自破了。</p>
<p><img src="https://oss.justin3go.com/blogs/lifecycle-four-stages.png" alt="记忆的生命周期四环：抽取 → 存储 → 检索 → 更新/遗忘，第四环产出的新事实回流到第一环"></p>
<p>接下来四节，笔者就沿着这四个环节一路走下去。先从最前端、也最容易被低估的一环——抽取——讲起。</p>
<h2 id="第一环-抽取——记什么、谁来记" tabindex="-1">第一环：抽取——记什么、谁来记 <a class="header-anchor" href="#第一环-抽取——记什么、谁来记" aria-label="Permalink to &quot;第一环：抽取——记什么、谁来记&quot;">&ZeroWidthSpace;</a></h2>
<p>抽取要回答两个问题：<strong>谁来写记忆</strong>，以及<strong>抽什么</strong>。这两个问题听起来朴素，却是整套系统的源头——抽取这一关丢掉的信息，后面再强的检索也捞不回来。</p>
<h3 id="谁来写记忆-四种模式" tabindex="-1">谁来写记忆：四种模式 <a class="header-anchor" href="#谁来写记忆-四种模式" aria-label="Permalink to &quot;谁来写记忆：四种模式&quot;">&ZeroWidthSpace;</a></h3>
<p>把视野放到一批热门开源项目上，「谁来写记忆」大致只有四种答案：</p>
<table tabindex="0">
<thead>
<tr>
<th>模式</th>
<th>谁动手</th>
<th>代表实现</th>
<th>一句话特征</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>直接 append</strong></td>
<td>系统自动</td>
<td>MetaGPT、Pi</td>
<td>来什么存什么，不做任何提炼</td>
</tr>
<tr>
<td><strong>LLM 抽取</strong></td>
<td>一次额外的 LLM 调用</td>
<td>mem0、crewAI、Codex、Graphiti</td>
<td>把对话蒸馏成事实/三元组/经验条目</td>
</tr>
<tr>
<td><strong>Agent 自编辑</strong></td>
<td>主模型在对话中调工具</td>
<td>Letta、Claude Code auto memory</td>
<td>模型自己决定改写哪条记忆</td>
</tr>
<tr>
<td><strong>人工维护</strong></td>
<td>人手写</td>
<td>CLAUDE.md、AGENTS.md</td>
<td>记忆就是人写的指令文件，Agent 只读不写</td>
</tr>
</tbody>
</table>
<p><strong>直接 append</strong> 是最粗暴的一档。MetaGPT 在角色 observe/react 时把收到和产出的消息原样塞进一个 list，去重都只是「对象是否已存在」级别；Pi 则把会话存成 append-only 的 JSONL 树，从不提炼。好处是零成本、零信息损失，坏处是这堆原始流水迟早膨胀到没法直接喂回模型。</p>
<p><strong>LLM 抽取</strong> 是数据库派的主流。它用一次额外的 LLM 调用，把一段对话蒸馏成结构化的「事实 / 三元组 / insight / 经验条目」。mem0、crewAI、Codex、Graphiti 都走这条路——区别只在抽成什么形态（下一小节展开）。</p>
<p><strong>Agent 自编辑</strong> 最优雅。Letta（MemGPT 后身）把一组记忆操作直接做成工具交给主模型调用——<code>core_memory_append</code>、<code>memory_replace</code>、<code>memory_insert</code>、<code>memory_rethink</code>——模型在推理循环里像编辑文档一样维护自己的 core memory。Claude Code 的 auto memory 同理，模型判断「这条值得未来复用」后，界面会显示 &quot;Writing memory&quot;。它的妙处是把「什么重要」的判断权交给了那个最懂当前上下文的主模型，但代价是每次记忆操作都要烧主模型的 inference token。</p>
<p><strong>人工维护</strong> 则干脆把抽取这一步外包给人。Claude Code 的 <code>CLAUDE.md</code>、Codex/opencode 的 <code>AGENTS.md</code> 就是人手写的指令文件，Agent 只读不写。它质量最高、最可控，但显然不 scale——指望用户持续手填记忆是不现实的。</p>
<blockquote>
<p>现实里很少有项目是纯种。Codex 就同时挂着三层：人工的 <code>AGENTS.md</code>、LLM 蒸馏的 <code>~/.codex/memories/</code>、以及 append-only 的会话日志，叠在一起用。Claude Code 同样是「人工 <code>CLAUDE.md</code> + 模型自写 auto memory」双轨。所以上面这张表读的是「主力机制」，不是非此即彼。</p>
</blockquote>
<p>把「存哪」（文件派 vs 数据库派）和「谁来写」这两个轴叠起来，一批项目大致落在下面这张矩阵里：</p>
<p><img src="https://oss.justin3go.com/blogs/classification-matrix.png" alt="15 个项目按「文件派 vs 数据库派」与「谁来写记忆」两轴分类的矩阵"></p>
<p>这张图最值得玩味的是<strong>两个空角</strong>。文件派几乎没人纯靠 append——因为文件是给人读的，不提炼就成了一堆没法看的流水；数据库派几乎没人纯靠人工维护——机器召回的容量人手根本填不过来。于是两派各自从两端往中间挪，最后<strong>都向「LLM 抽取」收敛</strong>。这也解释了为什么 LLM 抽取是目前最常见的写入方式——它恰好卡在「人填不动」和「append 没法看」之间的那个甜区。</p>
<h3 id="抽什么-取向的分野" tabindex="-1">抽什么：取向的分野 <a class="header-anchor" href="#抽什么-取向的分野" aria-label="Permalink to &quot;抽什么：取向的分野&quot;">&ZeroWidthSpace;</a></h3>
<p>确定了「谁来写」，下一个问题是「抽成什么」。同样是 LLM 抽取，各家的取向差得很远，这直接决定了记忆库长什么样、能干什么。</p>
<p><strong>mem0：抽通用个人事实/偏好。</strong> mem0 的 <code>FACT_RETRIEVAL_PROMPT</code> 把对话压成一句句短陈述——<code>Name is John</code>、<code>Favourite movies are ...</code> 这种。它面向的是 C 端用户画像场景，追求的是「跨会话稳定、可独立成立」的个人事实。值得一提的是 mem0 在新版本里默认管线已经倒向 ADD-only 抽取（一次 LLM 调用、不再 UPDATE/DELETE、累积不覆盖），它的抽取 prompt 甚至明确举例：「有只狗叫 Max」和「和 Max 去露营」是<strong>两条</strong>记忆而非重复——也就是说连 mem0 自己都在重新权衡「激进合并 vs 保留细节」。</p>
<p><strong>Codex：抽四类高信号。</strong> Codex 在「抽什么」上最讲究。它的 stage-one 抽取模板明确只收四类：<strong>用户偏好信号、可复用知识、失败护盾（<code>symptom → cause → fix</code>）、repo 地图</strong>。更关键的是它对「证据来源」立了两条硬规矩——<strong>underindex 助手自述、overindex 用户原话与代码证据</strong>。这一条直击长期记忆最隐蔽的失败模式：把 agent 自己的臆测当成既定事实存进去，下次召回又被当真。Codex 的选择是宁可漏抽，也不让助手的自说自话污染记忆库。</p>
<p><strong>AutoGen：从失败里「学」insight。</strong> AutoGen 的 task-centric memory 走了条独特的路——它的 <code>learn_from_failure</code> 把同一个任务反复交给 agent，一旦答错，就让 LLM 针对这次失败提炼一条 insight 存起来。这里的记忆不是被动「记」下来的，而是从错误里<strong>主动学</strong>出来的，更接近 CoALA 框架里的程序记忆（procedural memory）。</p>
<p>把这三家并排看，差异一目了然：mem0 关心「你是谁、你喜欢什么」，Codex 关心「这个仓库怎么干活、哪里踩过坑」，AutoGen 关心「这类任务怎么才能做对」。同一个「抽取」动作，三种世界观。</p>
<h3 id="被动-vs-主动-本环节的根本分野" tabindex="-1">被动 vs 主动：本环节的根本分野 <a class="header-anchor" href="#被动-vs-主动-本环节的根本分野" aria-label="Permalink to &quot;被动 vs 主动：本环节的根本分野&quot;">&ZeroWidthSpace;</a></h3>
<p>抽取这一环最底层的取舍，其实可以收敛成一句话：<strong>被动抽取，还是主动自编辑。</strong> 这也是整份概念框架里反复出现的那条主线。</p>
<ul>
<li><strong>被动抽取</strong>（mem0、crewAI 这类 LLM 抽取，以及 append）：写入由系统在对话之外触发，主模型甚至感知不到记忆正在被写。好处是<strong>省 token</strong>——记忆操作不占主对话的 inference 预算；坏处是它对「这一刻上下文里什么最重要」的判断不如主模型细腻，<strong>模型没抽到的，就永远丢了</strong>。</li>
<li><strong>主动自编辑</strong>（Letta、Claude Code auto memory）：由主模型在推理循环里自己决定写什么、改什么。好处是判断<strong>细腻</strong>——最懂当前语境的那个模型亲自下手；坏处是<strong>每次都要烧 inference token</strong>，且记忆质量完全押在模型的自觉上，它不主动存，照样丢。</li>
</ul>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>被动抽取</th>
<th>主动自编辑</th>
</tr>
</thead>
<tbody>
<tr>
<td>触发方</td>
<td>系统（对话之外）</td>
<td>主模型（推理循环内）</td>
</tr>
<tr>
<td>token 成本</td>
<td>低</td>
<td>高（每次推理都算）</td>
</tr>
<tr>
<td>判断细腻度</td>
<td>较粗（独立 LLM 凭 prompt 判断）</td>
<td>高（最懂上下文的模型亲自判断）</td>
</tr>
<tr>
<td>典型失败</td>
<td>抽漏即永久丢失</td>
<td>模型不主动存即丢失</td>
</tr>
<tr>
<td>代表</td>
<td>mem0、crewAI</td>
<td>Letta、Claude Code</td>
</tr>
</tbody>
</table>
<p>两条路没有谁更优，只有适不适配。给 C 端产品做无人值守的用户记忆，被动抽取的省 token 和一致性是刚需；做一个要「越用越懂你」的自主 agent，主动自编辑的细腻才撑得起体验。记住这个分野，后面看存储和检索的选择会顺很多——抽取阶段押了哪一边，往往就决定了整条管线的性格。</p>
<h2 id="第二环-存储——记在哪" tabindex="-1">第二环：存储——记在哪 <a class="header-anchor" href="#第二环-存储——记在哪" aria-label="Permalink to &quot;第二环：存储——记在哪&quot;">&ZeroWidthSpace;</a></h2>
<p>抽取决定了「记什么」，存储决定了「记在哪种载体上」。这一环看似只是个数据库选型问题，实则是整篇文章里最容易暴露设计哲学的地方——你把记忆存成什么，决定了它能长多大、谁能看见它、出了错怎么改、迁移时疼不疼。</p>
<h3 id="三大存储流派" tabindex="-1">三大存储流派 <a class="header-anchor" href="#三大存储流派" aria-label="Permalink to &quot;三大存储流派&quot;">&ZeroWidthSpace;</a></h3>
<p>笔者综合两份资料，把当下的存储后端归为三大流派。</p>
<table tabindex="0">
<thead>
<tr>
<th>流派</th>
<th>载体</th>
<th>召回靠什么</th>
<th>代表实现</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>向量库</strong></td>
<td>embedding + 相似度索引</td>
<td>语义检索（常配 BM25）</td>
<td>mem0、LlamaIndex、Cursor 的 Turbopuffer</td>
</tr>
<tr>
<td><strong>时序知识图谱</strong></td>
<td>实体-关系图 + 时间元数据</td>
<td>图遍历 + 向量 + 全文</td>
<td>Zep/Graphiti、cognee</td>
</tr>
<tr>
<td><strong>纯文件 / KV</strong></td>
<td>Markdown / JSONL / 键值</td>
<td>读进上下文、grep</td>
<td>Claude memory tool、Letta filesystem、<code>CLAUDE.md</code></td>
</tr>
</tbody>
</table>
<p><strong>向量库</strong>是最主流的选择。把每条记忆 embed 成向量塞进索引，召回时用查询向量做近邻搜索。它的强项是语义模糊匹配——你问「我喜欢看什么电影」，能召回当初存的「Favourite movies are ...」，哪怕字面不一样。mem0 就坐在 App 与 LLM 之间当一层「记忆引擎」，底层是向量库；Cursor 的代码库索引则把代码按函数/类语义切分后存进 serverless 的 Turbopuffer。</p>
<p><strong>时序知识图谱</strong>走得更远——它不存离散的「事实条目」，而存「实体 + 实体间的关系」，并给每条关系边挂上时间元数据。Zep 的 Graphiti 引擎是这一派的标杆，它在 Neo4j 上维护三层子图（情景子图存原始事件、语义实体子图存抽取出的实体关系、社区子图做高层聚类），召回时融合语义、全文 BM25、图算法三路搜索。它最大的卖点是双时态模型，这一点笔者放到下一环「更新与遗忘」里细说。cognee 也属此派，但它走的是统一三层存储。</p>
<p><strong>纯文件 / KV</strong>则朴素到近乎反直觉：记忆就是一个 Markdown 文件，召回就是把它读进 system prompt。Claude API 的 memory tool 让模型用文件系统命令（<code>create</code> / <code>str_replace</code> / <code>insert</code>）自管理一个 <code>/memories</code> 目录；Claude Code 是 <code>CLAUDE.md</code> 级联文件；Letta 在 2025 年起也转向了 git-backed 的 Context Repositories，每次记忆变更自动版本化提交。</p>
<p>需要强调的是，<strong>这三派并非互斥</strong>。mem0 和 cognee 都明确走「混合」路线：mem0 的存储后端是向量库（Pinecone / Qdrant / Weaviate / Chroma / pgvector）+ 可选图数据库（Neo4j / Memgraph）存实体关系 + 常规 DB 存元数据；cognee 则用一套 ECL pipeline 把数据同时灌进关系、向量、图三层。换句话说，「向量 vs 图 vs 文件」是按主力载体归类，真到工程里往往是叠着用的。</p>
<h3 id="一个更本质的分类轴-数据库派-vs-文件派" tabindex="-1">一个更本质的分类轴：数据库派 vs 文件派 <a class="header-anchor" href="#一个更本质的分类轴-数据库派-vs-文件派" aria-label="Permalink to &quot;一个更本质的分类轴：数据库派 vs 文件派&quot;">&ZeroWidthSpace;</a></h3>
<p>三派分类有用，但笔者读完两份资料后觉得，真正决定开发者体验的，是一条更粗的轴：<strong>这堆记忆，是存进一个机器才看得懂的数据库，还是存成一个人也能打开的文件？</strong></p>
<p>把向量库和知识图谱合并成「<strong>数据库派</strong>」，把纯文件/KV 单列为「<strong>文件派</strong>」，整个图景一下子清晰了。这两派的取舍几乎是镜像对称的：</p>
<table tabindex="0">
<thead>
<tr>
<th>维度</th>
<th>数据库派（向量库 / 知识图谱）</th>
<th>文件派（Markdown / JSONL）</th>
</tr>
</thead>
<tbody>
<tr>
<td>容量</td>
<td>近乎无限</td>
<td>受上下文预算约束</td>
</tr>
<tr>
<td>召回</td>
<td>语义检索强（向量 / 图遍历 / hybrid）</td>
<td>读进 system prompt、grep</td>
</tr>
<tr>
<td>人能看见吗</td>
<td>看不见（向量是一堆浮点数）</td>
<td>能直接读</td>
</tr>
<tr>
<td>人能手改吗</td>
<td>难，得走 API</td>
<td>能，直接编辑</td>
</tr>
<tr>
<td>可追溯</td>
<td>不能手动 diff</td>
<td>git 可追溯，可 blame</td>
</tr>
<tr>
<td>外部依赖</td>
<td>要维护一套基础设施</td>
<td>零外部依赖</td>
</tr>
<tr>
<td>迁移成本</td>
<td>高（绑定具体后端）</td>
<td>几乎为零（就是文本）</td>
</tr>
</tbody>
</table>
<p>数据库派的代价集中在「<strong>人</strong>」这一侧：记忆变成了向量索引或图节点，你打不开、看不懂、不能像 review 代码那样 diff 它写了什么，换一套后端还要做数据迁移。而且你得额外养一套基础设施——Qdrant、Neo4j、pgvector、FalkorDB、LanceDB，跑起来、监控好、不丢数据，都是运维账。Zep/Graphiti 的运维成本高（要跑 Neo4j）在两份资料里都被反复点名。</p>
<p>文件派的代价则集中在「<strong>容量</strong>」这一侧：上下文窗口就那么大，一个 <code>MEMORY.md</code> 不可能无限膨胀。这一派靠「索引 + 按需读」来扩展容量——常驻一个精简索引，明细文件按需 fetch——但这套技巧本身就是为了绕开「人能读的文件装不下太多东西」这个硬约束。</p>
<blockquote>
<p>这里有个常被忽略的前提：文件派之所以「人能读」，是因为它存的是经过提炼的自然语言，而不是原始流水账。文件派几乎没人纯靠 append——人要读，就得先提炼；否则一个塞满原始对话的 JSONL 谈不上「可读」，文件派的核心优势就名存实亡了。</p>
</blockquote>
<h3 id="趋势-个人-agent-选文件派-记忆中间件选数据库派" tabindex="-1">趋势：个人 agent 选文件派，记忆中间件选数据库派 <a class="header-anchor" href="#趋势-个人-agent-选文件派-记忆中间件选数据库派" aria-label="Permalink to &quot;趋势：个人 agent 选文件派，记忆中间件选数据库派&quot;">&ZeroWidthSpace;</a></h3>
<p>把这条轴套到具体项目上，会浮现一个相当一致的规律：</p>
<p><strong>几乎所有 2025 年之后火起来的「个人 / coding agent」都选了文件派</strong>——Codex 的 <code>AGENTS.md</code> + <code>~/.codex/memories/</code>、Claude Code 的 <code>CLAUDE.md</code>、opencode、Pi、Hermes 的 <code>MEMORY.md</code>、OpenClaw 的 <code>MEMORY.md</code>，无一例外。Cursor 那套 <code>.cursor/rules</code>、Copilot 的 <code>copilot-instructions.md</code>、Windsurf 的 <code>.windsurfrules</code>、以及跨工具事实标准 <code>AGENTS.md</code>，本质上也都是文件式配置。</p>
<p><strong>而把记忆当成独立产品卖的「记忆中间件」才用数据库派</strong>——mem0、Letta、Zep 都是。这很好理解：它们的定位是「给别人的 agent 当一层可插拔的记忆服务」，要的就是大容量、强语义检索、多租户隔离，至于「人能不能用肉眼读懂这条记忆」根本不在它们的产品诉求里。</p>
<p>为什么个人 / coding agent 集体倒向文件派？原因不难理解——<strong>它们的用户是开发者</strong>。开发者对「记忆」的要求和 C 端用户截然不同：他们要的是「记忆我能看见、能管、能进版本控制」。一个 <code>CLAUDE.md</code> 可以被 code review、可以 <code>git blame</code> 出某条规则是谁哪天加的、可以在 PR 里被讨论和回滚——这些都是开发者日常工作流里天然就有的东西。让 agent 的记忆复用这套工作流，远比让开发者去查一个向量库里到底存了什么要顺手。Letta 自家那个著名实验也从侧面印证了这条路的性价比：一个用 GPT-4o-mini 的简单文件系统 agent 在 LoCoMo 上拿到 74.0%，反而高于 Mem0 报告的其最佳图变体 68.5%——它的结论是「记忆质量更多取决于 agent 如何管理上下文，而非具体检索机制」。对开发者场景而言，「能管」往往比「检索多花哨」更重要。</p>
<h3 id="各家后端选型一览" tabindex="-1">各家后端选型一览 <a class="header-anchor" href="#各家后端选型一览" aria-label="Permalink to &quot;各家后端选型一览&quot;">&ZeroWidthSpace;</a></h3>
<p>最后把数据库派各家的具体后端选型摊开，方便对照——这也能看出「混合」到底混了些什么：</p>
<table tabindex="0">
<thead>
<tr>
<th>项目</th>
<th>主力后端</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>mem0</strong></td>
<td>Pinecone / Qdrant / Weaviate / Chroma / pgvector（向量）+ 可选 Neo4j / Memgraph（图）+ 常规 DB（元数据）</td>
<td>典型混合：向量主存、图存关系、DB 存时间戳与 user/agent/session ID</td>
</tr>
<tr>
<td><strong>Graphiti（Zep）</strong></td>
<td>Neo4j</td>
<td>单一图后端，每条边带时序元数据</td>
</tr>
<tr>
<td><strong>cognee</strong></td>
<td>默认 LanceDB</td>
<td>用一套 LanceDB 统一关系 + 向量 + 图三层</td>
</tr>
<tr>
<td><strong>LangGraph</strong></td>
<td>生产用 PostgreSQL + pgvector</td>
<td>默认 cosine 相似度；也支持 SQLite、InMemory（仅 demo）</td>
</tr>
</tbody>
</table>
<p>可以看到，即便同属数据库派，选型也分两种气质：mem0 是「按职责分库」——向量、图、关系库各司其职，灵活但要同时养好几套基础设施；cognee 则反其道而行，用 LanceDB 一个后端兜住三层，少一个依赖少一份运维。LangGraph 走的是另一条务实路线——直接复用最普通的 PostgreSQL + pgvector，把记忆设计的活儿全压给开发者，但好处是不引入任何陌生的新基础设施。<strong>这本身就是「数据库派内部」的又一层取舍：是用多个专用后端换能力上限，还是用单一通用后端换运维省心。</strong></p>
<h2 id="第三环-检索——怎么把记忆喂回去" tabindex="-1">第三环：检索——怎么把记忆喂回去 <a class="header-anchor" href="#第三环-检索——怎么把记忆喂回去" aria-label="Permalink to &quot;第三环：检索——怎么把记忆喂回去&quot;">&ZeroWidthSpace;</a></h2>
<p>存和检索是一枚硬币的两面。前一节聊了「记忆存在哪、长什么样」，这一节聊「轮到要用的时候，怎么把对的那几条捞出来、塞进上下文」。一个值得先说破的规律是：<strong>召回方式基本跟着存储载体走</strong>——你选了向量库，召回路径几乎注定是语义检索；你选了 Markdown 文件，召回路径几乎注定是 index-then-fetch。所以这一节也顺着上一节的「数据库派 vs 文件派」两条路分别讲。</p>
<h3 id="数据库派-从单路语义检索-到-hybrid-多路融合" tabindex="-1">数据库派：从单路语义检索，到 hybrid 多路融合 <a class="header-anchor" href="#数据库派-从单路语义检索-到-hybrid-多路融合" aria-label="Permalink to &quot;数据库派：从单路语义检索，到 hybrid 多路融合&quot;">&ZeroWidthSpace;</a></h3>
<p>数据库派的召回清一色是<strong>语义检索</strong>——把 query embedding 算出来，到向量库里做 top-K 相似度匹配。但只靠向量有它的硬伤：纯语义检索对「精确实体名 / 罕见专有名词 / 数字日期」不敏感（embedding 会把「pnpm」和「npm」拉得很近），对「这条事实是不是当下还成立」也无感。于是这一派近一两年的共同演化方向是 <strong>hybrid</strong>——多路召回各取所长，再融合排序。</p>
<p>几个有代表性的实现，可以摆在一起对比：</p>
<table tabindex="0">
<thead>
<tr>
<th>代表实现</th>
<th>召回路数</th>
<th>融合 / 排序</th>
<th>特别之处</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Graphiti</strong> (Zep)</td>
<td>向量 + BM25 全文 + 图 BFS 三路</td>
<td>RRF 融合后再上 cross-encoder 重排</td>
<td>图遍历能补「多跳关系」，语义+全文补不了</td>
</tr>
<tr>
<td><strong>mem0</strong>（新算法）</td>
<td>向量 + BM25 + 实体 boost 三路并行打分</td>
<td>多信号分数融合</td>
<td>额外带<strong>时间感知检索</strong>，给「当前状态 / 过去事件 / 未来计划」排正确的时间实例</td>
</tr>
<tr>
<td><strong>crewAI</strong> (RecallFlow)</td>
<td>向量召回</td>
<td>按<strong>置信度路由</strong></td>
<td>结果置信度低于阈值就 <code>explore_deeper</code>，把当前结果喂回 LLM 再搜一轮</td>
</tr>
</tbody>
</table>
<p>Graphiti 是三路里最「全」的：向量负责语义相近、BM25 负责关键词精确命中、图 BFS 负责沿实体关系往外跳，三路结果用 RRF（Reciprocal Rank Fusion）合并，最后再过一遍 cross-encoder 做精排。mem0 的新算法思路类似——语义、BM25 关键词、实体匹配三路并行打分融合；它额外有一层<strong>时间感知检索</strong>，专门解决「我去年的职位」和「我现在的职位」这种同一实体不同时间实例该返回哪个的问题（这恰好对应下一节 Graphiti bi-temporal 软失效在召回端的回报：旧事实不删，但召回时按时间挑对的版本）。</p>
<p>crewAI 的 RecallFlow 则给召回加了一层<strong>自适应控制</strong>：它不是「搜一次就完事」，而是先看返回结果的置信度，低于阈值就触发 <code>explore_deeper</code>，把已有结果喂回 LLM 重新组织 query 再搜一轮。这等于把「检索」从一次性操作变成了一个带反馈的小循环。</p>
<blockquote>
<p>一句 caveat：hybrid 这套虽然召回质量更高，但代价是每次召回要跑多路检索 + 融合 + 重排，延迟和工程复杂度都上去了。Mem0 自家测得，纯记忆方案相比 full-context 已经能把 p95 搜索延迟压到约 0.2 秒、单次约 1764 token（对比 full-context 的 p95 17.12 秒、约 26K token）——但那是「比 full-context 省」，hybrid 内部各路怎么取舍仍是工程活。</p>
</blockquote>
<h3 id="文件派-index-then-fetch-分层召回" tabindex="-1">文件派：index-then-fetch 分层召回 <a class="header-anchor" href="#文件派-index-then-fetch-分层召回" aria-label="Permalink to &quot;文件派：index-then-fetch 分层召回&quot;">&ZeroWidthSpace;</a></h3>
<p>文件派没有向量库，召回靠的是「把文件读进 system prompt」。但文件可以无限长，上下文预算却有限，怎么办？答案是 <strong>index-then-fetch</strong>——常驻一份「索引」，明细按需 grep + 打开。</p>
<p>以 <strong>Codex</strong> 为代表，这条链路是这样的：</p>
<ol>
<li>把 <code>memory_summary.md</code> 注入系统提示，当作<strong>常驻索引</strong>（它知道「有哪些记忆、大概讲什么」，但不含明细）；</li>
<li>agent 用关键词 <strong>grep <code>MEMORY.md</code></strong>，定位到相关条目；</li>
<li>命中后，按指针<strong>打开明细文件</strong>读全文；</li>
<li>必要时<strong>回到原始 rollout jsonl</strong> 查证据（「这条结论当初是从哪段对话蒸馏出来的」）；</li>
<li>全程限 <strong>4–6 步预算</strong>——不让 agent 无节制地翻文件。</li>
</ol>
<p><strong>Claude Code 的 auto memory 是同构的一套</strong>：<code>MEMORY.md</code> 的前 200 行 / 25KB 当索引，每会话注入；具体 topic 文件按需读。两者的共性是「索引常驻、明细按需取」。</p>
<p><img src="https://oss.justin3go.com/blogs/index-then-fetch.png" alt="文件派 index-then-fetch 分层召回：常驻索引 → 按需 grep → 打开明细 → 回到原始证据"></p>
<p>这是文件派能跟数据库派比「容量」的关键技巧——<strong>不靠向量，靠分层 + 按需 fetch，常驻成本恒定、明细按需取</strong>。记忆库本身可以长到几十上百个文件，但每会话固定注入的就那一份索引，明细只在 agent 主动 grep 命中时才进上下文。这也解释了为什么 2025 年之后那批 coding agent 敢用纯文件做记忆而不担心「装不下」。</p>
<h3 id="一个特例-hermes-用全文检索而非语义" tabindex="-1">一个特例：Hermes 用全文检索而非语义 <a class="header-anchor" href="#一个特例-hermes-用全文检索而非语义" aria-label="Permalink to &quot;一个特例：Hermes 用全文检索而非语义&quot;">&ZeroWidthSpace;</a></h3>
<p>数据库派 = 语义、文件派 = 分层，这个对应关系有个值得记一笔的例外：<strong>Hermes 的跨会话召回用的是 FTS5 全文检索，而不是语义检索</strong>。它把历史消息存进 SQLite，用 FTS5（含 trigram CJK 索引，对中文友好）做关键词全文搜索。</p>
<p>这个选择的妙处在于它故意放弃了语义：</p>
<ul>
<li><strong>零 LLM 成本</strong>——全文检索是纯数据库操作，不需要算 embedding、不需要 LLM 介入；</li>
<li><strong>返回真实消息原文</strong>——召回的是当初说过的原话，不是被摘要过的二手版本；</li>
<li>代码注释里特意写明 <strong>&quot;no summary LLM path&quot;</strong>——把语义摘要从召回主路径里彻底拿掉。</li>
</ul>
<p>换句话说，当你的诉求是「精确找回某句话说过什么」而不是「找回语义相近的概念」时，朴素的全文检索反而又快又准又便宜。这是对「召回一定要上向量」这个默认假设的一个有力反例。</p>
<h3 id="检索机制可能没那么决定性" tabindex="-1">检索机制可能没那么决定性 <a class="header-anchor" href="#检索机制可能没那么决定性" aria-label="Permalink to &quot;检索机制可能没那么决定性&quot;">&ZeroWidthSpace;</a></h3>
<p>讲完三条召回路线，笔者想在这里埋一个伏笔，提醒读者别把「检索」这一环看得太重。</p>
<p>一个反直觉的实验结论是：<strong>召回机制本身，可能没有大家想象的那么决定性</strong>。Letta 自家做过一组对照——一个用 GPT-4o-mini 的纯文件系统 agent，几乎没怎么调提示，就在 LoCoMo 上拿到 74.0%，反而高过不少专用记忆库（包括 Mem0 报告的其最佳图变体 68.5%）。它得出的结论是：「记忆更多关乎 agent 如何管理上下文，而非具体的检索机制。」</p>
<p>这话听起来像是在拆自己的台，但它指向一个真问题：那些漂亮的 hybrid / RRF / cross-encoder 召回流水线，到底有多少分数是「检索做得好」挣来的，又有多少是 benchmark 本身可以被激进检索策略刷出来的？这背后牵扯到一整套 benchmark 水分的问题——这里先点到为止，留到后面专门聊评测的章节再展开。</p>
<h2 id="第四环-更新与遗忘——记忆库怎么不腐烂" tabindex="-1">第四环：更新与遗忘——记忆库怎么不腐烂 <a class="header-anchor" href="#第四环-更新与遗忘——记忆库怎么不腐烂" aria-label="Permalink to &quot;第四环：更新与遗忘——记忆库怎么不腐烂&quot;">&ZeroWidthSpace;</a></h2>
<p>如果说前三环（抽取、存储、检索）决定了记忆系统「能不能用」，那这一环决定的是「能不能一直用」。一个只进不出、来什么存什么的记忆库，跑上几百轮对话之后必然腐烂：同一个事实被记三遍、旧偏好和新偏好打架、过时信息混在召回结果里把模型带偏。所以笔者认为，<strong>更新与遗忘才是区分「玩具」和「能长期运行的记忆系统」的真正分水岭</strong>——而它恰恰是绝大多数项目做得最潦草、甚至干脆不做的一环。</p>
<p>把记忆生命周期摊开看，前三环大家做法虽有差异但都「做了」；唯独到了第四环，会出现一个断崖：相当多的项目在这里直接交白卷。下面分两块讲，先说去重/合并（写入侧的「防腐」），再说遗忘（时间维度的「排毒」）。</p>
<h3 id="去重与合并-脏数据是该「进库前拦」还是「召回时绕」" tabindex="-1">去重与合并：脏数据是该「进库前拦」还是「召回时绕」 <a class="header-anchor" href="#去重与合并-脏数据是该「进库前拦」还是「召回时绕」" aria-label="Permalink to &quot;去重与合并：脏数据是该「进库前拦」还是「召回时绕」&quot;">&ZeroWidthSpace;</a></h3>
<p>先看现状。在笔者翻过的这批项目里，<strong>大多数根本不做实质性的去重/合并</strong>：AutoGen 的记忆库是单调自增 ID 的纯 append，来一条加一条；MetaGPT 稍好一点，但也只是对象级的精确去重（<code>if message in self.storage</code> 那种），语义上重复的两条照样并存。这类系统的隐含假设是「召回时再想办法」——靠检索排序、靠重排把重复和过时的内容压下去。问题是脏数据已经进库了，你只能绕着它走，绕不干净。</p>
<p>少数把去重当核心的项目，思路可以归成四种。</p>
<p><strong>① 写入时相似度门控 + LLM 决策（crewAI / mem0）。</strong> 这一类的精髓是把「整理」从召回时提前到了<strong>写入时</strong>。crewAI 的做法堪称教科书：每条新记忆入库前先做一次向量检索查相似项，若 top 相似度 ≥ <code>consolidation_threshold</code>（默认 0.85）就触发一次 LLM 调用，让它输出一个 <code>ConsolidationPlan</code>——对命中的已有记录逐条给出 <code>keep</code>/<code>update</code>/<code>delete</code> 动作，并决定这条新记忆 <code>insert_new</code> 是否还有必要。mem0 经典的 <code>DEFAULT_UPDATE_MEMORY_PROMPT</code> 思路一致：检索 top-10 旧记忆，让 LLM 判每条该 ADD / UPDATE / DELETE / NONE。这里有个细节笔者觉得特别妙——mem0 会把记忆的 UUID 先映射成连续的整数 id 再喂给 LLM，<strong>防的是 id 幻觉</strong>：让模型生成或引用一长串 UUID，它很容易编一个出来，而连续整数它编错的概率低得多。</p>
<p><img src="https://oss.justin3go.com/blogs/write-time-gating.png" alt="写入时相似度门控 + LLM 决策的去重流程"></p>
<p>这套机制的关键，一句话概括就是：<strong>去重/合并发生在「进库前」而不是「召回时」，脏数据根本进不来。</strong> 这跟 append-only 派「先全进来再说」是两种完全相反的哲学。</p>
<p><strong>② 图结构天然去重（cognee / Graphiti）。</strong> 图存储有个免费的好处：实体本身就是节点，同名实体天然该塌缩成同一个。cognee 把这一点用到了极致——它直接用 <code>uuid5(NAMESPACE, name 规范化)</code> 作为节点 id，也就是说「张三」这个实体不管被写入多少次，规范化后算出来的 id 都一样，自动落到同一个节点上，再用一次 LLM 调用把这个节点累积的多条描述合并成一条。Graphiti 走得更细：先用 MinHash + LSH + Jaccard 做模糊匹配快速筛候选，再用 LLM 兜底判定，而且 prompt 里写了一条硬约束——<strong>数字、日期、关键限定词不同的，绝不算重复</strong>。这条约束很重要，因为「2024 年的预算」和「2025 年的预算」字面高度相似，但合并掉就是灾难。</p>
<p><strong>③ 离线 LLM 重组（Letta / Codex / Hermes）。</strong> 前两种是在写入路径上同步做，这一种则是把整理彻底剥离成后台异步任务，由一个专门的 agent 来干。Letta 的 sleep-time agent 在后台读对话 transcript，被明确要求保证 memory block「综合、可读、最新」，用 <code>memory_rethink</code> 这种工具把整块记忆重写一遍。Codex 的 consolidation 把「合并优先于新增」直接写成了硬规则——prompt 里有 <code>no-op preferred</code>、<code>aggressively merge</code> 这样的字眼，并且巧妙地拿 git workspace 的 diff 当路由信号判断哪些记忆需要重新整理。Hermes 的 curator 则做「umbrella 合并」，它有句话笔者印象很深：<strong>「一会话一 skill 是失败」</strong>——意思是如果每次会话都新长出一个独立技能而不去归并到已有的伞状技能下，那这个记忆系统就退化成了流水账。</p>
<p>这三类（写入门控、图去重、离线重组）的共同点是：它们都不指望主对话 agent 在干活的间隙顺手维护记忆。这也引出后面小结里那个判断。</p>
<blockquote>
<p>不过这里有一个值得警惕的反转。笔者克隆到的 mem0 快照里，<strong>默认管线其实已经从「LLM 判 UPDATE」倒向了 V3 的「ADD-only + <code>linked_memory_ids</code> 链接 + md5 哈希去重」</strong>——也就是说不再让 LLM 激进地覆盖旧记忆，而是只增不改、靠链接关联、靠哈希挡掉完全相同的内容。它的 <code>ADDITIVE_EXTRACTION_PROMPT</code> 里有个很说明问题的例子：「养了一条叫 Max 的狗」和「和 Max 去露营」会被当成<strong>两条</strong>记忆而不是重复项合并掉。连 mem0 自己——这个最早把「smart consolidation」当卖点的项目——都在重新权衡「激进合并 vs 保留细节」的取舍。原因不难理解：合并太狠会丢信息，把两条本该独立的事实揉成一条，未来就再也拆不开了。所以「合并优先」不是越激进越好，它本身是个需要校准的旋钮。</p>
</blockquote>
<h3 id="遗忘-大部分系统其实压根不会忘" tabindex="-1">遗忘：大部分系统其实压根不会忘 <a class="header-anchor" href="#遗忘-大部分系统其实压根不会忘" aria-label="Permalink to &quot;遗忘：大部分系统其实压根不会忘&quot;">&ZeroWidthSpace;</a></h3>
<p>如果说去重还有不少项目认真做，那遗忘这件事就更惨了——笔者的发现相当反直觉：<strong>绝大部分记忆系统根本没有真正的遗忘机制。</strong> mem0、cognee、AutoGen 都是只增不减；Letta 的 archival storage 文档里写得明明白白，「persists indefinitely」（永久保留），删除只能靠显式调 API。换句话说，这些系统的「容量管理」全压在召回侧（检索时少取一点）和上下文侧（塞不下就截断），记忆库本身是个只涨不跌的池子。</p>
<p>真正做了遗忘的，思路可以分四类，而且一个比一个有意思。</p>
<p><strong>① 软失效 / 版本化（Graphiti，笔者认为最优雅）。</strong> Graphiti 给每条事实边都带了 <strong>bi-temporal 四个时间戳</strong>：<code>created_at</code> / <code>expired_at</code> 记的是「系统何时录入、何时让这条记录失效」（录入轴），<code>valid_at</code> / <code>invalid_at</code> 记的是「这个事实在现实中何时开始成立、何时终止」（现实轴）。当一条新事实进来、与某条旧事实矛盾时，<strong>旧边不删</strong>，而是给它打上 <code>expired_at=now</code>（系统现在认为它过期了）和 <code>invalid_at=新事实的 valid_at</code>（现实中它从新事实生效那一刻起就不再成立）。</p>
<table tabindex="0">
<thead>
<tr>
<th>时间轴</th>
<th>字段</th>
<th>语义</th>
</tr>
</thead>
<tbody>
<tr>
<td>系统录入轴</td>
<td><code>created_at</code> / <code>expired_at</code></td>
<td>系统何时录入这条边、何时把它标记为失效</td>
</tr>
<tr>
<td>现实成立轴</td>
<td><code>valid_at</code> / <code>invalid_at</code></td>
<td>这个事实在现实世界中何时开始、何时终止</td>
</tr>
</tbody>
</table>
<p>这套设计的回报是两种查询都能做：既能查「现在为真的事实」（过滤掉已失效的边），又能做 point-in-time 的时间旅行——「系统在某个历史时刻以为什么是真的」。这对处理「用户偏好会演化」这类场景特别干净：包管理从 npm 换成 pnpm，删除式更新会直接抹掉「曾经用过 npm」这段历史，而软失效让记忆变成一个可回溯的版本库。值得一提的是，AutoGPT 的新记忆层干脆直接复用了 Graphiti 这一整套。</p>
<p><img src="https://oss.justin3go.com/blogs/bi-temporal-timeline.png" alt="Graphiti bi-temporal 软失效：旧边不删只打失效戳，支持时间旅行查询"></p>
<p><strong>② 使用计数驱动（Codex / OpenClaw）。</strong> 这一类换了个判断维度：一条记忆该不该留，<strong>不看它写入时被打了多高的「重要性」分，而看它实际被用到的频次</strong>。Codex 的 <code>stage1_outputs</code> 表带了 <code>usage_count</code> 和 <code>last_usage</code> 两列，consolidation 时会忽略那些 <code>last_usage</code> 落在 <code>max_unused_days</code> 窗口之外的记忆——很久没被召回过的，就当它过期了。OpenClaw 同理，按真实召回流量来决定记忆的去留与晋升。笔者很认同这个取向：写入当下的「重要性」是主观臆测，而被反复召回是客观证据，后者显然更可信。</p>
<p><strong>③ 时间衰减 / 状态机（OpenClaw / Hermes / crewAI）。</strong> 这是最朴素也最直接的一类——给记忆加个随时间自然变淡的分数。OpenClaw 用指数衰减，而且是<strong>多层</strong>的——不同层半衰期不等（如 30 天 / 14 天），让不同性质的记忆按各自的速率变淡；crewAI 的召回打分里则直接含一项 <code>decay = 0.5^(age_days/30)</code>。Hermes 则把它做成了显式状态机：skill 30 天没用 → 标记 STALE，90 天没用 → archive（移进 <code>.archive/</code> 目录，<strong>可恢复，绝不物理删除</strong>）。注意这里有个反复出现的设计共识——衰减归衰减，归档归归档，但很少有人真的把数据物理抹掉。</p>
<p><strong>④ tombstone 逻辑遗忘（OpenHands，笔者认为最有工程美感）。</strong> OpenHands 的 condenser 把整个会话历史建模成一份 append-only 的事件日志。当上下文需要压缩时，它<strong>不删任何原始事件</strong>，而是写一条 <code>Condensation</code> tombstone（墓碑）事件，标记「这一批事件已经被这条 summary 替代了」；之后重放会话视图（View）时，过滤器看到墓碑就跳过被替代的原始事件，只呈现 summary。这就实现了<strong>物理保留、逻辑遗忘</strong>——原始证据一条没丢（可审计性拉满），但模型看到的上下文是精简过的（不超上限）。它的高明之处在于把「可审计」和「省上下文」这两个本来打架的目标彻底解耦了：一份数据，两套视图。</p>
<p>这四类放一起看，能拼出一张「遗忘策略全景」：</p>
<table tabindex="0">
<thead>
<tr>
<th>思路</th>
<th>代表项目</th>
<th>触发依据</th>
<th>是否物理删除</th>
</tr>
</thead>
<tbody>
<tr>
<td>软失效 / 版本化</td>
<td>Graphiti（AutoGPT 复用）</td>
<td>新事实与旧事实矛盾</td>
<td>否（打失效戳，支持时间旅行）</td>
</tr>
<tr>
<td>使用计数驱动</td>
<td>Codex / OpenClaw</td>
<td>实际召回频次 / <code>max_unused_days</code></td>
<td>否（归档，靠频次淘汰）</td>
</tr>
<tr>
<td>时间衰减 / 状态机</td>
<td>OpenClaw / Hermes / crewAI</td>
<td>距上次使用的时长</td>
<td>否（衰减分数 / STALE→archive）</td>
</tr>
<tr>
<td>tombstone 逻辑遗忘</td>
<td>OpenHands</td>
<td>上下文需要压缩</td>
<td>否（写墓碑，重放时过滤）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>一个贯穿全表的 caveat：你会发现「是否物理删除」这一列几乎全是「否」。这不是巧合——成熟的记忆系统几乎一致地选择了「逻辑遗忘而非物理删除」，因为物理删除会永久丢掉演化史和审计轨迹，而这两样在事后排查「为什么 agent 当时这么干」时往往是救命的。真正的稀缺能力不是「删得干净」，而是「忘得可逆」。</p>
</blockquote>
<h3 id="小结-别指望主对话-agent-顺手维护记忆" tabindex="-1">小结：别指望主对话 agent 顺手维护记忆 <a class="header-anchor" href="#小结-别指望主对话-agent-顺手维护记忆" aria-label="Permalink to &quot;小结：别指望主对话 agent 顺手维护记忆&quot;">&ZeroWidthSpace;</a></h3>
<p>把这一环的观察收一下：<strong>真正把「合并优先于新增」做扎实的项目，路径无非两条——要么在写入时就上门控（crewAI / mem0），把脏数据挡在库外；要么用一个离线的、专用的 agent 去定期重组（Letta / Codex / Hermes），把整理变成一个独立的后台任务。</strong> 这两条路看起来不同，但背后是同一个判断：记忆维护是一件需要「专门花算力、专门设约束」去做的事，<strong>绝不能指望正在干活的主对话 agent 在间隙里顺手维护</strong>——它注意力在任务上，顺手记下来的东西既不去重也不失效，攒几轮就把记忆库喂脏了。第四环之所以是分水岭，正是因为它逼着你把「记忆维护」当成一等公民来设计，而不是 append 之后的事后补救。</p>
<h2 id="架构取舍-四种写入哲学" tabindex="-1">架构取舍：四种写入哲学 <a class="header-anchor" href="#架构取舍-四种写入哲学" aria-label="Permalink to &quot;架构取舍：四种写入哲学&quot;">&ZeroWidthSpace;</a></h2>
<p>读完前面那么多机制，你大概已经有个直觉：<strong>记忆系统真正的分水岭不在「存哪儿」，而在「谁、在什么时候、按什么规则把东西写进去」</strong>。存储后端可以换（向量库换成图谱、文件换成 SQLite），但写入哲学一旦定下来，几乎决定了整个系统的体验、成本和可控性。笔者把这件事压缩成一张极精炼的对比表——它把四种主流写入哲学放在七个维度上横切：</p>
<table tabindex="0">
<thead>
<tr>
<th>维度</th>
<th>被动抽取（Mem0）</th>
<th>主动自编辑（Letta）</th>
<th>时序图谱（Zep）</th>
<th>文件式（Claude / Letta FS）</th>
</tr>
</thead>
<tbody>
<tr>
<td>写入决策</td>
<td>LLM 自动抽取</td>
<td>agent tool call</td>
<td>LLM 抽实体关系</td>
<td>agent 写文件</td>
</tr>
<tr>
<td>一致性 / 可预测</td>
<td>高</td>
<td>取决于模型</td>
<td>高</td>
<td>高</td>
</tr>
<tr>
<td>token 成本</td>
<td>低</td>
<td>高（每次推理）</td>
<td>中</td>
<td>中</td>
</tr>
<tr>
<td>冲突消解</td>
<td>LLM 判断 ADD/UPDATE/DELETE</td>
<td>agent 重写</td>
<td>双时态失效</td>
<td>覆写文件</td>
</tr>
<tr>
<td>可审计</td>
<td>中</td>
<td>高（ADE）</td>
<td>高（时间点查询）</td>
<td>最高（纯文本）</td>
</tr>
<tr>
<td>运维成本</td>
<td>低-中</td>
<td>中</td>
<td>高（Neo4j）</td>
<td>最低</td>
</tr>
<tr>
<td>最适场景</td>
<td>给现有 agent 加用户记忆</td>
<td>复杂自主 agent</td>
<td>企业动态 / 合规</td>
<td>coding agent</td>
</tr>
</tbody>
</table>
<p>这张表值得逐列读，但笔者更想把它读成「四对核心矛盾」。</p>
<p><strong>第一对矛盾：成本 vs 可控。</strong> 被动抽取（Mem0）把「记什么」交给一次独立的、廉价的 LLM 抽取调用，主对话 agent 完全不知情，所以 token 成本最低、行为最可预测；代价是它<strong>无法对「当前上下文里什么真正重要」做细腻判断</strong>——抽取 prompt 没覆盖到的信号就永久丢了。主动自编辑（Letta）正好相反：模型在推理循环里自己调 <code>core_memory_append</code>/<code>memory_replace</code> 决定写什么，理论上最贴近「agent 当下认为重要的东西」，但每一次记忆操作都烧主模型的 inference token，且<strong>记忆质量完全锁死在模型判断上</strong>——一致性那一栏写的是「取决于模型」，这五个字就是这条路线最大的不确定性。</p>
<p><strong>第二对矛盾：冲突消解放在哪个时间点。</strong> 这是四种哲学差异最大的地方。Mem0 在写入时用 LLM 判 ADD/UPDATE/DELETE/NOOP——脏数据进库前先决策；Letta 让 agent 在后续对话里直接重写整块 core memory；文件式干脆就是「覆写文件」，最朴素也最暴力，旧值直接没了；而 Zep/Graphiti 的双时态失效是唯一一个<strong>不丢历史</strong>的方案——矛盾来了不删旧边，只打失效戳，因此它的「可审计」是「时间点查询」级别的，能回答「系统在某个历史时刻以为什么是真的」。这一列其实暗示了一个分级：覆写 &lt; LLM 判断 &lt; 双时态失效，越往后历史保真度越高，代价是机制越重。</p>
<p><strong>第三对矛盾：可审计 vs 无运维。</strong> 注意「可审计」和「运维成本」这两列几乎是镜像的——文件式同时拿下「可审计最高」和「运维成本最低」，看起来是免费的午餐。但这背后有个没写进表格的隐藏约束：文件式的容量受上下文预算硬约束，它能做到零运维 + 纯文本可 diff，是因为它把「扩容」的难题转嫁给了 index-then-fetch 这套分层召回技巧，而不是真的没有代价。相对地，Zep 用 Neo4j 换来了时序推理和审计能力，运维成本那一栏老老实实写着「高」。</p>
<p><strong>第四对矛盾，也是最该记住的一条：没有银弹，只有场景。</strong> 表格最后一行其实是整张表的结论——四种哲学不是优劣关系，而是<strong>各自咬定了一类场景</strong>：给现成 agent 快速加用户记忆选 Mem0，从零构建复杂自主 agent 选 Letta，企业合规 / 动态环境选 Zep，coding agent 选文件式。更有意思的是 Letta 自家那个反直觉实验：纯文件系统 + GPT-4o-mini 在 LoCoMo 上拿到 <strong>74.0%</strong>，反而高过 Mem0 论文里报告的最佳图变体 <strong>68.5%</strong>（arXiv:2504.19413）。</p>
<blockquote>
<p>Letta 由此得出的结论是：「当前记忆基准可能意义不大，记忆更多关乎 agent 如何管理上下文，而非具体检索机制。」换句话说，写入哲学的选择，比你用的是向量还是图谱重要得多。这也提醒读者，上面表格里的「最适场景」是工程取舍的结果，不是 benchmark 分数的排名——benchmark 本身水分就很大，口径还各不相同（这一点下文专门展开）。</p>
</blockquote>
<h2 id="主流产品在收敛什么" tabindex="-1">主流产品在收敛什么 <a class="header-anchor" href="#主流产品在收敛什么" aria-label="Permalink to &quot;主流产品在收敛什么&quot;">&ZeroWidthSpace;</a></h2>
<p>把视线从开源项目转到消费级产品，会看到一个比开源世界清晰得多的趋势：<strong>ChatGPT、Gemini、Claude 三家，加上一票 coding agent，正在收敛到同一套范式——「压缩的长期画像 + 原始工作记忆」</strong>。</p>
<p>这句话值得拆开。所谓「压缩的长期画像」，是一份跨会话持久、被精炼过的用户/项目摘要，常驻或按需注入；所谓「原始工作记忆」，是一段未经压缩的近期对话窗口，用来给那份滞后的画像打补丁（因为摘要往往不实时更新）。<strong>为什么三家在没有互相抄的情况下不约而同走到这里？</strong> 因为它们面对的是同一个物理约束——上下文窗口有限、full-context 既贵又慢（Mem0 测得 full-context p95 延迟 17.12 秒、单次约 26K token），而用户的真实历史是无限增长的。要在「个性化」和「成本/延迟」之间找平衡点，「一份压缩画像兜底 + 一段原始近期对话补 delta」几乎是唯一的工程解。下面逐个看四家是怎么把同一个范式落到不同取舍上的。</p>
<p><strong>ChatGPT 走的是双层 always-on，把「无摩擦」做到极致。</strong> 它有两层：一层是 <strong>Saved memories</strong>，显式、用户可编辑的事实列表（姓名、偏好、目标），底层是 bio tool，存在系统提示的 &quot;Model Set Context&quot; 区，带时间戳（如 <code>[2025-05-02] The user likes ice cream</code>）——这是三家里唯一对用户完全可见、可审计的清单。另一层是 <strong>Reference chat history</strong>，2025 年 4 月 10 日上线的隐式画像，注意它<strong>并不真正「搜索」过去对话</strong>，而是维护一份近期聊天的滚动历史、随时间构建用户画像，用户无法直接查看、不可审计。ChatGPT 的取舍很鲜明：一切自动加载进每个对话，换来「零等待个性化」，代价是牺牲了可审计性和可预测性——你说不准它到底从哪条历史里捞出了什么。</p>
<p><strong>Gemini 走的是单一真相源，是三家里最克制的一个。</strong> 2025 年 8 月上线的 Personal Context，核心是<strong>一份 <code>user_context</code> 结构化文档</strong>：固定 schema 的分段类型化大纲，每条记忆是一个简短事实 bullet，带 <strong>Statement + Rationale + 时间戳</strong>（如 &quot;User explicitly stated on June 18, 2025…&quot;），再配一个「最近对话轮次」的原始消息窗口当 delta。它最聪明的设计是<strong>把冲突消解完全压在时间戳上</strong>——「当前角色」是一年前更新的、今天用户说「我换了新公司」，系统就能合理覆盖旧职位；又能把「2024 年 6 月考虑搬去 SF」解读成时间限定的历史而非永久身份。Shlok Khemani 评价 Gemini 这套「比 ChatGPT 和 Claude 都简单」，是单一真相源。坐拥最多数据的 Google 反而在「何时触发个性化、存什么」上最保守，这本身就是个值得玩味的取舍。</p>
<p><strong>Claude 走的是透明可控、工作场景导向，但它的复杂在于「其实是两套系统」。</strong> (A) <strong>Search and reference chats</strong>（2025 年 9 月）是实时、按需的 RAG：模型主动调 <code>conversation_search</code> 和 <code>recent_chats</code> 两个 tool 去检索原始聊天历史，仅付费用户可用，带引用链接回原聊天。(B) <strong>Generate memory from chat history</strong>（后加）则是存储的合成画像：Claude 自动摘要对话、跨聊天历史合成关键洞察、<strong>每 24 小时更新一次</strong>，用户能在 &quot;View and edit memory&quot; 里查看和编辑，编辑立即生效；2026 年 3 月这一层扩展到所有用户（含免费）。Claude 还有个独有设计——<strong>project 隔离</strong>：每个 project 有独立的 memory 空间和专属 summary，且跨聊天的 memory summary 明确<strong>排除</strong> project 内的聊天，让「产品路线图」和「创意写作」互不串味。</p>
<blockquote>
<p>这里有个极易混淆、必须讲清的点：消费版 <strong>Claude.ai 的 chat memory</strong>、开发者 <strong>API 的 memory tool</strong>、以及 <strong>Claude Code 的 CLAUDE.md</strong>，是三套完全不同的系统。API 的 memory tool 是 <code>/memories</code> 文件目录，模型用 <code>create</code>/<code>str_replace</code>/<code>insert</code> 命令自管理（beta header <code>context-management-2025-06-27</code>，2025-09-29 上线 beta）；Claude Code 是按优先级级联加载的 CLAUDE.md 文件；它们都不等于 Claude.ai 网页版那套合成画像 + RAG 搜索。看到 &quot;Claude memory&quot; 四个字，先问清是哪一套。</p>
</blockquote>
<p><strong>Coding agent 收敛得更彻底——「代码库索引 + rules 文件 + 自动 memories」成了事实标准三件套。</strong> Cursor 是最完整的样本：<strong>codebase indexing</strong> 把代码按函数/类语义切分后算 embedding 存进 Turbopuffer 向量库，用 <strong>Merkle 树</strong>做增量更新（每 5–10 分钟查 hash，只重传改动文件），且只上传 embedding 不传原始代码；<strong>rules</strong> 存在 <code>.cursor/rules/</code> 或根目录 AGENTS.md；v1.0（2025 年 6 月）的 <strong>Memories</strong> 则是一个 sidecar model 观察对话、建议保存条目供开发者批准，且刻意做成 per-project、不跨项目共享。GitHub Copilot Memory（2026 公开预览，3 月对 Pro 默认开）的取舍也很典型——它把记忆分成 repository-level facts（带引用指向支撑代码，<strong>用前先校验当前分支是否仍成立</strong>，只用 validated facts）和 user-level preferences（跨仓库），并用「28 天未使用自动删除、成功使用重置计时器」做被动遗忘。而把整个生态串起来的，是 CLAUDE.md / AGENTS.md / <code>.windsurfrules</code> / copilot-instructions.md 这套<strong>文件式配置</strong>——它已经成了跨工具的事实标准。</p>
<p>把这四类放一起看，收敛的不只是「双层结构」这个形态，更是三个共识：其一，<strong>长期画像必须被压缩</strong>，没人敢把全量历史塞进上下文；其二，<strong>画像需要一段原始近期对话来补实时性</strong>，因为摘要天然滞后；其三，<strong>冲突消解越来越依赖时间维度</strong>——Gemini 的时间戳、Copilot 的「用前校验是否仍成立」、Claude 的 24 小时刷新周期，本质都是在回答同一个问题：「这条旧记忆，现在还算数吗？」 真正区分各家的，反倒是那条贯穿始终的产品价值观——ChatGPT 押「无摩擦」、Gemini 押「单一真相源」、Claude 押「透明可控把权交给用户」。范式趋同，哲学分野，这才是这一节最值得记住的洞察。</p>
<h2 id="别太信-benchmark" tabindex="-1">别太信 benchmark <a class="header-anchor" href="#别太信-benchmark" aria-label="Permalink to &quot;别太信 benchmark&quot;">&ZeroWidthSpace;</a></h2>
<p>聊了这么多机制，到了该泼冷水的环节：<strong>当前这套记忆 benchmark，可能没你想的那么有参考价值。</strong></p>
<p>最常被引用的 LoCoMo 已经被证明可以靠激进检索策略刷分——只要召回时把候选答案塞得足够多、足够准，分数就上去了，跟记忆系统本身的「理解」关系不大。而真正让笔者意识到「基准失真」的，是 Letta 自家那篇博客《Benchmarking AI Agent Memory: Is a Filesystem All You Need?》里的一个反直觉实验：</p>
<blockquote>
<p>他们用一个<strong>纯文件系统</strong> + GPT-4o-mini，再加上「极少提示调优」，在 LoCoMo 上拿到了 <strong>74.0%</strong>——明显高于 Mem0 在自家论文（arXiv:2504.19413）里报告的最佳图变体 <strong>68.5%</strong>。</p>
</blockquote>
<p>一个没有向量库、没有知识图谱、没有任何花哨检索机制的「破文件夹」，反而赢了精心设计的图记忆。Letta 由此给出的结论很扎心：当前的记忆基准可能意义不大，<strong>记忆质量更多取决于「agent 怎么管理自己的上下文」，而不是用了哪种具体的检索机制</strong>。换句话说，你纠结向量还是图、RRF 还是 cross-encoder 的那些功夫，大概率不如把「什么时候读、读多少、读进来怎么用」这件事想清楚来得值钱。</p>
<blockquote>
<p><strong>caveat：横向比较 benchmark 分数基本是个陷阱。</strong> 各家报告 LoCoMo 分数时用的 eval-LLM 和检索设置都不一样，不可直接横比。更尴尬的是连 Mem0 自己不同页面的报数都对不上：博客里写 LoCoMo 92.5% / LongMemEval 94.4%（约 6,900 token/query），另一些页面却给的是 91.6% / 93.4%，而且托管版还包含「专有优化、开源版达不到」的部分。所以别拿别人 README 里的数字当真——<strong>用你自己的真实数据去评估</strong>，这是唯一靠谱的做法。</p>
</blockquote>
<h2 id="落地-四条路线-没有银弹" tabindex="-1">落地：四条路线，没有银弹 <a class="header-anchor" href="#落地-四条路线-没有银弹" aria-label="Permalink to &quot;落地：四条路线，没有银弹&quot;">&ZeroWidthSpace;</a></h2>
<p>如果你看完这一切，想给自己的 Agent 上一套记忆系统，笔者的第一句话是：<strong>先确认你属于哪条路线，再选机制。</strong> 这四条路线对应四类截然不同的诉求，参考实现也完全不同：</p>
<table tabindex="0">
<thead>
<tr>
<th>你的场景</th>
<th>推荐路线</th>
<th>参考项目</th>
</tr>
</thead>
<tbody>
<tr>
<td>给 SaaS / C 端产品做用户记忆，要无人值守</td>
<td>数据库派 + 写入时合并门控</td>
<td>mem0、crewAI</td>
</tr>
<tr>
<td>事实会频繁演化、需要时间旅行查询</td>
<td>知识图谱 + bi-temporal</td>
<td>Graphiti、Zep</td>
</tr>
<tr>
<td>coding / 个人 agent，要人能看、能管、能进 git</td>
<td>文件派 + index-then-fetch</td>
<td>Codex、Claude Code</td>
</tr>
<tr>
<td>想要「用得越久越聪明」的自我改进</td>
<td>文件派 + 双速离线整理</td>
<td>Hermes、OpenClaw</td>
</tr>
</tbody>
</table>
<p>这四条路线没有高下之分，错配才是灾难——给一个 coding agent 上 Neo4j，或者让一个 C 端产品指望用户手动维护 Markdown，都是自找麻烦。</p>
<p>选定路线之后，无论走哪条，下面这几条通用原则都建议刻进 DNA，它们是从十几个项目的踩坑里反复验证出来的：</p>
<ul>
<li><strong>不要过早上图数据库。</strong> 向量库 + LLM 抽取能覆盖绝大多数场景，只有当「关系推理」真正成为核心需求时，图才值回它那份不低的运维成本（一套 Neo4j 不是免费的）。</li>
<li><strong>给每条记忆打时间戳。</strong> 这是冲突消解和时序查询的前提。反面教材很现成——OpenAI 早期的记忆不给条目打时间戳，直接导致在 LoCoMo 的时序类问题上惨败（Mem0 图变体在时序题上 58.13% vs OpenAI 21.71%，差距大头就出在这）。时间戳几乎零成本，但缺了它，「我上周三在干嘛」这类问题你永远答不对。</li>
<li><strong>隔离 user / session / project 作用域。</strong> 这是相关性和安全的基础。Claude 的 project 隔离、Cursor 的 per-project memories 都印证了这一点——别让 A 项目的记忆污染 B 项目的上下文。</li>
<li><strong>benchmark 当参考、不当真。</strong> 理由上一节已经说透了。</li>
</ul>
<p>如果一定要笔者推一个「集大成的工程范例」，那是 Codex 的两阶段记忆维护管道——它几乎把「怎么自动维护一个文件记忆库」这件事标准化了：</p>
<p><img src="https://oss.justin3go.com/blogs/codex-two-phase-pipeline.png" alt="Codex 两阶段记忆维护管道：Phase 1 并行蒸馏入库，Phase 2 沙箱内 git-diff 驱动整理"></p>
<p>Phase 1 并行地把每个历史会话蒸馏成 SQLite 结构化记录；Phase 2 则由一个跑在<strong>无网、无审批沙箱</strong>里的专用 consolidation 子 agent，用 <code>~/.codex/memories/.git</code> 的 baseline-to-worktree diff 当作唯一的脏数据检测与 ingestion / forgetting 路由信号。它把「记忆整理」本身做成了一个<strong>受沙箱约束、可增量、可遗忘、带使用计数反馈</strong>的 agent 任务，而不是简单 append 或撒手不管。想自己搭一套文件式自维护记忆库的人，从这个管道开始抄准没错。</p>
<h2 id="收尾-正在收敛的共识-与两个没人解决的缺口" tabindex="-1">收尾：正在收敛的共识，与两个没人解决的缺口 <a class="header-anchor" href="#收尾-正在收敛的共识-与两个没人解决的缺口" aria-label="Permalink to &quot;收尾：正在收敛的共识，与两个没人解决的缺口&quot;">&ZeroWidthSpace;</a></h2>
<p>把十几个项目和三家主流产品摊在一起看，会发现喧嚣之下其实有两条共识正在悄悄收敛：</p>
<p><strong>其一，「合并优先于新增」。</strong> 无论你存在数据库还是文件里，激进的 append 都会让记忆库慢慢腐烂——重复、过时、自相矛盾的条目越堆越多，最后召回质量崩盘。区别只在于「合并这件事在什么时候做」：crewAI、mem0 选择在<strong>写入时</strong>用相似度门控 + LLM 决策把脏数据挡在库外；Letta、Codex、Hermes 则交给<strong>离线</strong>的专用 agent 去做重组。但方向是一致的——别指望主对话 agent 顺手维护，把合并当成一等公民去设计。</p>
<p><strong>其二，「原始记录与压缩视图分离」。</strong> 成熟方案都不会用 summary 直接覆盖掉原始证据：OpenHands 用 tombstone 逻辑遗忘但物理保留事件、Codex 把 rollout 原始会话日志与蒸馏后的 summary 分开存、Graphiti 的失效边只打时间戳从不删除。日常召回时看精炼后的视图（省 token、降噪），需要查证或回溯时还能翻到原始记录。可审计性和上下文预算这对矛盾，靠「分层」而非「二选一」来化解。</p>
<p>但话不能说满。两个全行业至今都没真正解决的缺口，笔者必须点出来：</p>
<p>第一，<strong>写入前的人工 review，没有一个项目完整做到。</strong> 十几个项目里，记忆写入前有人工确认门的一个都没有——全是 LLM 抽取后直接落库。最接近的两个也只是局部：AutoGPT 的<strong>删除</strong>走两步确认（找候选 → 用户确认 → 执行），但写入仍全自动；Hermes 的 curator 有 dry-run 模式产出 <code>REPORT.md</code> 让人批准，但只覆盖 skill 整理这一块。「记忆质量靠 prompt 硬约束 + 自动化兜底，不靠人」是当前的主流哲学，本质是为了无人值守的全自动体验而放弃人工把关——这个取舍是否划算，留待时间检验。</p>
<blockquote>
<p>第二，也是更要命的一个：<strong>安全。</strong> 多个来源都指出，记忆系统天然易受<strong>间接 prompt injection</strong> 攻击——攻击者可以在一段看似无害的内容里埋入指令，诱导 agent 在用户毫不知情的情况下读取、甚至写入记忆。一旦被污染的记忆进了库，它会在未来每一次召回时反复影响 agent 的行为，且极难察觉。这不是某一家的实现 bug，而是<strong>所有自动记忆方案的共性隐患</strong>。在你给 Agent 接上「会自己记东西」的能力之前，请务必把这条风险放在心上。</p>
</blockquote>
<p>记忆让 Agent「用得越久越聪明」，但也让它「被污染一次就长期受影响」。这件事远没到尘埃落定的时候——共识在收敛，缺口也明明白白地摆在那里。对想动手的人来说，这恰恰是最好的入场时机。</p>
]]></content:encoded>
            <author>just@justin3go.com (sam)</author>
        </item>
    </channel>
</rss>