<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent 工程 on 扎塔-Zata</title><link>https://www.zata.cc/categories/agent-%E5%B7%A5%E7%A8%8B/</link><description>Recent content in Agent 工程 on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Example Person</copyright><lastBuildDate>Thu, 24 Sep 2026 17:09:06 +0800</lastBuildDate><atom:link href="https://www.zata.cc/categories/agent-%E5%B7%A5%E7%A8%8B/index.xml" rel="self" type="application/rss+xml"/><item><title>Agent 决策审计落地：写入点、复核器与门禁降级判据</title><link>https://www.zata.cc/p/agent-decision-audit-implementation/</link><pubDate>Thu, 24 Sep 2026 14:15:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-decision-audit-implementation/</guid><description>&lt;img src="https://www.zata.cc/p/agent-decision-audit-implementation/images/index/index.svg" alt="Featured image of post Agent 决策审计落地：写入点、复核器与门禁降级判据" />&lt;p>一份能用的决策审计，第一版可以只有六行 JSONL。还是&lt;a class="link" href="https://www.zata.cc/p/agent-decision-audit-and-tracing/" >上一篇&lt;/a>里那个改数据库连接配置的例子——Agent 选中了单元测试和真实数据库冒烟，这次把事件流补全：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;plan_created&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;planner&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_revision&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;a1b2c3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;checks&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;unit-tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;selected&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;real-db-smoke&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;selected&amp;#34;&lt;/span>&lt;span class="p">}]}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;check_started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;executor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;unit-tests&amp;#34;&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;check_finished&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;executor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;unit-tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;result&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;passed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;exit_code&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;evidence_ref&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;runs/17/logs/pytest-1&amp;#34;&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;check_started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;executor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;real-db-smoke&amp;#34;&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;check_finished&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;executor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;real-db-smoke&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;result&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;unavailable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;reason&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;无预发凭据&amp;#34;&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;verdict_issued&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;verifier&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">:{&lt;/span>&lt;span class="nt">&amp;#34;verdict&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;escalate&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;missing_boundaries&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="s2">&amp;#34;连接池真实行为&amp;#34;&lt;/span>&lt;span class="p">]}}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>字段比设计篇的记录少一些——&lt;code>run_id&lt;/code>、&lt;code>attempt_id&lt;/code>、&lt;code>occurred_at&lt;/code>、&lt;code>trace_id&lt;/code> 都还在，这里略去不展。第六行就是这套东西的价值：冒烟拿不到凭据时，裁决不是&amp;quot;通过&amp;quot;，是升级。事后任何人拿着 &lt;code>dec-42&lt;/code> 都能回答：当时计划跑什么、实际跑到哪、缺了什么、为什么没放行。&lt;/p>
&lt;p>设计篇把&amp;quot;该记什么&amp;quot;讲清了：身份模型、计划/执行/裁决分离、&lt;code>history_complete&lt;/code>。这篇接着回答落地时真正卡住的三件事——**写入点挂在哪、复核器怎么不变成第二个 Agent、什么条件下才允许它影响放行。**我的判断放在前面：存储本身一晚上就能写完，卡人的从来是这三件。&lt;/p>
&lt;h2 id="一先说不建什么">一、先说不建什么
&lt;/h2>&lt;p>动手前先划掉三个&amp;quot;看起来该做&amp;quot;的东西。审计的第一死因不是记得不准，是被绕过——系统一重，团队总有办法绕过它。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>做重了的信号&lt;/th>
&lt;th>v1 的做法&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>起一个独立的审计服务&lt;/td>
&lt;td>一张追加式表，执行进程内直接写&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>把原文（prompt、源码、终端输出）存进审计&lt;/td>
&lt;td>只存引用与摘要，原文走受控存储&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>上线即接管放行、替换现有门禁&lt;/td>
&lt;td>先与固定门禁并行，只记录分歧&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>第三条是全文最重要的边界。设计篇说决策审计让&amp;quot;自选验证深度&amp;quot;的自由度可复核，但可复核不等于可放行——从&amp;quot;并行记录&amp;quot;走到&amp;quot;参与放行&amp;quot;，中间隔着第五节那套判据，判据没满足之前，记录再完整也不构成授权。&lt;/p>
&lt;h2 id="二最小数据契约一张只允许-insert-的表">二、最小数据契约：一张只允许 INSERT 的表
&lt;/h2>&lt;p>DDL 以 SQLite 3 为准，换 PostgreSQL 只需要改触发器写法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">decision_events&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">seq&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INTEGER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOINCREMENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">event_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- 幂等键，由业务身份拼出
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">decision_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">run_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">attempt_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">event_type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CHECK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event_type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;plan_created&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;check_started&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;check_finished&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;verdict_issued&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;decision_invalidated&amp;#39;&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">actor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">source_revision&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">policy_version&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">trace_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">occurred_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">detail&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- JSON，自带 detail_schema_version
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idx_events_decision&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">decision_events&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">decision_id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TRIGGER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">no_update&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEFORE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">decision_events&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RAISE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ABORT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;append-only&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TRIGGER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">no_delete&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEFORE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">decision_events&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RAISE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ABORT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;append-only&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>追加式不是审美偏好，它换来两个便宜：没有 UPDATE，历史就不可改；完整性验证退化为检查&amp;quot;存在且有序&amp;quot;（第六节展开）。触发器挡的是自己人手滑，真正的防线在读取端。&lt;/p>
&lt;p>五个事件类型各自的写入责任：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>event_type&lt;/th>
&lt;th>谁写&lt;/th>
&lt;th>必填 detail&lt;/th>
&lt;th>记录的事实&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>plan_created&lt;/td>
&lt;td>planner&lt;/td>
&lt;td>checks（每项含 disposition 与 reason）&lt;/td>
&lt;td>决策的存在起点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check_started&lt;/td>
&lt;td>executor&lt;/td>
&lt;td>check_id&lt;/td>
&lt;td>某项检查开始执行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>check_finished&lt;/td>
&lt;td>executor&lt;/td>
&lt;td>check_id、result、evidence_ref&lt;/td>
&lt;td>执行器观察到的结果&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>verdict_issued&lt;/td>
&lt;td>verifier / 放行器&lt;/td>
&lt;td>verdict、依据摘要&lt;/td>
&lt;td>对计划与证据的裁决&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>decision_invalidated&lt;/td>
&lt;td>平台&lt;/td>
&lt;td>失效原因、新 revision&lt;/td>
&lt;td>旧裁决作废&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>两个容易写错的字段。&lt;code>occurred_at&lt;/code> 用写入方本地时钟就行，排序靠 &lt;code>seq&lt;/code>，时钟只负责展示——分布式环境里别指望时钟可排序。&lt;code>detail&lt;/code> 里必须带 &lt;code>detail_schema_version&lt;/code>：追加式意味着旧记录永远不会迁移，演进只能靠新版本号，读取端按版本解释。&lt;/p>
&lt;p>事件不是随便堆的，读取端会按一个状态机校验它们：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-implementation/images/index/event-state-machine.svg"
loading="lazy"
alt="五个事件类型构成的状态机"
>&lt;/p>
&lt;p>&lt;em>▲ 图：自绘&lt;/em>&lt;/p>
&lt;h2 id="三写入点审计不改变控制流只挂在三个钩子上">三、写入点：审计不改变控制流，只挂在三个钩子上
&lt;/h2>&lt;p>决策审计在 Runtime 里没有自己的环节。它不参与循环，不加延迟，只挂在三个既有节点的出口上：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-implementation/images/index/write-points.svg"
loading="lazy"
alt="三个写入点在 Runtime 中的位置"
>&lt;/p>
&lt;p>&lt;em>▲ 图：自绘&lt;/em>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>写入点&lt;/th>
&lt;th>挂在哪&lt;/th>
&lt;th>actor&lt;/th>
&lt;th>写什么&lt;/th>
&lt;th>不写什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>计划成立时&lt;/td>
&lt;td>planner 的结构化输出通过 schema 校验之后&lt;/td>
&lt;td>planner&lt;/td>
&lt;td>plan_created&lt;/td>
&lt;td>模型原始 token 流&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>检查起止时&lt;/td>
&lt;td>执行器进程内，每项检查的前后&lt;/td>
&lt;td>executor&lt;/td>
&lt;td>check_started / check_finished&lt;/td>
&lt;td>Agent 对结果的转述&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>裁决签发时&lt;/td>
&lt;td>verifier 出结论、放行器动动作&lt;/td>
&lt;td>verifier&lt;/td>
&lt;td>verdict_issued&lt;/td>
&lt;td>覆盖 planner 的记录&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个坑都在挂的位置上。&lt;/p>
&lt;p>**计划写入要在 schema 校验之后。**校验之前写，Agent 输出里每段格式垃圾都进审计表。审计表被噪声淹没后没人再信它——这比没有审计更糟。&lt;/p>
&lt;p>**check_finished 只能由执行器写。**这是设计篇&amp;quot;Agent 承诺和执行器事实分开&amp;quot;的实现面：Agent 在上下文里说&amp;quot;测试通过了&amp;quot;不算数，执行器进程里那行代码写下的才算。执行器与 Agent 同进程时，至少保证写入点在执行函数内部，而不是在 Agent 转述的文本后面。&lt;/p>
&lt;p>**审计写不进去时，决策还能不能继续？**按阶段分两说。v1 的并行阶段，写入失败只把该 attempt 计为&amp;quot;审计缺失&amp;quot;，让缺失率本身成为统计指标；到了将来接管的阶段，写不进审计的 attempt 一律不允许自动放行。哪种都可以，唯独不能静默吞掉失败。&lt;/p>
&lt;p>写入器是全文最简单的代码，唯一值得注意的是幂等键——由业务身份拼出来，重试同一个动作不会写进两条：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">append_event&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">conn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">decision_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">run_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">actor&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_revision&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">policy_version&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">detail&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attempt_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trace_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">key_suffix&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">event_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decision_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">:&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">event_type&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">:&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">key_suffix&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="c1"># 确定性幂等键&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">conn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;INSERT OR IGNORE INTO decision_events &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;(event_id, decision_id, run_id, attempt_id, event_type, actor,&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34; source_revision, policy_version, trace_id, occurred_at, detail)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34; VALUES (?,?,?,?,?,?,?,?,?,?,?)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">event_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">decision_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">run_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attempt_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event_type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">actor&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">source_revision&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">policy_version&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trace_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">timezone&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">utc&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">detail&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">conn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commit&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>幂等键的取法和&lt;a class="link" href="https://www.zata.cc/p/agent-production-engineering-handbook/" >全景手册&lt;/a>对工具调用的要求同源：按业务身份（&lt;code>decision_id + event_type + key_suffix&lt;/code>）去重，不按请求 ID。检查类事件里 &lt;code>key_suffix&lt;/code> 就是 &lt;code>check_id&lt;/code>，同一项检查反复重试，&lt;code>check_started&lt;/code> 只落一条。&lt;/p>
&lt;h2 id="四复核器规则在前模型在后">四、复核器：规则在前，模型在后
&lt;/h2>&lt;p>复核器最大的落地风险，是写成&amp;quot;再跑一个 Agent 看一遍&amp;quot;——那就成了第二个 planner，共享同样的盲点，还多付一遍模型钱。把复核拆成两层，规则层不花钱，不过就升级：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">verify_decision&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pending_revision&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plan&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">first_of&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;plan_created&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 缺它，整份记录无效&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">checks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]:&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">events&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;check_finished&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">missing&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plan&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;checks&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 规则一&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;selected&amp;#34;&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">checks&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">not_passed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">cid&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">cid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">checks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1"># 规则二&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;result&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;passed&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">plan&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;source_revision&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">pending_revision&lt;/span> &lt;span class="c1"># 规则三&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">unexplained&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">plan&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;checks&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 规则四&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;skipped&amp;#34;&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;reason&amp;#34;&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">missing&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">not_passed&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">stale&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">unexplained&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">escalate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">missing&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">not_passed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stale&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">unexplained&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">model_coverage_review&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plan&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">events&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 规则全过，才问模型覆盖面&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>四条规则各自挡一种&amp;quot;结果是绿的、判断仍然错&amp;quot;：&lt;strong>规则一&lt;/strong>挡&amp;quot;计划写了但没跑完&amp;quot;；&lt;strong>规则二&lt;/strong>挡 &lt;code>unavailable&lt;/code>、&lt;code>failed&lt;/code> 被当通过——开场例子里冒烟拿不到凭据，裁决只能 escalate，就是这条在起作用；&lt;strong>规则三&lt;/strong>挡&amp;quot;检查的是 A、要合并的是 B&amp;quot;；&lt;strong>规则四&lt;/strong>挡跳过不留理由。规则四其实应该在 &lt;code>plan_created&lt;/code> 写入时就被 schema 拦下，复核器再查一遍，是因为写入端校验会随时间漂移，读取端要有独立的防线。&lt;/p>
&lt;p>规则层全过，才进第二层。模型复核只回答规则回答不了的问题：&lt;strong>计划对风险事实的覆盖面&lt;/strong>。&amp;ldquo;改了连接池配置，计划里却没有真实数据库冒烟&amp;quot;这类漏边界，规则查不出来，模型或人可以。第二层的产出同样是一条 &lt;code>verdict_issued&lt;/code>，&lt;code>actor=verifier&lt;/code>，引用同一个 &lt;code>decision_id&lt;/code>，不覆盖任何旧记录。verdict 只需要两个值：支持放行、升级人工——&amp;ldquo;拦截&amp;quot;不是它的职责，规则层已经拦了。&lt;/p>
&lt;p>防共享盲点的最低成本做法：verifier 的提示词版本、甚至模型路由，都和 planner 不同，且两者的 &lt;code>policy_version&lt;/code> 都写进了记录。事后能看出&amp;quot;同一个模型自己复核自己&amp;quot;这种结构缺陷。&lt;/p>
&lt;h2 id="五降级判据比记录格式更重要">五、降级判据比记录格式更重要
&lt;/h2>&lt;p>并行阶段的目标不是&amp;quot;验证审计好用&amp;rdquo;，是&lt;strong>把固定门禁和决策审计放在同一批任务上，量化它们在哪儿不一致&lt;/strong>。每个 attempt 两边各有结论，落在四个象限：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-implementation/images/index/divergence-quadrant.svg"
loading="lazy"
alt="分歧四象限与降级判据"
>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>固定门禁拦下&lt;/th>
&lt;th>固定门禁放行&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>决策审计放行&lt;/strong>&lt;/td>
&lt;td>漏报候选——最危险的象限，逐条人工复检&lt;/td>
&lt;td>一致放行，正常交付，抽样复核&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>决策审计拦截&lt;/strong>&lt;/td>
&lt;td>一致拦截，抽查确认拦截理由&lt;/td>
&lt;td>误报候选——计入审计的误拦成本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>分歧统计一条 SQL 就够：&lt;code>gate_outcomes&lt;/code> 记固定门禁结果，&lt;code>audit_outcomes&lt;/code> 从 &lt;code>verdict_issued&lt;/code> 聚合出 &lt;code>passed&lt;/code>（verdict 为&amp;quot;支持放行&amp;rdquo;）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">gate_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attempts&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SUM&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">blocked&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">passed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">audit_missed&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SUM&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">blocked&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">passed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">audit_false_block&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gate_outcomes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">audit_outcomes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">USING&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attempt_id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">gate_name&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>（SQLite 3 的布尔求和写法；PostgreSQL 换成 &lt;code>COUNT(*) FILTER (WHERE …)&lt;/code>。）&lt;/p>
&lt;p>什么时候允许决策审计接管某个固定门禁？三个条件必须&lt;strong>同时&lt;/strong>满足，并且要写进试点协议、带上样本量下限：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>漏报候选归零&lt;/strong>。人工逐条复检这个象限，没有一条真实漏网。样本量下限按该门禁的历史拦截率反推——拦得越少的门禁，需要越长的并行期。&lt;/li>
&lt;li>&lt;strong>误报候选率不高于固定门禁自己的误拦率&lt;/strong>。否则不是升级，只是把可见的脚本误报换成不易发现的判断误报。&lt;/li>
&lt;li>&lt;strong>分歧可归因&lt;/strong>。每条分歧都能落到具体原因：策略版本差、检查覆盖差、环境不可用。归因不出的分歧占比高，说明记录本身还不可复核——先修记录，别谈接管。&lt;/li>
&lt;/ol>
&lt;p>三个条件连续 N 轮（N 写进协议）同时满足，才把&lt;strong>这一项&lt;/strong>门禁从阻断降为提示，逐项降、不打包。迁移链完整性这种失误代价极高的规则，可能永远不该降。&lt;/p>
&lt;p>并行期里 &lt;code>decision_invalidated&lt;/code> 同样要写：待合并分支更新导致 &lt;code>source_revision&lt;/code> 变化、&lt;code>policy_version&lt;/code> 升级，都追加一条失效事件。放行器只认&amp;quot;verdict 绑定的 revision 等于当前待放行的 revision&amp;quot;。&lt;/p>
&lt;p>判据比记录格式重要，这句话值得单独一段：&lt;strong>没有判据的并行记录，只是一份更贵的日志。&lt;/strong>&lt;/p>
&lt;h2 id="六审计存储自身会坏读取端怎么认账">六、审计存储自身会坏：读取端怎么认账
&lt;/h2>&lt;p>设计篇提过 &lt;code>history_complete&lt;/code>，落到读取端是一个便宜的校验函数：拿一个 &lt;code>decision_id&lt;/code> 的事件序列，检查它以 &lt;code>plan_created&lt;/code> 开头、以 &lt;code>verdict_issued&lt;/code> 结尾、每个 &lt;code>check_started&lt;/code> 都有配对的 &lt;code>check_finished&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">history_complete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="c1"># events 已按 seq 排序、按 decision_id 过滤&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">types&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">events&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">types&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">types&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;plan_created&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s2">&amp;#34;verdict_issued&amp;#34;&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">types&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="s2">&amp;#34;decision_invalidated&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">types&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">started&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">events&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;check_started&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">finished&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;detail&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">events&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;check_finished&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">started&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">finished&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意校验按 &lt;code>decision_id&lt;/code> 分组做，不按全局 &lt;code>seq&lt;/code>——并发之下全局 &lt;code>seq&lt;/code> 本来就有空洞，完整性是每个决策自己的性质。&lt;/p>
&lt;p>校验不过，这个 decision 的证据就当不存在：放行器走&amp;quot;无审计&amp;quot;路径（人工或固定门禁），并把&amp;quot;无审计放行&amp;quot;本身记下来。沉默缺失被误读成通过，是审计存储最坏的失败方式，宁可显式降级。&lt;/p>
&lt;p>单机 SQLite 的丢失风险要诚实写进 v1 的边界：它没解决多副本，靠的是这张表很小（一次决策几 KB）、全量备份便宜、备份脚本可以拿校验函数自检。真正占空间的是 evidence 原文，那是另一套保留策略——审计表只存 &lt;code>evidence_ref&lt;/code> 和摘要，本来就不该跟着膨胀。&lt;/p>
&lt;h2 id="七我的判断">七、我的判断
&lt;/h2>&lt;ul>
&lt;li>v1 的合理成本上限是&lt;strong>一两周&lt;/strong>。做的过程中一旦发现要起独立服务、要改现有 CI 的控制流，说明做重了，回第一节的表对照。&lt;/li>
&lt;li>决策审计改变的不是风险本身，是漏报的&lt;strong>可发现性&lt;/strong>：没有它，&amp;ldquo;Agent 跳过了该跑的检查&amp;quot;只能在事故后追查；有了并行期，漏报候选在统计表里按周可见。&lt;/li>
&lt;li>反过来，如果 Agent 只提建议、放行始终由人或固定规则拍板，trace 加审批日志就够了。这套东西的成本，只在放行权真的开始转移时回本。&lt;/li>
&lt;/ul>
&lt;h2 id="速查落地顺序与每步的完成判据">速查：落地顺序与每步的完成判据
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>步骤&lt;/th>
&lt;th>交付物&lt;/th>
&lt;th>完成判据&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1. 建表&lt;/td>
&lt;td>DDL + 追加式触发器&lt;/td>
&lt;td>UPDATE / DELETE 被 ABORT&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2. 写入点&lt;/td>
&lt;td>三个钩子 + 幂等写入器&lt;/td>
&lt;td>同一事件重试只落一条&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3. 读取校验&lt;/td>
&lt;td>按 decision_id 的完整性函数&lt;/td>
&lt;td>缺头、缺尾、缺配对的序列被判无效&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4. 规则复核器&lt;/td>
&lt;td>四条确定性规则&lt;/td>
&lt;td>任一不过即 escalate，不进模型层&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5. 并行运行&lt;/td>
&lt;td>分歧四象限统计&lt;/td>
&lt;td>每个门禁的漏报/误报候选都有数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6. 判据与降级&lt;/td>
&lt;td>写进试点协议的三个条件&lt;/td>
&lt;td>逐项降级，永不打包&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一句话收尾：&lt;strong>先让它记录分歧，再让它参与放行——降级判据写清楚之前，决策审计只是一份更贵的日志。&lt;/strong>&lt;/p>
&lt;h2 id="延伸阅读">延伸阅读
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-decision-audit-and-tracing/" >Agent 决策审计：它与 Tracing 的关系&lt;/a>——设计篇：身份模型、记录字段与决策、Trace 的边界&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-production-engineering-handbook/" >Agent 生产工程全景手册：从 Runtime 到业务闭环&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/" >Agent Tracing 基础：Trace、Span 与 OpenTelemetry 埋点&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Session、Thread、Run：一条消息为什么是一个 Run</title><link>https://www.zata.cc/p/session-thread-run/</link><pubDate>Wed, 23 Sep 2026 23:11:14 +0800</pubDate><guid>https://www.zata.cc/p/session-thread-run/</guid><description>&lt;img src="https://www.zata.cc/p/session-thread-run/images/index/index.svg" alt="Featured image of post Session、Thread、Run：一条消息为什么是一个 Run" />&lt;p>把 Run 表定义摊开看，有个细节很别扭：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentRunModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Base&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Mapped&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Run ID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">thread_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Mapped&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 外键 -&amp;gt; conversation_thread，NOT NULL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Mapped&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 外键 -&amp;gt; chat_session，允许为空&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Mapped&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">status&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Mapped&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一张表上挂了两个&amp;quot;会话类&amp;quot;外键，一个必填、一个可空。第一眼看上去像是设计没收敛——要么统一用 session，要么统一用 thread，为什么两个都要？&lt;/p>
&lt;p>真正解释这件事的，是那个&lt;strong>允许为空&lt;/strong>的字段。它存在的理由，不是一个空值占位，而是&amp;quot;一次执行可以不隶属于任何对话&amp;quot;。等子 Agent 出现时，这个空位会立刻被填满意义。&lt;/p>
&lt;h2 id="一三个名字三种事实">一、三个名字，三种事实
&lt;/h2>&lt;p>这三层不是同义词，也不是新旧替代关系，它们各自是一类问题的权威答案：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层&lt;/th>
&lt;th>表&lt;/th>
&lt;th>谁看得见&lt;/th>
&lt;th>回答的问题&lt;/th>
&lt;th>生命周期&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Session&lt;/td>
&lt;td>&lt;code>chat_session&lt;/code>&lt;/td>
&lt;td>用户&lt;/td>
&lt;td>这是哪一段对话&lt;/td>
&lt;td>长期，可继续&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Thread&lt;/td>
&lt;td>&lt;code>conversation_thread&lt;/code>&lt;/td>
&lt;td>只有后端&lt;/td>
&lt;td>模型的连续记忆装在哪&lt;/td>
&lt;td>与对话同寿&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Run&lt;/td>
&lt;td>&lt;code>agent_run&lt;/code>&lt;/td>
&lt;td>用户（状态/取消）、管理员（审计）&lt;/td>
&lt;td>这是哪一次执行、用了什么、跑到哪&lt;/td>
&lt;td>单次，有终态&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Session 用户可见的一段对话：标题、owner、历史消息
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Thread 模型的连续记忆（checkpointer 线程），前端不维护
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Run 一次执行：快照 / 状态机 / 事件流 / 取消 / 计费
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/session-thread-run/images/three-layers.svg"
loading="lazy"
alt="会话、记忆、执行三层身份"
>&lt;/p>
&lt;p>几处容易混的地方，一次说清：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Session 是产品概念&lt;/strong>。它管标题、归属、消息顺序；删掉一个 Session 意味着用户不再看到那段对话。&lt;/li>
&lt;li>&lt;strong>Thread 是实现细节&lt;/strong>。Session 到 Thread 的映射在后端完成，客户端不需要传也不需要存 &lt;code>thread_id&lt;/code>。它存在的唯一理由是：模型需要一个连续的上下文容器。&lt;/li>
&lt;li>&lt;strong>Run 是事实源&lt;/strong>。它不属于&amp;quot;对话&amp;quot;这个层级，而属于&amp;quot;执行&amp;quot;这个层级：一次输入落进去，出来的是带序号的事件流和一个终态。&lt;/li>
&lt;/ul>
&lt;p>所以那条别扭的表定义翻译过来是：&lt;strong>每次执行必须属于一个记忆线程，但不一定属于一段用户对话。&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>这里要跟另一套常见用法区分开：在审计与可观测性的语境里，&lt;code>run_id&lt;/code> 有时指&amp;quot;跨暂停、重试仍稳定的业务任务身份&amp;quot;，再用 &lt;code>attempt_id&lt;/code>、&lt;code>trace_id&lt;/code> 区分每一次尝试（可参考本系列的《Agent 决策审计：它与 Tracing 的关系》）。本文说的 Run 是&lt;strong>执行层&lt;/strong>的单位：单次、有终态、不可重开——想重跑，就是一次新的 Run。两套用法服务的问题不同，混用会在&amp;quot;重试算不算同一次&amp;quot;上直接打架。&lt;/p>
&lt;/blockquote>
&lt;h2 id="二一条消息就是一个-run">二、一条消息，就是一个 Run
&lt;/h2>&lt;p>用户视角里，一次交互是&amp;quot;在对话里发了一句话&amp;quot;。到 API 这一层，它变成一次 Run 创建：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-http" data-lang="http">&lt;span class="line">&lt;span class="cl">&lt;span class="err">POST /sessions/{session_id}/messages
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">→ 202 { &amp;#34;message_id&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;run_id&amp;#34;: &amp;#34;run_xxx&amp;#34;, &amp;#34;events_url&amp;#34;: &amp;#34;/api/agent-runs/run_xxx/events&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err"> &amp;#34;agent_id&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;agent_name&amp;#34;: &amp;#34;...&amp;#34; }
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>返回体里同时出现 &lt;code>session_id&lt;/code>（在 URL 上）和 &lt;code>run_id&lt;/code>（在响应里），这不是冗余，而是两个层级各自交回自己的凭据：前者继续指向那段对话，后者指向刚刚开始的这次执行——订阅事件、取消、查状态，用的全是它。&lt;/p>
&lt;p>历史消息也保留了这层关系：assistant 消息上挂一个唯一的 &lt;code>run_id&lt;/code> 回指生成它的那次执行。这样当用户在同一个对话里换了 Agent，回看历史时依然能说出&amp;quot;这条回答当时是哪个 Agent 给的&amp;quot;，而不是拿当前绑定去反推过去。&lt;/p>
&lt;p>而 Session 上的 &lt;code>agent_id&lt;/code> 只是&lt;strong>初始默认值&lt;/strong>：同一段对话的后续每条消息都可以逐次切换 Agent，切换只影响下一次发送。这一点是后面所有讨论的前提——如果 Session 就等于 Run，这条规则根本无法表达。&lt;/p>
&lt;h2 id="三为什么不能一个-session-一个-run-id">三、为什么不能&amp;quot;一个 Session 一个 Run ID&amp;quot;
&lt;/h2>&lt;p>合并成一层听起来更简洁，但会立刻遇到四个对不上的地方：&lt;/p>
&lt;p>&lt;strong>1. 快照必须逐次冻结。&lt;/strong> 每个 Run 在创建事务里固化两份不可变快照：Agent 执行快照和资料上下文快照。它们记录的是&amp;quot;这一次用的版本&amp;quot;，而不是&amp;quot;这段对话现在用什么&amp;quot;。会话级 ID 没有位置安放&amp;quot;逐次&amp;quot;这个语义。&lt;/p>
&lt;p>&lt;strong>2. 终态是执行的属性，不是对话的。&lt;/strong> Run 的状态机有唯一终态且不可重开：&lt;code>succeeded&lt;/code>、&lt;code>failed&lt;/code>、&lt;code>cancelled&lt;/code>、&lt;code>interrupted&lt;/code>。一次执行失败，不应该把整段还能继续聊的对话标记为失败。&lt;/p>
&lt;p>&lt;strong>3. 幂等键挂在 Run 上。&lt;/strong> 创建 Run 必须带 &lt;code>Idempotency-Key&lt;/code>，唯一约束是 &lt;code>(owner_id, idempotency_key_hash)&lt;/code>。同一用户、同一个 key、同一份请求会重放原来那次 Run；同样的 key 配不同请求则稳定返回 &lt;code>idempotency_conflict&lt;/code>。重放的单位是&amp;quot;那一次执行&amp;quot;，不是&amp;quot;那段对话&amp;quot;。&lt;/p>
&lt;p>&lt;strong>4. 取消、租约、事件游标都是 per-Run 的。&lt;/strong> &lt;code>GET /api/agent-runs/{run_id}/events?after_seq=N&lt;/code> 的游标是 Run 内的序号；执行租约（lease）也写在 Run 上。刷新续流用 &lt;code>Last-Event-ID&lt;/code> 接上原来那条流——如果一段对话只有一条流，换 Agent、重试、取消全都会糊在同一根管道里。&lt;/p>
&lt;p>换个角度看得更清楚。挂在 Run 上的这些字段，每一条都在说&amp;quot;这是单次执行的属性&amp;quot;：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>事实&lt;/th>
&lt;th>字段&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>用哪个 Agent、什么版本&lt;/td>
&lt;td>&lt;code>agent_id&lt;/code>、&lt;code>agent_execution_snapshot&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>看的哪些资料&lt;/td>
&lt;td>&lt;code>context_pack_snapshot&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>跑到哪一步&lt;/td>
&lt;td>&lt;code>status&lt;/code>、&lt;code>last_event_seq&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>谁在跑&lt;/td>
&lt;td>&lt;code>executor_instance_id&lt;/code>、&lt;code>lease_expires_at&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>能不能重放&lt;/td>
&lt;td>&lt;code>idempotency_key_hash&lt;/code>、&lt;code>request_checksum&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>结果与失败&lt;/td>
&lt;td>&lt;code>final_message_id&lt;/code>、&lt;code>error&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>时间线&lt;/td>
&lt;td>&lt;code>created_at&lt;/code> / &lt;code>started_at&lt;/code> / &lt;code>finished_at&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="四run-的状态机把事实和活性分开">四、Run 的状态机：把&amp;quot;事实&amp;quot;和&amp;quot;活性&amp;quot;分开
&lt;/h2>&lt;p>既然执行是单位，状态就必须说准。非终态有三个，终态有四个：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">queued ─→ running ─→ succeeded
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ │ └→ failed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ├─────→ cancelled
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─────────┴─────→ interrupted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cancelling 是 running 到 cancelled 之间的过渡态
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三条容易踩的语义：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>租约不是历史事实。&lt;/strong> &lt;code>lease_expires_at&lt;/code> 只表示&amp;quot;执行者还活着&amp;quot;，它过期不代表这次执行失败了。失联的 Run 由 reconciliation 追加 &lt;code>run.interrupted&lt;/code> 收口——而且&lt;strong>不会自动重跑模型&lt;/strong>。想重跑，是人的决定，是一次新的 Run。&lt;/li>
&lt;li>&lt;strong>取消只追加一次终态。&lt;/strong> 用户取消先写 &lt;code>run.cancelling&lt;/code>，运行中的执行器收到信号后回收当前图或模型流，恰好追加一次 &lt;code>run.cancelled&lt;/code>（带 &lt;code>external_stop_confirmed: true&lt;/code>）。确认取消之后，不会再出现 &lt;code>run.completed&lt;/code> 或 &lt;code>run.failed&lt;/code>。&lt;/li>
&lt;li>&lt;strong>终态不可重开。&lt;/strong> 一个 Run 进入终态，它的故事就结束了。任何&amp;quot;再来一次&amp;quot;都是新 Run、新快照、新的幂等键。&lt;/li>
&lt;/ul>
&lt;p>这套规则的价值不在单 Agent 场景——那里一条流从头读到尾就够了。它的价值在于：&lt;strong>当你需要并行、需要重试、需要给一部分工作单独踩刹车时，你必须有一个不可重开的最小单位。&lt;/strong>&lt;/p>
&lt;h2 id="五委派那天这个空字段被填满了">五、委派那天，这个空字段被填满了
&lt;/h2>&lt;p>现在回到开头那个可空的 &lt;code>session_id&lt;/code>。&lt;/p>
&lt;p>子 Agent 委派落地后，父 Run 在执行过程中会请求平台创建 child Run。child 是&lt;strong>另一次真实执行&lt;/strong>：目标 Agent 不同、快照不同、独立线程、独立事件流、独立状态与独立取消入口。它满足&amp;quot;执行&amp;quot;的全部定义，但不隶属于用户的那段对话——用户没有在对话里发过那句话。&lt;/p>
&lt;p>所以 child 的 &lt;code>session_id&lt;/code> 就是 &lt;code>NULL&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">root Run session_id = sess_xxx 用户可见，出现在会话历史
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ child A session_id = NULL 内部执行，只有树里能看到
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ child B session_id = NULL
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>父子关系不表达在 Session 上，而是靠 Run 表自己的两个自引用字段：&lt;code>root_run_id&lt;/code> 指向所属的根执行，&lt;code>parent_run_id&lt;/code> 指向直接父节点。运行树因此可以从持久化记录里重建：刷新页面后树还在，服务重启后未完成节点统一收敛为 &lt;code>interrupted&lt;/code>。&lt;/p>
&lt;p>对前端而言，这个分层直接决定了工作量：原来&amp;quot;发一次消息 = 拿到一个 run_id = 连一条 &lt;code>events_url&lt;/code>&amp;ldquo;的假设，要变成&amp;quot;先读树，再按每个节点的 &lt;code>events_url&lt;/code> 各自订阅&amp;rdquo;。一条流变多条流，正是因为一次对话里现在真的有了多次执行。&lt;/p>
&lt;p>反过来验证一下：如果当初把 Run 合并进 Session，委派就得在 Session 上开洞——一段对话要同时容纳多个 Agent、多份快照、多个终态、多个取消目标。这个洞会一直开到把 Session 拆回去为止。&lt;/p>
&lt;h2 id="几点收获">几点收获
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>ID 的层级应该跟生命周期对齐，而不是跟界面层级对齐。&lt;/strong> 用户看到的是对话，系统需要的是执行；把两者合成一个 ID，短期少一个字段，长期处处要打补丁。&lt;/li>
&lt;li>&lt;strong>允许为空的外键往往在讲一个未来的故事。&lt;/strong> &lt;code>session_id&lt;/code> 可空不是随手留的，它提前承认了&amp;quot;执行可以不属于任何对话&amp;quot;这类存在。&lt;/li>
&lt;li>&lt;strong>状态机要区分事实与活性。&lt;/strong> 心跳、租约、连接状态说明的是&amp;quot;谁还在跑&amp;quot;；状态、事件流说明的是&amp;quot;发生了什么&amp;quot;。把两者混在一起，就会出现&amp;quot;进程没了所以这次执行失败了&amp;quot;这种错误结论。&lt;/li>
&lt;li>&lt;strong>幂等、取消、游标、计费都需要一个不可重开的最小单位。&lt;/strong> 它们四个的需求指向同一个答案：Run。先把这层定清楚，后面加并行、加委派、加预算，都是在这个单位上做加法。&lt;/li>
&lt;/ul>
&lt;p>回到最开始那张表：两个外键不是没收敛的设计，而是两层身份各自在场的证据。一个必填，因为执行总需要记忆；一个可空，因为执行不一定需要观众。&lt;/p></description></item><item><title>Agent 生产工程全景手册：从 Runtime 到业务闭环</title><link>https://www.zata.cc/p/agent-production-engineering-handbook/</link><pubDate>Wed, 23 Sep 2026 16:48:35 +0800</pubDate><guid>https://www.zata.cc/p/agent-production-engineering-handbook/</guid><description>&lt;img src="https://www.zata.cc/p/agent-production-engineering-handbook/images/index/index.svg" alt="Featured image of post Agent 生产工程全景手册：从 Runtime 到业务闭环" />&lt;blockquote>
&lt;p>本文是一份 Agent 工程知识总纲，目标是把“能跑一个 Agent”推进到“能让 Agent 系统在真实任务中稳定运行，并能证明它有效”。它覆盖架构、运行、验证、治理和业务结果；具体框架的 API 与云产品参数应以对应版本的官方文档为准。&lt;/p>
&lt;/blockquote>
&lt;h2 id="先看全貌生产级-agent-系统是什么">先看全貌：生产级 Agent 系统是什么
&lt;/h2>&lt;p>生产级 Agent 不是一个模型调用循环，而是一套围绕任务运行的系统。模型负责在有限上下文中提出下一步行动；平台负责验证、执行、记录和约束这些行动；用户或业务系统负责定义什么结果才算完成。&lt;/p>
&lt;p>先把系统划分成三个边界：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>控制面&lt;/strong>：身份、策略、配置版本、模型路由、配额、队列和工作流定义。它决定谁可以发起什么任务，以及任务如何调度。&lt;/li>
&lt;li>&lt;strong>执行面&lt;/strong>：Run Controller、Agent Runtime、工具网关、Worker 和沙箱。它在受限环境中推进任务，并把状态写回持久化存储。&lt;/li>
&lt;li>&lt;strong>证据面&lt;/strong>：事件、Trace、审计、评测结果、成本和业务指标。它回答任务发生了什么、是否合格、出了问题如何追查。&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-production-engineering-handbook/images/agent-platform-layers.svg"
loading="lazy"
alt="生产级 Agent 平台的控制面、执行面与证据面"
>&lt;/p>
&lt;div class="mermaid">
flowchart LR
U[用户 / Issue / API] --&amp;gt; G[入口与身份认证]
G --&amp;gt; Q[任务队列与调度]
Q --&amp;gt; O[工作流编排]
O --&amp;gt; R[Agent Runtime]
R --&amp;gt; M[模型与上下文]
R --&amp;gt; T[工具 / MCP / 沙箱]
T --&amp;gt; D[代码库 / 数据 / 外部系统]
R --&amp;gt; E[事件、日志、Trace]
E --&amp;gt; V[评测、审计、成本与告警]
V --&amp;gt; O
O --&amp;gt; H[人工审批 / 交付]
H --&amp;gt; U
&lt;/div>
&lt;p>一个典型 Coding Agent 任务会经过：接收 Issue、检查权限、创建隔离工作区、读取代码和约束、调用模型规划、执行工具、运行测试、整理证据、等待必要审批、创建 PR，最后记录结果。任务可能持续数分钟甚至数小时，中间会遇到模型超时、工具异常、用户取消、机器重启和预算耗尽。因此，系统设计必须覆盖任务全生命周期，而不只是“模型返回了答案”。&lt;/p>
&lt;h3 id="能力层次知道会做做过生产能负责">能力层次：知道、会做、做过生产、能负责
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层级&lt;/th>
&lt;th>证据&lt;/th>
&lt;th>例子&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>知道&lt;/td>
&lt;td>能说清概念、适用范围和风险&lt;/td>
&lt;td>解释幂等、工具权限和离线评测&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>会做&lt;/td>
&lt;td>独立完成可复现实验或功能&lt;/td>
&lt;td>实现可恢复的 Agent Run，跑固定任务集&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>做过生产&lt;/td>
&lt;td>真实用户持续使用，处理过故障和回归&lt;/td>
&lt;td>有任务量、成功率、接管率、事故复盘&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>能负责&lt;/td>
&lt;td>能做架构取舍、设指标、推动协作并承担结果&lt;/td>
&lt;td>设计多租户平台并对质量、成本和业务价值负责&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>“做过生产”不是部署到一台服务器就算。它意味着有真实使用、有运行数据、有故障处理、有变更回归，也知道系统在哪些情况下应该拒绝执行或交给人。&lt;/p>
&lt;h2 id="1-agent-runtime把模型循环变成可管理的任务执行">1. Agent Runtime：把模型循环变成可管理的任务执行
&lt;/h2>&lt;h3 id="11-runtime-的边界">1.1 Runtime 的边界
&lt;/h3>&lt;p>Agent Runtime 是一次 Agent 运行的执行环境与控制层。它负责把输入、上下文、模型、工具和状态组织成受控循环，直到任务成功、失败、被取消或达到限制。&lt;/p>
&lt;p>一次基础循环如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">接收输入与权限上下文
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 构造模型请求
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 解析模型输出（最终答复或工具调用）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 校验工具参数与权限
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 执行工具并记录结果
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 更新任务状态和上下文
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 继续循环，直到终止条件成立
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Runtime 应明确区分“模型建议做什么”和“平台允许执行什么”。模型输出不是授权凭证；任何读写工具都需要由 Runtime 按用户、租户、任务和资源范围再次鉴权。&lt;/p>
&lt;h3 id="12-一次-run-的完整生命周期">1.2 一次 Run 的完整生命周期
&lt;/h3>&lt;p>Run 是有身份、有边界、有预算、有结果的一次任务尝试。一次用户目标可能因为重试或人工修复而有多个 Run；不要把“业务任务”和“某次执行尝试”混成一个对象。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">创建请求 → 鉴权/策略检查 → 排队 → 分配 Worker → 准备沙箱
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 运行 Agent Loop ↔ 调用模型/工具
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 验收与风险检查 → 成功/等待审批/失败/取消/超时
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 保存交付物 → 清理资源 → 汇总指标与审计
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/agent-production-engineering-handbook/images/run-lifecycle.svg"
loading="lazy"
alt="一次 Run 的生命周期与恢复边界"
>&lt;/p>
&lt;p>建议区分以下对象：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>对象&lt;/th>
&lt;th>生命周期&lt;/th>
&lt;th>应保存的核心字段&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Task&lt;/td>
&lt;td>用户希望完成的业务目标&lt;/td>
&lt;td>来源、需求、仓库/资源、验收条件、发起者&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Run&lt;/td>
&lt;td>对 Task 的一次执行尝试&lt;/td>
&lt;td>状态、配置版本、模型、预算、起止时间、终止原因&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Step&lt;/td>
&lt;td>Run 中可观察、可重试或可审批的工作单元&lt;/td>
&lt;td>类型、输入/输出引用、状态、尝试次数、耗时&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Event&lt;/td>
&lt;td>状态变化或重要事实的追加记录&lt;/td>
&lt;td>序号、时间、主体、事件类型、关联对象、脱敏载荷&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Artifact&lt;/td>
&lt;td>运行产生的文件或交付物&lt;/td>
&lt;td>内容摘要、存储位置、访问策略、来源 Step&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>将大块日志和工作区快照放对象存储，以不可变引用关联 Run；关系数据库保存状态与索引。不要把完整代码 diff、长模型输出和二进制文件塞入运行状态行。&lt;/p>
&lt;h3 id="13-runtime-的核心部件">1.3 Runtime 的核心部件
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>部件&lt;/th>
&lt;th>责任&lt;/th>
&lt;th>关键设计问题&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Run Controller&lt;/td>
&lt;td>创建、推进、取消和终止一次运行&lt;/td>
&lt;td>状态转换是否合法？谁能取消？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Model Adapter&lt;/td>
&lt;td>统一模型请求、流式输出、工具调用和错误&lt;/td>
&lt;td>超时、限流、模型差异如何处理？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Context Builder&lt;/td>
&lt;td>选择并组织提示词、历史、检索资料和工具说明&lt;/td>
&lt;td>如何限制 token、隔离不可信内容？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tool Gateway&lt;/td>
&lt;td>校验 schema、权限、配额并分发工具调用&lt;/td>
&lt;td>写操作是否需要审批？如何防止重放？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>State Store&lt;/td>
&lt;td>保存 Run、步骤、事件和检查点&lt;/td>
&lt;td>进程重启后能否恢复？状态如何迁移？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Execution Sandbox&lt;/td>
&lt;td>隔离运行代码、浏览器或命令&lt;/td>
&lt;td>文件、网络、密钥、CPU 和内存边界是什么？&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Observability&lt;/td>
&lt;td>记录结构化事件、指标、Trace 和审计&lt;/td>
&lt;td>能否重建因果链？敏感数据如何脱敏？&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="14-状态机事件与恢复">1.4 状态机、事件与恢复
&lt;/h3>&lt;p>不要只把运行状态存成一个 &lt;code>running=true/false&lt;/code>。至少为任务定义清晰的状态机，例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">queued → running → waiting_approval → running → succeeded
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↘ retrying ↗ ↘ failed / cancelled / timed_out
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>每次状态转换都应有可检查的前置条件，并写入事件记录。建议保存：Run ID、任务输入摘要、配置和提示词版本、模型版本、工具调用、步骤结果、预算消耗、错误分类、检查点和终止原因。事件用于审计和重建过程；检查点用于从安全边界继续运行。二者用途不同，不能只留一份最终对话文本。&lt;/p>
&lt;p>恢复时要区分：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>可安全重试&lt;/strong>：只读请求、带幂等键的操作，或明确没有副作用的步骤。&lt;/li>
&lt;li>&lt;strong>需要对账&lt;/strong>：请求可能已在下游成功，但本地没收到响应。先查下游状态，再决定是否重试。&lt;/li>
&lt;li>&lt;strong>不能自动重试&lt;/strong>：不可逆外部副作用、资金或权限变更等高风险步骤，应暂停并要求人工处理。&lt;/li>
&lt;/ul>
&lt;p>幂等键应覆盖业务任务与操作身份，例如 &lt;code>run_id + step_id + operation&lt;/code>。只按 HTTP 请求 ID 去重，无法阻止同一业务动作在重试时重复发生。&lt;/p>
&lt;p>检查点需要放在语义安全的边界，例如“工具调用已完成且结果已持久化”，而不是任意一段对话文本之后。若在工具执行完成、结果还没写入状态库时进程崩溃，重启后系统无法知道副作用是否发生。这种不确定窗口需要由下游幂等键、查询对账或人工确认来解决。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>故障位置&lt;/th>
&lt;th>恢复动作&lt;/th>
&lt;th>需要防范的问题&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>调用模型前&lt;/td>
&lt;td>从当前步骤继续&lt;/td>
&lt;td>请求重复、上下文版本变化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>模型响应后、尚未执行工具&lt;/td>
&lt;td>保存响应或重发请求&lt;/td>
&lt;td>采样结果变化、重复费用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具执行中&lt;/td>
&lt;td>按工具语义查询/重试/转人工&lt;/td>
&lt;td>下游已成功但本地超时&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具成功、结果未落库&lt;/td>
&lt;td>使用幂等键查询结果&lt;/td>
&lt;td>重复写入或重复发布&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>等待审批&lt;/td>
&lt;td>持久暂停，审批事件到达后恢复&lt;/td>
&lt;td>审批过期、参数被替换&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Worker 被终止&lt;/td>
&lt;td>租约过期后由新 Worker 接管&lt;/td>
&lt;td>两个 Worker 同时执行&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Worker 领取任务时可使用带过期时间的租约。续租失败的旧 Worker 必须停止写入；数据库条件更新或 fencing token 可以阻止失去租约的进程覆盖新执行者的状态。租约本身不能撤销已经发出的外部副作用，因此副作用仍需幂等保护。&lt;/p>
&lt;h3 id="15-限制循环和资源">1.5 限制循环和资源
&lt;/h3>&lt;p>每个 Run 都要有明确预算：最大步骤数、墙钟时间、模型调用次数、输入输出 token、工具调用次数、并发数、CPU/内存、网络访问范围和金钱成本。达到限制时要有确定行为：安全终止、降级、请求用户补充信息，或转人工；不能无限循环或静默超支。&lt;/p>
&lt;p>常见防护包括重复工具调用检测、相同状态循环检测、指数退避和抖动、熔断、请求取消传播、模型与工具级超时、队列背压。重试策略必须设置总预算，避免瞬时故障引发重试风暴。&lt;/p>
&lt;h3 id="16-会话上下文不等于长期记忆">1.6 会话上下文不等于长期记忆
&lt;/h3>&lt;p>短期上下文用于当前 Run 的推理，长期记忆用于跨 Run 保留经过授权、经过筛选的信息。把所有历史直接塞回上下文会增加费用、噪声和隐私风险。上下文构建应显式决定：系统规则、任务输入、可信业务数据、检索资料、历史摘要、工具定义各自的边界和优先级。&lt;/p>
&lt;p>上下文预算需要为模型输出和工具结果留空间；检索结果应带来源与时间；工具返回内容和用户内容都视为不可信数据，不能让它们覆盖系统指令。压缩历史时保留决策、未完成事项、关键证据和引用，而不是只保留一段无法核对的自然语言总结。&lt;/p>
&lt;h3 id="17-工具执行与隔离">1.7 工具执行与隔离
&lt;/h3>&lt;p>工具调用的安全边界至少包括：JSON/schema 校验、身份与资源授权、租户隔离、速率和成本限制、审计记录、超时与取消。工具风险可以分级：只读、可逆写入、不可逆或外部副作用。高风险工具应采用最小权限、人工审批和执行后核验。&lt;/p>
&lt;p>执行不可信代码时，进程隔离不等于完整沙箱。要检查文件系统、网络出口、凭证注入、容器逃逸面、CPU/内存/运行时限额和清理策略。密钥尽量通过短期凭证或受控代理提供，避免写入提示词、日志、环境快照和 Agent 可读文件。&lt;/p>
&lt;p>延伸阅读：&lt;a class="link" href="https://www.zata.cc/p/agent-runtime-explained/" >Agent Runtime 详解&lt;/a>、&lt;a class="link" href="https://www.zata.cc/p/agent-%E6%B2%99%E7%AE%B1%E9%80%89%E5%9E%8B%E6%8C%87%E5%8D%97%E9%9A%94%E7%A6%BB%E8%BE%B9%E7%95%8C%E4%BA%A7%E5%93%81%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%88%A4%E6%96%AD%E6%A0%87%E5%87%86/" >Agent 沙箱选型指南&lt;/a>、&lt;a class="link" href="https://www.zata.cc/p/agent-run-%E6%B5%81%E5%BC%8F%E5%8D%8F%E8%AE%AE%E4%BA%8B%E4%BB%B6%E6%BA%AF%E6%BA%90sse-%E6%8A%95%E5%BD%B1%E4%B8%8E%E6%96%AD%E7%BA%BF%E6%81%A2%E5%A4%8D/" >Agent Run 流式协议&lt;/a>。&lt;/p>
&lt;h2 id="2-工作流调度与交付">2. 工作流、调度与交付
&lt;/h2>&lt;h3 id="21-工作流与-agent-loop-的区别">2.1 工作流与 Agent Loop 的区别
&lt;/h3>&lt;p>Agent Loop 是一次运行内部“观察—推理—行动”的循环。工作流是更高层的业务过程，规定多个步骤、条件、并行分支、人工检查和失败补偿如何衔接。调度器负责何时、在哪个执行资源上启动工作；队列负责缓冲和分发工作。它们相关，但不是同一个组件。&lt;/p>
&lt;p>适合用确定性工作流的部分：权限检查、测试、审批、发布、通知、数据迁移。适合交给 Agent 判断的部分：从 Issue 提取意图、探索代码、提出修改方案、解释失败。把所有步骤都交给模型，会让本应确定的控制流程变得不可预测。&lt;/p>
&lt;h3 id="22-工作流的基本要素">2.2 工作流的基本要素
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>触发器&lt;/strong>：Issue、Webhook、定时任务、API 请求或人工启动。&lt;/li>
&lt;li>&lt;strong>步骤与依赖&lt;/strong>：顺序、并行、条件分支、循环和子流程。&lt;/li>
&lt;li>&lt;strong>状态&lt;/strong>：每步输入、输出、版本、开始结束时间和执行结果。&lt;/li>
&lt;li>&lt;strong>等待点&lt;/strong>：人工审批、外部事件、限流恢复或长时间等待。&lt;/li>
&lt;li>&lt;strong>失败策略&lt;/strong>：重试、补偿、回滚、暂停、转人工或终止。&lt;/li>
&lt;li>&lt;strong>版本&lt;/strong>：流程定义和执行中的 Run 使用哪个版本；升级如何兼容旧 Run。&lt;/li>
&lt;li>&lt;strong>可见性&lt;/strong>：用户能看到进展、暂停原因、待审批动作和最终证据。&lt;/li>
&lt;/ul>
&lt;p>对长任务，应将工作流状态持久化，不能依赖一个常驻进程的内存。工作进程可随时被替换；任务恢复依靠数据库、事件日志、队列和明确的步骤边界。&lt;/p>
&lt;h3 id="23-如何选择同步执行队列与持久化工作流">2.3 如何选择同步执行、队列与持久化工作流
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>执行方式&lt;/th>
&lt;th>适合场景&lt;/th>
&lt;th>主要代价&lt;/th>
&lt;th>必须考虑&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>同步请求&lt;/td>
&lt;td>几秒内完成、没有长等待、失败可以直接返回&lt;/td>
&lt;td>请求连接占用、超时传播复杂&lt;/td>
&lt;td>网关超时、客户端断线、取消语义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>队列 + Worker&lt;/td>
&lt;td>异步任务、需要削峰和并发控制&lt;/td>
&lt;td>需要状态查询和结果通知&lt;/td>
&lt;td>重复投递、死信、背压、公平调度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>持久化工作流引擎&lt;/td>
&lt;td>长任务、等待审批、多步骤副作用、跨小时恢复&lt;/td>
&lt;td>状态模型和版本治理更复杂&lt;/td>
&lt;td>历史兼容、活动超时、重放确定性&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>不要为“可能以后会很复杂”先引入重量级引擎。先根据任务时长、恢复要求、等待点和副作用数量选型；一旦需要长时间等待、可靠恢复和人工审批，内存里的简单队列通常就不够了。&lt;/p>
&lt;h3 id="24-工作流定义应版本化">2.4 工作流定义应版本化
&lt;/h3>&lt;p>每次执行开始时固定工作流版本、工具策略版本、提示词版本和模型路由版本。运行中的流程不要被新配置静默改变。升级策略通常有三种：让旧 Run 按旧版本继续；提供显式迁移函数；或安全暂停旧 Run 并由人工决定。修改审批条件、权限规则或数据格式时，要把迁移纳入发布评审。&lt;/p>
&lt;p>工作流活动应有确定的输入输出契约。活动可以是普通服务函数，也可以是 Agent 子任务；Agent 子任务也要有超时、预算、输入范围、验收条件和明确的失败结果。&lt;/p>
&lt;h3 id="25-幂等补偿和人工门禁">2.5 幂等、补偿和人工门禁
&lt;/h3>&lt;p>队列通常提供至少一次投递，因此消费者必须假设同一消息会重复到达。通过 Run/Step 唯一键、状态条件更新和下游幂等键实现去重。若一个流程包含多个外部副作用，数据库事务通常无法覆盖所有系统；应采用 Saga 思路，为已完成步骤定义补偿操作，或在失败时暂停并人工对账。&lt;/p>
&lt;p>人工审批不是弹窗装饰，而是流程中的持久状态。审批记录应绑定具体动作、目标资源、参数摘要、发起者、审批者、时间和策略版本；动作内容发生变化时，旧审批不能自动复用。审批通过后仍要再次检查授权和资源状态。&lt;/p>
&lt;h3 id="26-coding-agent-示例工作流">2.6 Coding Agent 示例工作流
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">接收 Issue
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 校验来源、仓库权限、预算和任务类型
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 创建 Worktree / 沙箱并固定基线提交
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ Agent 分析并制定计划
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 按权限执行代码和工具操作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 运行单测、集成测试和静态检查
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 归纳差异、测试输出和风险
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 人工审查高风险变更
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 创建 PR 并等待 CI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 汇总结果、清理资源、记录指标
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>每个阶段都应有输入契约、输出契约和超时。Agent 未完成任务时，系统要能给出明确失败原因，而不是制造一个看似成功的空 PR。验证步骤失败时，工作流应保留工作区和证据，方便继续修复或人工接管。&lt;/p>
&lt;h3 id="31-软件测试与-agent-评测分别验证什么">3.1 软件测试与 Agent 评测分别验证什么
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>要回答的问题&lt;/th>
&lt;th>例子&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>单元测试&lt;/td>
&lt;td>单个函数和状态转换是否正确？&lt;/td>
&lt;td>重试计数、权限判定、序列化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>集成测试&lt;/td>
&lt;td>模型、工具、存储和队列接口能否协作？&lt;/td>
&lt;td>Tool Gateway 与沙箱联调&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>端到端测试&lt;/td>
&lt;td>从入口到交付的链路是否跑通？&lt;/td>
&lt;td>Issue 到 PR 的真实流程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>可靠性测试&lt;/td>
&lt;td>故障后是否可恢复、是否会重复副作用？&lt;/td>
&lt;td>杀进程、断网、超时、重复消息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Agent 评测&lt;/td>
&lt;td>Agent 对一组任务完成得如何？&lt;/td>
&lt;td>修改正确性、验收测试、成本和时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>安全测试&lt;/td>
&lt;td>是否越权、泄露、受注入影响或逃出隔离？&lt;/td>
&lt;td>提示注入、恶意仓库内容、网络探测&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>传统测试主要验证程序行为；Agent 评测验证一组可变模型行为。二者需要共同构成发布门禁，不能用单元测试覆盖率替代任务成功率。&lt;/p>
&lt;h3 id="32-测试金字塔和发布门禁">3.2 测试金字塔和发布门禁
&lt;/h3>&lt;p>越靠近底层的测试运行越快、越稳定，适合高频执行；端到端和真实模型测试更接近用户结果，但运行成本更高、方差也更大。推荐的门禁组合：&lt;/p>
&lt;ol>
&lt;li>每次提交：格式、静态检查、单元测试、权限和状态机测试。&lt;/li>
&lt;li>合并前：集成测试、工具契约测试、少量端到端 Smoke 用例。&lt;/li>
&lt;li>模型/提示词/工作流变更：固定 Agent 回归集和安全用例。&lt;/li>
&lt;li>发布候选：多次重复运行关键任务、容量检查、故障注入与人工抽查。&lt;/li>
&lt;li>上线后：小流量灰度、监控任务质量和成本，达到停止条件时回滚配置或关闭高风险工具。&lt;/li>
&lt;/ol>
&lt;p>端到端测试不要依赖无法控制的实时模型响应来断言每个 token。可以在不同层次使用固定响应模拟器验证协议，在隔离评测环境中调用真实模型验证整体表现，并为每层分别设定通过标准。&lt;/p>
&lt;h3 id="33-建立可信的任务-benchmark">3.3 建立可信的任务 Benchmark
&lt;/h3>&lt;p>每个评测用例至少固定：任务描述、仓库和提交版本、环境、工具权限、模型与提示词版本、预算、验收标准、参考结果或自动评分器。用例应覆盖正常路径、边界输入、工具失败、模糊需求和安全风险。数据集要记录来源、许可、隐私处理和修改历史。&lt;/p>
&lt;p>对于 Coding Agent，验收可组合使用：隐藏测试、公开测试、静态分析、差异范围检查、构建成功、人工代码审查和任务说明一致性。测试必须在干净环境中从固定基线执行，不能接受 Agent 修改测试来“证明”自己正确。对于开放式任务，先由领域专家写评分 rubric，再通过盲评样本校准自动评分器。&lt;/p>
&lt;p>指标至少分四组：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>结果质量&lt;/strong>：验收测试通过率、功能正确性、缺陷率、回归率。&lt;/li>
&lt;li>&lt;strong>交付效率&lt;/strong>：端到端时间、首次成功时间、人工接管率、返工次数。&lt;/li>
&lt;li>&lt;strong>资源消耗&lt;/strong>：模型调用次数、token、工具时间、单任务成本。&lt;/li>
&lt;li>&lt;strong>安全与可靠性&lt;/strong>：越权率、敏感信息泄露率、恢复成功率、重复副作用率。&lt;/li>
&lt;/ol>
&lt;p>成功定义必须先写清楚。例如，代码“有 diff”不等于任务成功；只有满足验收、未破坏现有功能、成本在预算内且没有越权才可计为成功。LLM-as-judge 可补充主观质量判断，但评分器也会偏差，应抽样人工校准，并保留可重复的客观检查。&lt;/p>
&lt;h3 id="34-方差回归与线上反馈">3.4 方差、回归与线上反馈
&lt;/h3>&lt;p>模型采样会导致同一用例多次运行结果不同。关键用例应重复运行，报告均值、区间和失败分布；样本较小时不要把细微涨幅包装成确定提升。修改模型、系统提示、工具 schema、检索、Runtime 或工作流后，运行同一套回归集，并按预先设定的质量、安全、延迟和成本门槛决定是否发布。&lt;/p>
&lt;p>线上评估需要关注分布漂移：真实用户任务可能超出离线集合。记录失败类型和人工接管原因，经隐私审查后抽样补充到评测集；防止只收录 Agent 容易完成的任务。可采用影子运行、有限灰度和 A/B，但要避免把高风险动作直接交给实验组。&lt;/p>
&lt;h3 id="35-失败分类与实验设计">3.5 失败分类与实验设计
&lt;/h3>&lt;p>失败应按可行动的原因分类，而不是只记“模型失败”：需求解析错误、上下文缺失、计划不完整、工具选择错误、参数无效、权限拒绝、环境不一致、测试没有发现缺陷、结果总结失真、预算耗尽、用户取消等。分类要能落到改进手段，并允许多个原因共同存在。&lt;/p>
&lt;p>比较两个 Agent 版本时，固定任务集和执行环境，记录模型、提示词、工具、Runtime、采样参数和数据版本。一次只改变少数变量；否则即使成绩变化，也无法知道原因。报告总成功率之外的分任务类型表现、失败类别、成本和方差。避免只挑容易提升的任务或在看过测试答案后反复调到过拟合。&lt;/p>
&lt;h3 id="36-故障注入和发布验证">3.6 故障注入和发布验证
&lt;/h3>&lt;p>主动模拟模型 429/5xx、工具超时、队列重复投递、数据库不可用、进程被杀、沙箱耗尽、用户取消和预算超限。验证的不只是“报警响了”，还包括：Run 状态是否正确、重试是否受控、用户能否理解当前状态、恢复后是否重复副作用、审计证据是否完整。&lt;/p>
&lt;p>发布后用 SLO 判断系统是否健康。可为入口可用性、任务排队时间、任务完成率、恢复时间和安全事件设置服务目标；当错误预算耗尽，应暂停高风险变更，优先修复可靠性问题。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-production-engineering-handbook/images/evaluation-loop.svg"
loading="lazy"
alt="从任务集、运行、评分到回归门禁的评测闭环"
>&lt;/p>
&lt;h2 id="4-可观测性审计与事故响应">4. 可观测性、审计与事故响应
&lt;/h2>&lt;h3 id="41-三种记录各自回答什么">4.1 三种记录各自回答什么
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>日志&lt;/strong>：某个组件发生了什么，适合诊断具体错误。&lt;/li>
&lt;li>&lt;strong>指标&lt;/strong>：整体趋势和服务健康度如何，适合告警与容量规划。&lt;/li>
&lt;li>&lt;strong>Trace&lt;/strong>：一次任务跨模型、工具、队列和服务的因果链是什么。&lt;/li>
&lt;li>&lt;strong>审计事件&lt;/strong>：谁或哪个 Agent 在什么授权下，对哪个资源做了什么动作，结果如何。&lt;/li>
&lt;/ul>
&lt;p>一次 Agent Run 的 Trace 应贯穿排队、Runtime、模型请求、工具调用、测试、审批和交付。每个 Span 记录耗时、状态、版本和必要的成本信息。提示词和完整输出可能含有敏感数据，不应无条件写入通用日志；采用访问控制、脱敏、保留期限和按需取样。&lt;/p>
&lt;p>可把每个 Run 的关联标识统一起来：&lt;code>task_id&lt;/code> 关联业务目标，&lt;code>run_id&lt;/code> 关联一次尝试，&lt;code>step_id&lt;/code> 关联工作流步骤，&lt;code>trace_id&lt;/code> 关联分布式调用链。不要把用户邮箱、原始 Issue 文本等个人或敏感信息直接放入指标标签，避免高基数和隐私泄漏。&lt;/p>
&lt;p>建议先建立以下运行指标：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>指标&lt;/th>
&lt;th>关注点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>入队数、排队时长、队列深度&lt;/td>
&lt;td>是否积压、调度是否公平&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>活跃 Run、完成/失败/取消数&lt;/td>
&lt;td>执行容量和终止原因&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>模型请求成功率、限流率、延迟&lt;/td>
&lt;td>供应商健康与路由表现&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具调用成功率、超时率&lt;/td>
&lt;td>工具可靠性和参数质量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>每 Run Token、成本、沙箱时长&lt;/td>
&lt;td>成本上限与优化空间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>人工审批等待、人工接管、返工&lt;/td>
&lt;td>产品可用性与人力负担&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>SLO 要对应用户能感知的结果。例如“99% 的任务在 30 秒内开始执行”比“Worker CPU 低于 80%”更接近服务承诺。Agent 的任务成功率受任务类型影响，需要分层统计；只报全局均值会掩盖某类任务严重退化。对于低频高风险安全事件，可采用绝对告警和人工升级，而不是等待统计显著性。&lt;/p>
&lt;h3 id="42-事故闭环">4.2 事故闭环
&lt;/h3>&lt;p>事故处理要能完成发现、止损、恢复、根因分析和预防。复盘聚焦系统条件，不止写“模型犯错”：是权限过宽、验收标准缺失、重试导致副作用、上下文截断、部署变更回归，还是用户操作不清楚？每个行动项都要有负责人和可验证的完成标准。&lt;/p>
&lt;p>事故响应手册至少说明：如何停止新 Run、如何取消正在运行的 Run、如何撤销/轮换凭证、如何封禁工具或模型路由、如何保留证据、如何恢复队列和数据、谁负责通知用户。定期演练这些操作；紧急按钮如果从未演练，不能视为可用控制。&lt;/p>
&lt;p>延伸阅读：&lt;a class="link" href="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/" >Agent Tracing 基础&lt;/a>、&lt;a class="link" href="https://www.zata.cc/p/agent-decision-audit-and-tracing/" >Agent 决策审计&lt;/a>、&lt;a class="link" href="https://www.zata.cc/p/agent-decision-audit-implementation/" >Agent 决策审计落地&lt;/a>。&lt;/p>
&lt;h2 id="5-安全与治理让-agent-的权限可控行为可追溯">5. 安全与治理：让 Agent 的权限可控、行为可追溯
&lt;/h2>&lt;h3 id="51-威胁边界">5.1 威胁边界
&lt;/h3>&lt;p>把用户、模型、工具、检索内容、代码仓库、浏览器页面、MCP 服务和外部 API 都纳入威胁模型。攻击者可能通过直接提示、文档/网页中的间接提示注入、恶意依赖、伪造工具输出或被盗凭证影响 Agent。模型输出本身也可能错误或越权。&lt;/p>
&lt;p>风险至少包括：提示注入、数据泄露、工具越权、命令执行、供应链攻击、跨租户访问、凭证暴露、拒绝服务和不可逆副作用。每种风险要写明资产、攻击路径、影响、预防、检测和应急处置。&lt;/p>
&lt;h3 id="52-控制原则">5.2 控制原则
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>最小权限&lt;/strong>：按用户、Run、仓库和操作发放短期、细粒度权限。&lt;/li>
&lt;li>&lt;strong>明确授权&lt;/strong>：模型不能通过自然语言自行扩大权限。&lt;/li>
&lt;li>&lt;strong>输入分区&lt;/strong>：系统指令与不可信内容分开，并把检索和工具结果标为数据。&lt;/li>
&lt;li>&lt;strong>外部副作用门禁&lt;/strong>：删除、发布、付款、权限修改等动作默认需审批或受严格策略控制。&lt;/li>
&lt;li>&lt;strong>网络与文件隔离&lt;/strong>：限制沙箱出站访问和挂载路径，防止读取宿主凭证。&lt;/li>
&lt;li>&lt;strong>审计和响应&lt;/strong>：保存必要的动作证据，支持撤销凭证、停用工具和终止 Run。&lt;/li>
&lt;li>&lt;strong>自动化红队回归&lt;/strong>：把已发现的攻击模式加入回归集，确保修复不会退化。&lt;/li>
&lt;/ul>
&lt;p>提示词防护只能作为一层，不能替代工具授权、沙箱、网络限制和人工控制。对高影响操作，应使用确定性策略在模型之外实施。&lt;/p>
&lt;h3 id="53-威胁建模工作表">5.3 威胁建模工作表
&lt;/h3>&lt;p>对每项重要能力，可以按“资产—主体—入口—动作—影响—控制—证据”建立威胁记录：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>可能路径&lt;/th>
&lt;th>必要控制&lt;/th>
&lt;th>验证证据&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>仓库中的恶意指令诱导 Agent 外传密钥&lt;/td>
&lt;td>Agent 读取 README 后调用网络工具&lt;/td>
&lt;td>密钥不暴露给模型；网络出口白名单；工具授权&lt;/td>
&lt;td>注入回归用例、网络拒绝日志&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>两租户共享缓存返回彼此结果&lt;/td>
&lt;td>缓存键没有租户/权限维度&lt;/td>
&lt;td>租户隔离键、授权后读取、敏感结果不共享&lt;/td>
&lt;td>跨租户负向测试&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>重试重复创建外部资源&lt;/td>
&lt;td>下游成功但响应丢失&lt;/td>
&lt;td>幂等键、状态查询和对账&lt;/td>
&lt;td>断网故障注入与副作用计数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>沙箱读取宿主凭证&lt;/td>
&lt;td>目录挂载或环境变量泄露&lt;/td>
&lt;td>最小挂载、短期凭证代理、无特权容器&lt;/td>
&lt;td>沙箱逃逸测试和配置审查&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>审批后内容被替换&lt;/td>
&lt;td>审批只绑定 Run 而不绑定参数&lt;/td>
&lt;td>对规范化动作摘要签名/绑定，执行前复核&lt;/td>
&lt;td>参数篡改测试与审计记录&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>不要把威胁模型当成一次性表格。新工具、新数据源、新租户共享能力和权限变更都可能引入新的攻击路径；安全测试应跟随架构变化持续更新。&lt;/p>
&lt;h2 id="6-模型rag-与上下文工程">6. 模型、RAG 与上下文工程
&lt;/h2>&lt;h3 id="61-工程岗位需要的模型深度">6.1 工程岗位需要的模型深度
&lt;/h3>&lt;p>Agent 平台工程师不必训练大参数基座模型，但应理解 Transformer 如何基于上下文计算下一个 Token、Tokenization 与上下文窗口对输入长度的影响、推理调用的延迟与成本、结构化输出和 Tool Use 的失败模式、采样参数、模型版本变化以及模型能力边界。无需成为训练算法专家，但应能和算法团队一起定义任务、数据、评价器和实验设计。&lt;/p>
&lt;p>工程上要把模型看成带有能力、价格、延迟、上下文长度和服务等级属性的依赖，而不是一个永远稳定的纯函数。模型升级、供应商切换、系统提示调整和推理参数修改都属于影响行为的变更，需要版本记录与回归验证。&lt;/p>
&lt;p>可逐步掌握：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>模型路由&lt;/strong>：根据任务难度、延迟、隐私和预算选择模型；失败时有受控降级策略。&lt;/li>
&lt;li>&lt;strong>RAG&lt;/strong>：文档解析、切分、索引、召回、重排、引用和检索质量评估。&lt;/li>
&lt;li>&lt;strong>上下文工程&lt;/strong>：按任务装配信息，控制噪声、冲突、长度和可信度。&lt;/li>
&lt;li>&lt;strong>推理部署&lt;/strong>：理解吞吐、并发、批处理、KV cache、量化和服务弹性等核心取舍。&lt;/li>
&lt;li>&lt;strong>微调实验&lt;/strong>：能完成一次有基线、有独立测试集、有版本记录的 LoRA/SFT 实验。&lt;/li>
&lt;/ol>
&lt;p>是否微调，应由失败分析决定。知识频繁变化通常应先更新检索数据；工具调用失败可能需要改 schema、示例或 Runtime；只有在行为模式稳定、数据合规且收益可测时，才考虑训练。训练集和测试集必须隔离，避免把测试答案泄漏进训练。&lt;/p>
&lt;h3 id="62-rag-的质量链">6.2 RAG 的质量链
&lt;/h3>&lt;p>RAG 不是“接向量数据库”。需要分别评估解析与切分质量、召回率、排序、答案忠实度、引用准确性、时效性和访问控制。权限过滤要在检索阶段生效，不能先取出不该访问的内容，再指望模型不复述。对每个答案保留来源片段，方便验证与审计。&lt;/p>
&lt;p>检索阶段可用 Recall@k 检查相关片段是否进入候选集，用 MRR 或 nDCG 检查相关结果是否排在前面；生成阶段则检查答案是否由证据支持、引用是否指向正确段落、证据不足时是否正确拒答。检索和生成要分开诊断：答案错了，不一定是模型不会总结，也可能是解析漏页、切分破坏语义、过滤器误删或重排器排序失误。&lt;/p>
&lt;h3 id="63-成本和延迟">6.3 成本和延迟
&lt;/h3>&lt;p>完整任务成本包括模型 Token、工具/沙箱计算、检索、存储、网络和人工处理。使用小模型处理分类、提取等简单步骤，大模型处理复杂推理；结合缓存、上下文裁剪、并行化和预算上限。缓存键要考虑租户、权限、模型与提示词版本、数据时效，避免跨用户泄漏或返回过期结果。&lt;/p>
&lt;p>跟踪端到端延迟及 p50/p95/p99，而不只看模型首 Token 时间。对于长任务，可将用户感知拆成排队时间、首次进展时间、人工等待时间和总完成时间。&lt;/p>
&lt;h3 id="64-什么时候微调什么时候不微调">6.4 什么时候微调，什么时候不微调
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>观察到的问题&lt;/th>
&lt;th>优先检查&lt;/th>
&lt;th>可能的处理方式&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>答案缺少最新事实&lt;/td>
&lt;td>数据是否进入检索、过滤是否正确&lt;/td>
&lt;td>更新知识源、重建索引、改进引用验证&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具参数经常不合法&lt;/td>
&lt;td>Schema、示例、解析与错误反馈&lt;/td>
&lt;td>收紧 schema、结构化输出、工具契约测试&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>任务经常漏步骤&lt;/td>
&lt;td>工作流是否显式、上下文是否完整&lt;/td>
&lt;td>拆分阶段、增加检查点、调整规划提示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>领域表达风格不稳定&lt;/td>
&lt;td>规范样例是否足够、评价目标是否清楚&lt;/td>
&lt;td>提示词和样例优化；必要时评估微调&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>高难推理不稳定&lt;/td>
&lt;td>任务是否超出模型能力、评测是否可诊断&lt;/td>
&lt;td>模型升级/路由、工具辅助、问题降级或人工复核&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>只有当目标行为稳定、样本质量和授权合格、训练/验证/测试分离、推理成本与收益可量化时，微调才是合理候选。一次实验应保留基座模型、数据版本、训练配置、随机种子、评估集、结果和回滚方式；否则无法判断提升来自哪里，也无法复现。&lt;/p>
&lt;h3 id="65-sft偏好优化与-agent-轨迹">6.5 SFT、偏好优化与 Agent 轨迹
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>SFT（监督微调）&lt;/strong>：用高质量输入—目标输出示例训练模型模仿期望行为。数据要定义清楚角色、工具调用格式、成功终止和拒绝边界。&lt;/li>
&lt;li>&lt;strong>偏好优化（如 DPO）&lt;/strong>：用偏好对表达“同一上下文下哪个结果更好”。偏好标注必须有一致标准；不能把安全拒绝错误地标成低质量答案。&lt;/li>
&lt;li>&lt;strong>Tool Use 训练&lt;/strong>：不只训练工具名，还需要覆盖何时调用、参数如何组织、工具失败后如何修正、何时停止和何时询问用户。&lt;/li>
&lt;li>&lt;strong>Agent 轨迹数据&lt;/strong>：包含任务、上下文、动作、工具结果、验证反馈和最终结果。轨迹必须检查权限、隐私、成功标签和失败原因，不能把“曾经执行过”当成“值得模仿”。&lt;/li>
&lt;/ul>
&lt;p>训练集和验证集按任务或来源隔离，防止同一个仓库、用户或模板的近重复任务跨集合泄漏。实验结果应与不微调的强基线比较，并同时评估任务质量、拒答、安全、工具调用、延迟和部署成本。&lt;/p>
&lt;h2 id="7-分布式执行与多租户平台">7. 分布式执行与多租户平台
&lt;/h2>&lt;p>从单机原型走向平台，主要增加的是故障域、隔离和控制面复杂度，而不是简单地把服务多开几份。&lt;/p>
&lt;h3 id="71-参考架构和数据边界">7.1 参考架构和数据边界
&lt;/h3>&lt;p>典型组件包括 API/控制面、任务队列、调度器、无状态 Worker、状态数据库、对象存储、模型网关、沙箱池和可观测性管道。控制面管理配置、身份、配额和策略；数据面执行具体 Run。运行状态应持久化，Worker 尽可能可替换。&lt;/p>
&lt;p>多租户需逐层隔离：身份与数据行、队列和配额、密钥、检索索引、文件/沙箱、Trace 查看权限及缓存。任何一个共享层都要验证租户边界。限流和公平调度可避免一个租户占满模型额度或 Worker。&lt;/p>
&lt;h3 id="72-可靠性和扩展">7.2 可靠性和扩展
&lt;/h3>&lt;ul>
&lt;li>队列削峰并提供背压；消费者处理重复投递。&lt;/li>
&lt;li>Worker 水平扩展，但受模型并发、沙箱容量和租户配额约束。&lt;/li>
&lt;li>数据库使用事务和版本化迁移；可再生成的索引与不可丢失的原始任务数据分开治理。&lt;/li>
&lt;li>对模型供应商、队列和存储故障定义降级、重试、隔离和恢复策略。&lt;/li>
&lt;li>设定容量指标和成本预算；压测排队、吞吐、尾延迟与恢复能力。&lt;/li>
&lt;li>通过备份恢复演练验证数据可恢复，而不是只确认备份任务成功。&lt;/li>
&lt;/ul>
&lt;p>先收集真实负载和瓶颈，再引入分布式组件。对个人原型，过早引入复杂微服务会增加运维面，却未必增加用户价值。&lt;/p>
&lt;h3 id="73-容量规划与压测">7.3 容量规划与压测
&lt;/h3>&lt;p>Agent 工作负载通常是长短任务混合、下游调用扇出明显、资源占用差异很大。容量规划不能只按 API 请求/秒估算，还要测每个 Run 的平均/高分位模型调用次数、工具并发、沙箱占用时长、人工等待和结果存储量。&lt;/p>
&lt;p>压测至少覆盖：稳定负载下的吞吐、突发负载下的排队与背压、慢模型造成的并发占用、某租户大量任务时的公平性、数据库/队列故障时的退化行为，以及恢复后队列是否能在目标时间内清空。高并发不一定等于高有效吞吐；如果模型供应商限流，继续增加 Worker 只会堆积更多等待任务。&lt;/p>
&lt;h3 id="74-数据治理与隐私生命周期">7.4 数据治理与隐私生命周期
&lt;/h3>&lt;p>为 Run 输入、模型请求/响应、工具输出、代码快照、Trace 和评测样本分别定义数据分类、访问角色、加密方式、保留期限和删除路径。默认只记录诊断所需的摘要和引用；确需存原文时应明确用途、访问审批和保留时间。用户删除任务时，要识别对象存储、缓存、搜索索引、分析副本和备份中的数据如何按策略删除或到期。&lt;/p>
&lt;p>评测数据尤其容易混入客户代码和个人信息。对外公开 Benchmark 前要审查许可、机密、凭证、版权和重识别风险，并提供数据来源与清理说明。&lt;/p>
&lt;h2 id="8-真实试点与业务结果">8. 真实试点与业务结果
&lt;/h2>&lt;h3 id="81-先定义任务边界">8.1 先定义任务边界
&lt;/h3>&lt;p>挑选频繁、可验收、风险可控、节省时间可测的任务类型，例如测试补全或低风险 Bug 修复。明确不支持的任务、用户需要提供的材料、所需权限、人工检查点和失败退路。不要一开始就承诺“Agent 自动完成所有研发工作”。&lt;/p>
&lt;h3 id="82-建立基线和试点协议">8.2 建立基线和试点协议
&lt;/h3>&lt;p>试点前记录当前人工流程的完成时间、返工率、缺陷率和直接成本；约定试点持续时间、参与团队、任务范围、数据权限、成功门槛和停止条件。记录所有任务，包括失败、放弃和人工接管，避免只展示成功样例。&lt;/p>
&lt;p>推荐对比：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>指标&lt;/th>
&lt;th>定义示例&lt;/th>
&lt;th>使用注意&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>任务成功率&lt;/td>
&lt;td>满足预设验收条件的任务数 / 纳入任务数&lt;/td>
&lt;td>固定任务范围，报告分母&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>人工接管率&lt;/td>
&lt;td>需要人继续实质工作或纠正的任务比例&lt;/td>
&lt;td>审批与接管分开统计&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>交付时间&lt;/td>
&lt;td>从接单到验收的端到端时长&lt;/td>
&lt;td>区分排队和人工等待&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>返工与回归&lt;/td>
&lt;td>失败重跑、后续修复和引入缺陷&lt;/td>
&lt;td>观察足够长时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>单任务成本&lt;/td>
&lt;td>模型、工具、基础设施及必要人工成本&lt;/td>
&lt;td>统一计算口径&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用户持续使用&lt;/td>
&lt;td>重复使用的团队/用户和任务量&lt;/td>
&lt;td>注册数不等于持续采用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>业务价值要和对照基线比较；“处理了 2000 个任务”本身不是收益证明。还需说明任务难度、人工参与、失败占比和结果质量。&lt;/p>
&lt;h3 id="83-反馈进入产品闭环">8.3 反馈进入产品闭环
&lt;/h3>&lt;p>每次人工接管都应分类：需求不清、上下文缺失、模型规划错、工具不可用、权限不够、测试不充分、工作流设计差或用户不信任。优先修复重复出现且影响大的类别，并把修复转换为测试、策略或文档。外部案例和评价须取得许可，区分已验证结果与目标值。&lt;/p>
&lt;h3 id="84-试点结果的计算口径">8.4 试点结果的计算口径
&lt;/h3>&lt;p>定义一个可复核的“净节省时间”估算：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">净节省时间 = 人工基线时间
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - Agent 运行期间仍需投入的人工时间
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - 返工时间
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - 审核与运维分摊时间
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>将任务成功率与净节省时间一起看。若成功率上升但人工审查时间翻倍，整体收益可能为负；若平均节省明显但高风险错误增加，也不能简单判定成功。报告至少分任务类型、难度、模型版本和人工参与程度，并展示失败案例。&lt;/p>
&lt;p>样本选择要避免偏差：纳入连续时间段内符合条件的任务，解释排除条件；记录用户拒绝交给 Agent 的任务，避免只测“最容易的那一类”。对照组尽量保持任务类型和人员经验相近，必要时采用分阶段上线，以区分季节、团队和学习效应。&lt;/p>
&lt;h2 id="9-从能力地图到实践计划">9. 从能力地图到实践计划
&lt;/h2>&lt;p>建议按依赖关系推进，而不是同时造所有平台功能：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>阶段&lt;/th>
&lt;th>重点&lt;/th>
&lt;th>可审查交付物&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1. 定位与基线&lt;/td>
&lt;td>选定窄任务，定义结果和成本指标&lt;/td>
&lt;td>任务范围、基线、最小演示和用例集&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2. Runtime 与评测&lt;/td>
&lt;td>固定运行状态、工具边界、验收与回归&lt;/td>
&lt;td>可恢复 Run、评测报告、失败分类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3. 真实试点&lt;/td>
&lt;td>让小范围用户持续执行真实任务&lt;/td>
&lt;td>试点协议、使用数据、反馈和事故记录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4. 平台化&lt;/td>
&lt;td>按实际瓶颈建设队列、多租户、弹性和 SLO&lt;/td>
&lt;td>压测、故障演练、容量和成本报告&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5. 治理与扩展&lt;/td>
&lt;td>威胁模型、权限策略、审计和变更管理&lt;/td>
&lt;td>红队回归、威胁模型、发布门禁&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6. 影响力与负责&lt;/td>
&lt;td>多团队采用、案例、协作和技术决策&lt;/td>
&lt;td>可复现案例、设计决策、复盘和分享&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一年内合理的成长目标通常是：Runtime、测试和工作流达到有真实生产经历；评测、安全和模型工程达到能独立设计、实现并解释取舍；分布式平台完成有证据的有限规模部署；团队领导力通过试点协作逐步积累。成熟的平台负责人能力需要更长的真实责任周期。&lt;/p>
&lt;h3 id="91-逐步推进的-12-个月路线">9.1 逐步推进的 12 个月路线
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>阶段&lt;/th>
&lt;th>工作重点&lt;/th>
&lt;th>阶段证据&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>第 1–2 个月：定位和基线&lt;/td>
&lt;td>选一个高频、可验收任务；砍掉分散功能；为现有 Runner 建任务记录和版本信息&lt;/td>
&lt;td>可安装演示、任务定义、基线数据、首批真实用户&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第 3–4 个月：评测和模型&lt;/td>
&lt;td>建立固定 Benchmark、失败分类、成本/成功率报表；完成路由或上下文实验&lt;/td>
&lt;td>可重复评测、模型对比报告、回归门禁&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第 5–7 个月：有限平台化&lt;/td>
&lt;td>根据真实瓶颈加入队列、可恢复状态、多租户原型、压测和故障注入&lt;/td>
&lt;td>架构决策记录、压测数据、恢复演练&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第 5–8 个月：安全并行&lt;/td>
&lt;td>做威胁模型、最小权限、沙箱边界、密钥治理和红队回归&lt;/td>
&lt;td>威胁模型、策略测试、审计证据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第 2–9 个月：真实试点&lt;/td>
&lt;td>持续收集用户任务、人工接管原因和业务结果&lt;/td>
&lt;td>试点报告、用户反馈、事故/复盘记录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第 8–12 个月：放大影响&lt;/td>
&lt;td>文档、公开案例、外部贡献、面试和技术方案表达&lt;/td>
&lt;td>可复现项目、技术文章、案例证明和系统设计材料&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>时间表只是安排先后关系，不是必须按月完成的承诺。若试点尚未证明任务价值，就应该继续缩小任务和修复可靠性，不应因为路线图写着“平台化”而提前堆分布式组件。&lt;/p>
&lt;h3 id="92-试点规模目标如何使用">9.2 试点规模目标如何使用
&lt;/h3>&lt;p>可以把“多个团队或持续用户、上千个真实任务、百例级 Benchmark、重复运行、故障演练、用户案例”等作为求职证据的参考目标，但这些数字不适用于所有产品。金融审批和代码自动修改的风险不同，任务难度、人工介入和质量标准也不同。更重要的是定义口径、报告分母和结果可复核。达到数量但没有质量、成本和用户价值证据，不等于生产成熟。&lt;/p>
&lt;h3 id="93-技术负责人能力如何形成">9.3 技术负责人能力如何形成
&lt;/h3>&lt;p>高级工程师和技术负责人不仅实现组件，还要为取舍负责：什么先做、什么明确不做；哪些任务允许自动执行；质量、成本、延迟和风险如何权衡；何时买服务、何时自建；出现事故时如何止损；多个团队的需求如何形成稳定接口。可以用轻量的架构决策记录（ADR）保存背景、备选方案、决定、代价和复核条件，让决策可讨论、可修订。&lt;/p>
&lt;p>带人能力可从小范围试点开始：写清任务和验收，拆分工作，帮助同伴定位问题，做代码与设计评审，复盘交付偏差。衡量重点是团队能否持续交付和改进，而不是自己完成了多少代码。&lt;/p>
&lt;h3 id="94-把生产经验转成可信的职业证据">9.4 把生产经验转成可信的职业证据
&lt;/h3>&lt;p>简历和面试中的生产案例应能回答：用户是谁、问题是什么、系统如何工作、你负责什么、规模和指标如何定义、遇到过什么故障、如何处理、哪些方案被放弃、结果有什么局限。每个数字都要能解释分母、时间范围和数据来源。&lt;/p>
&lt;p>一个可信的案例表述应采用结构：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">背景与任务范围
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 本人负责的架构/实现/推广
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 约束与关键取舍
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 验证方式和真实使用规模
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 质量、效率、成本、安全指标变化
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 失败、局限与下一步
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>避免声称“成功率提升 30%”却说不清基线、样本、版本和是否包含人工接管。工程能力的有力证据不仅是代码，也包括复现实验、事故复盘、设计决策、用户反馈和可公开的架构材料。&lt;/p>
&lt;h2 id="10-生产就绪检查表">10. 生产就绪检查表
&lt;/h2>&lt;h3 id="任务与用户">任务与用户
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> 任务范围、拒绝条件、验收标准和人工接管路径明确。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 有真实基线、固定成功定义和隐私处理规则。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 用户能看到进度、暂停原因、预算和最终证据。&lt;/li>
&lt;/ul>
&lt;h3 id="runtime-与工作流">Runtime 与工作流
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> Run 状态、步骤状态、版本和事件可持久化。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 超时、取消、重试、重复投递和进程崩溃经过验证。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 副作用有幂等保护；无法确认结果时能对账。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 资源、模型调用、工具调用和成本均有硬限制。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 人工审批绑定具体动作与参数，流程升级不会错用旧审批。&lt;/li>
&lt;/ul>
&lt;h3 id="测试与评测">测试与评测
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> 有单元、集成、端到端、安全和故障注入测试。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 固定评测集覆盖常见任务、边界、失败和攻击场景。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 关键任务重复运行，报告方差、成本和失败类别。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 模型、提示词、工具、数据和 Runtime 变更能触发回归门禁。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 评分器经过人工抽检，测试集没有训练或提示泄漏。&lt;/li>
&lt;/ul>
&lt;h3 id="安全与运维">安全与运维
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> 身份、租户、仓库、工具和网络权限符合最小权限。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 不可信代码在受限沙箱中运行，密钥不会进入模型上下文或日志。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 日志、指标、Trace 和审计事件可关联并有保留策略。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 有告警、停止运行、撤销凭证、恢复数据和事故复盘流程。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 有容量、成本、延迟和服务目标，并经过演练。&lt;/li>
&lt;/ul>
&lt;h3 id="业务证据">业务证据
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> 记录全部纳入任务、人工介入、失败、取消和成本。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 与相同口径的人工基线比较质量、时间和成本。&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 结果由试点用户确认；公开案例获得许可并注明范围。&lt;/li>
&lt;/ul>
&lt;h2 id="11-常见误区">11. 常见误区
&lt;/h2>&lt;ol>
&lt;li>**把能调用模型当成 Runtime。**缺少状态、预算、权限、取消和恢复，仍只是 Demo 循环。&lt;/li>
&lt;li>**把 Agent 评测等同于单元测试。**代码可以测对，任务仍可能做错；要分别验证软件和 Agent 行为。&lt;/li>
&lt;li>**把工作流做成全由模型决定。**权限、审批、测试和发布等控制步骤应由确定性策略执行。&lt;/li>
&lt;li>**把重试当可靠性。**没有幂等和对账的重试会放大副作用。&lt;/li>
&lt;li>**把 Trace 当审计。**Trace 解释技术调用链，审计还要说明主体、授权、对象和动作结果。&lt;/li>
&lt;li>**先做多租户和微服务再找用户。**先证明任务价值和负载，再按瓶颈扩平台。&lt;/li>
&lt;li>**只报成功任务和模型分数。**要报告分母、人工投入、方差、成本、安全和后续缺陷。&lt;/li>
&lt;li>**把微调当作所有失败的解法。**先归因问题来自数据、提示、工具、流程还是模型，再选手段。&lt;/li>
&lt;/ol>
&lt;h2 id="延伸阅读">延伸阅读
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/" >Agent 工程实战：从 Demo 到生产&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-runtime-explained/" >Agent Runtime 详解&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/" >AI Agent Loop 工程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/langgraph-%E5%AE%9E%E6%88%98stategraph%E6%89%8B%E5%86%99-react-%E5%BE%AA%E7%8E%AF%E4%B8%8E-map-reduce-%E6%91%98%E8%A6%81/" >LangGraph 实战教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-%E7%94%A8%E6%88%B7%E8%AE%B0%E5%BF%86%E4%B8%8E-skill-%E6%B2%89%E6%B7%80%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%8F%82%E8%80%83%E4%B8%8E%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/" >Agent 用户记忆与 Skill 沉淀&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>本文是领域知识入口，不替代针对特定技术栈的操作手册。后续可将 Runtime、评测、工作流、安全和平台运营分别扩展为专题，并在这里维护它们之间的关系、实践顺序与生产验收标准。&lt;/p></description></item><item><title>内置 Agent 放哪：一个 is_runnable 陷阱与三类事实源</title><link>https://www.zata.cc/p/builtin-agent-source-of-truth/</link><pubDate>Tue, 22 Sep 2026 18:39:16 +0800</pubDate><guid>https://www.zata.cc/p/builtin-agent-source-of-truth/</guid><description>&lt;img src="https://www.zata.cc/p/builtin-agent-source-of-truth/images/index/index.svg" alt="Featured image of post 内置 Agent 放哪：一个 is_runnable 陷阱与三类事实源" />&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">is_runnable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">supported_source_types&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">frozenset&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">enabled&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">organization_unit_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_connection_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_source_agent_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_descriptor_checksum&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_source_type&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">supported_source_types&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这是我们 Agent 平台领域模型里的一个函数。六项条件硬 AND，是用户目录 &lt;code>available&lt;/code> 字段的唯一来源——某个 Agent 在用户面前是「可用」还是灰掉的，就看这一行。&lt;/p>
&lt;p>原型评审通过那天（13 个页面状态全可达、console 零报错），我以为万事俱备。这个函数不这么认为，而我当时的第一反应居然是：是不是原型里哪个开关忘了画。打开领域模型逐个条件对了一遍，才发现不对的是更深一层——我们正准备上线的「内置 Agent」，第四、第五个条件&lt;strong>永远没有值可填&lt;/strong>。&lt;/p>
&lt;h2 id="先交代这是个什么平台">先交代这是个什么平台
&lt;/h2>&lt;p>一个企业内的 Agent 托管平台，两类角色两个面：管理员在 Admin 侧管 Runtime、管 Agent、做分配；普通用户在 public 目录里挑一个 Agent 对话，每次对话背后是一次可恢复、有事件流的 Run。&lt;/p>
&lt;p>「Agent 在这个平台上」其实是两半东西：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>业务定义行&lt;/strong>，存在数据库里：名字、系统提示词、业务目的、任务契约、负责人、可见范围、分配给了哪些部门/人；&lt;/li>
&lt;li>&lt;strong>Runtime 绑定&lt;/strong>，就是上面 &lt;code>runtime_&lt;/code> 开头的那几个字段：这个 Agent 实际「由谁执行」——绑到哪条连接、远端的哪个候选、候选定义的哪个版本、来源类型是什么。&lt;/li>
&lt;/ul>
&lt;p>平台自己的执行引擎叫 internal Runtime（另有一个隔离沙箱 sandbox），而所谓&lt;strong>外部 Runtime&lt;/strong>，是通过 endpoint 接进来的第三方 Agent 服务：管理员登记一条连接，向它「发现」远端暴露的候选 Agent，再挑一个绑到业务定义行上——平台只负责转发对话，真正的执行发生在远端。&lt;/p>
&lt;p>上线至今的 Agent 全是这种「外部绑定型」。这次要新增的是另一种：&lt;strong>内置 Agent&lt;/strong>——跟着平台发布、定义由平台方（ops）拥有、跑在平台自己的 internal Runtime 上，不再有任何远端。&lt;/p>
&lt;p>而 &lt;code>is_runnable&lt;/code> 是在只有外部绑定型的世界里写出来的。这就是冲突的全部来源。&lt;/p>
&lt;h2 id="一个硬-and-的陷阱">一个硬 AND 的陷阱
&lt;/h2>&lt;p>回到开头那六项条件。为什么外部 Agent 个个能过？因为三个 &lt;code>runtime_*&lt;/code> 字段是一套「绑定三件套」，从发现流程里带回来：绑的是哪条连接（&lt;code>runtime_connection_id&lt;/code>）、远端的哪个候选（&lt;code>runtime_source_agent_id&lt;/code>）、候选定义的哪个版本（descriptor 的 SHA-256）。三个字段的语义&lt;strong>全部指向远端有个东西&lt;/strong>。&lt;/p>
&lt;p>内置 Agent 没有任何远端，也没有「发现候选再绑定」这一步。第四、第五个字段没东西可填，只能空着。于是内置 Agent 一建出来：管理页面上一切正常，用户目录里永远「不可用」。原型画得再顺，接上实现就是坏的。&lt;/p>
&lt;p>这个坑怎么爬，其实只有三条路，走一遍就明白为什么最后只剩「按来源分叉」一条：&lt;/p>
&lt;p>&lt;strong>路一：全局放宽，把两个字段从校验里删掉。&lt;/strong> 不行。这两项对外部 Agent 是实打实的防线——远端候选被改名、被下线，descriptor 的 checksum 会变化，创建 Run 时能当场拦下；删掉之后，坏绑定要到运行时被远端拒绝才知道，而且「这条分配基于哪个版本的远端定义」这个审计问题永远失去答案。&lt;/p>
&lt;p>&lt;strong>路二：内置行也填上值。&lt;/strong> 给 &lt;code>runtime_source_agent_id&lt;/code> 造一个假想候选的常量 ID、checksum 填占位符。能跑，但等于伪造语义——内置 Agent 并不对应任何「被发现的候选」，审计里那句「基于远端哪个版本」在内置行上变成一个指向不存在事物的假答案。这种假答案能骗过所有人，直到有人认真看一眼。&lt;/p>
&lt;p>&lt;strong>路三：按来源分叉。&lt;/strong> 条件不再是「六个字段都有值」，而是「这个来源的 Agent，凭什么算完整」。内置行的完整性定义本来就不同：它需要的不是远端三件套，而是&lt;strong>定义内容的哈希&lt;/strong>——声明文件的 SHA-256（下一节讲定义放哪，那里会看到这个哈希从哪来）。外部那套一个字不改，内置按自己的完整性定义显式分叉：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">origin&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;builtin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">enabled&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">organization_unit_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_connection_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_descriptor_checksum&lt;/span> &lt;span class="c1"># 声明文件内容的 SHA-256&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="ow">and&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">runtime_source_type&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">supported_source_types&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>但这句话立刻引出了下一个问题：&lt;strong>内置 Agent 的定义，到底放在哪里？&lt;/strong> 不回答「定义放哪」，checksum 根本无从谈起。&lt;/p>
&lt;h2 id="定义放哪三个候选和仓库里的三个先例">定义放哪：三个候选，和仓库里的三个先例
&lt;/h2>&lt;p>摆出来的选项有三个。翻代码时发现，每个选项在仓库里都已有先例——这反而让选择变得清楚了。&lt;/p>
&lt;p>&lt;strong>选项一：Python 常量 + 启动 seed。&lt;/strong> 内置工具就是这么办的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">seed_tools&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;写入模板内置工具种子数据。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">database_session&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ToolModel&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="c1"># 表里有数据就跳过：seed 只插一次&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">seed_tool_models&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ToolModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;网页搜索&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">...&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ToolModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;code_runner&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;代码执行&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">...&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最省事，但改一个 prompt 要发版。更麻烦的是它让数据库处于一种「半拥有」状态：seed 只插一次不更新，管理员在 DB 里改了，DB 成了事实源；管理员删了，下次重启又复活。「内置项的删除与启停归属」必须额外写死规则，不然一定有人踩。&lt;/p>
&lt;p>&lt;strong>选项二：config.toml 声明段。&lt;/strong> 平台里非密钥配置的家：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">mcp_connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">types&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">standard-remote-mcp&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">label&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;标准远程 MCP&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">capability_id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;mcp_generic&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可评审、可 diff，但 TOML 是放标量和短表的地方。内置 Agent 的核心资产是&lt;strong>一篇长 prompt&lt;/strong>，塞进配置文件会把 config.toml 变成没人愿意 review 的怪物。&lt;/p>
&lt;p>&lt;strong>选项三：ops 拥有的声明目录。&lt;/strong> 仓库里刚沉淀出的新模式——每个条目一个声明文件，和它的实现代码放在一起：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 启动声明目录：每个 *.toml 声明一个本地 stdio server 的启动方式...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ⚠️ 本目录必须由 ops 拥有、运行时只读，且不能落在 workspace_root 内&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">mcp_connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">declarations&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">root&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/skill_mcp_react_agent/servers&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>改文件 = 提 PR + 评审 + 重新部署；装配期校验，越界直接启动失败；管理面只能选用、不能编辑内容。「ops」在这里是所有权：声明目录归运维/平台方，管理员只能启停和分配，运行时进程只读。&lt;/p>
&lt;p>最后拍板选了目录，起决定性作用的却是第四个观察——&lt;strong>内置 Skill 早就在过同一条路&lt;/strong>：每个 Skill 一个目录、&lt;code>SKILL.md&lt;/code> 带 frontmatter、加载器解析、&lt;code>content_checksum = SHA-256(SKILL.md)&lt;/code>。内置 Agent 和内置 Skill 在资产性质上完全同构：ops 拥有、内容是长文本、随代码发布。Skill 用目录用得很好，没有理由给 Agent 发明第二套。&lt;/p>
&lt;p>于是 checksum 的问题顺手解了：内置行的 &lt;code>runtime_descriptor_checksum&lt;/code> 填&lt;strong>声明文件的内容哈希&lt;/strong>。和 Skill 的 &lt;code>content_checksum&lt;/code> 完全同构，审计表零分叉——「这条分配基于哪个版本的定义」对外部行是远端 descriptor 的哈希，对内置行是声明文件的哈希，语义同一个：&lt;strong>定义内容的哈希&lt;/strong>。&lt;/p>
&lt;p>顺带两条派生结论：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>内置 Agent 禁止派生。&lt;/strong> Derive（把一个 Agent 复制成独立新行，留 &lt;code>derived_from_agent_id&lt;/code> 但后续不同步）隐含假设「定义在 DB 里、可以随意复制」。内置 Agent 的定义根本不在 DB，派生出来的行既不是内置（不在目录里）也不是外部（没有远端），目录会被迫长出第三类。想以内置 Agent 为底做变体，正确动作是去声明目录加一个文件。&lt;/li>
&lt;li>&lt;strong>内置 Runtime 反过来，不需要目录。&lt;/strong> 内建的 Runtime 就一两个，存在与否跟着代码装配走（adapter 类注册了才存在），为它们建声明目录等于给两个硬编码条目套登记簿。它唯一值得声明的是基础提示词——而这一项现状已经是文件式的：config.toml 只放指针，prompt 是代码旁边的 markdown：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">system_prompt_path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;src/skill_mcp_react_agent/SYSTEM_PROMPT.md&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>长文本进文件、ops 评审、config 只做指针——声明目录的精神已经在这了，只差给它一个名分。&lt;/p>
&lt;h2 id="权限把-agent-给了他skill-跟不跟过去">权限：把 Agent 给了他，Skill 跟不跟过去？
&lt;/h2>&lt;p>模型问题定了，下一个问题更贴近日常：管理员把一个 Agent 分配给某人，这个人就能直接用 Agent 需要的 Skill 吗？还是全部解耦——给了 Agent 还要再给 Skill / MCP 的权限？&lt;/p>
&lt;p>先盘现状，发现已经是混合模型：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>资源&lt;/th>
&lt;th>授权挂在哪&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MCP 连接&lt;/td>
&lt;td>Agent 上（一个 Agent 绑一条连接）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具&lt;/td>
&lt;td>Agent 上（descriptor 声明）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Skill&lt;/td>
&lt;td>&lt;strong>人身上&lt;/strong>（平台范围默认全员；restricted 需要按人或按部门显式授权）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>而且 Skill 的门禁是刻意双层：目录展示和 Run 创建共用同一个策略实例，装配处的注释写得很直白——否则「目录里看不到，但直接提交 skill_id 仍能运行」，两层门禁形同虚设。也就是说现状的回答是&lt;strong>解耦&lt;/strong>：分配 Agent 给的是「可以指挥这个 Agent」，不是「获得这个 Agent 用到的一切」。&lt;/p>
&lt;p>要不要改成随 Agent 传递授权？想明白的结论还是不要，三个理由：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>越权面&lt;/strong>。Skill 的运行资产是会真实进入沙箱执行环境的字节。随 Agent 传递，等于分配动作一次性授出该 Agent 引用的全部执行能力，做分配的人根本不知道授出去了什么。&lt;/li>
&lt;li>&lt;strong>审计粒度&lt;/strong>。授权挂人身上，「谁能用这个 Skill」是一次查询；挂 Agent 上，要从分配图反向遍历才能算出来。&lt;/li>
&lt;li>&lt;strong>哲学一致&lt;/strong>。「Agent 声明它用什么、人决定他能用什么」的分离，和前面「定义不进 DB、DB 只存启停与分配」是同一件事：&lt;strong>声明与授权分离&lt;/strong>。&lt;/li>
&lt;/ol>
&lt;p>代价当然有：管理员要多做一步 Skill 分配，用户会碰到「Agent 能打开、跑到某步被拒」。这个用 UX 补，不用权限模型补——分配页展示该 Agent 引用了哪些 Skill、标出当前用户缺哪些授权、给一个「补齐授权」的快捷入口。&lt;/p>
&lt;h2 id="一条没查过的链路执行根本不读-db-里的提示词">一条没查过的链路：执行根本不读 DB 里的提示词
&lt;/h2>&lt;p>到这里，「让内置 Agent 能用」的清单看起来就是：模型加 &lt;code>origin&lt;/code>、&lt;code>is_runnable&lt;/code> 分叉、装配期 seed、Run 创建门禁分叉——两天量级的活。但把「创建 → 快照 → 执行」整条链路走一遍之后，发现真正的大鱼藏在水里：&lt;/p>
&lt;p>&lt;code>RunStartRequest&lt;/code> 只有 &lt;code>run_id / question / session_id / skill_id / resources&lt;/code>——&lt;strong>没有提示词字段&lt;/strong>。内部 runner 执行时用的是自己初始化时加载的配置 prompt，DB Agent 行上的 &lt;code>system_prompt&lt;/code> 在 canonical Run 链路里根本没人读，只被一版旧编排器消费过。&lt;/p>
&lt;p>也就是说：光修 &lt;code>is_runnable&lt;/code>，内置 Agent 只是「建得出来、显示可用」；管理员精心填写的业务提示词是摆设，跑出来的行为和平台默认 Agent 一模一样。&lt;/p>
&lt;p>要让它「按配置真的跑出区别」，得是第二档工作：把业务提示词送进执行链路，按「Runtime 基础 → Agent 业务 → 本次 Skill」固定顺序拼接，并且&lt;strong>在 Run 创建时就拼好冻进执行快照&lt;/strong>——而不是执行时现查。否则声明文件后续一改，历史 Run 的含义跟着变，审计链就断了。快照冻结合并结果，「这条 Run 基于哪个版本的什么定义」才永远有据可查。&lt;/p>
&lt;p>内部 runner 恰好有一个顺手的性质：它自己配置的那份 prompt（&lt;code>SYSTEM_PROMPT.md&lt;/code>）天然就是拼接的第一层「Runtime 基础」。两层结构不用推翻任何现有设计，缺的只是把第二层递进去的那段管道。&lt;/p>
&lt;h2 id="沉淀三类事实源">沉淀：三类事实源
&lt;/h2>&lt;p>问题讨论完，发现贯穿始终的其实只有一个问题：&lt;strong>每类「内置事实」的事实源在哪&lt;/strong>。仓库里绕了一圈，答案收敛成三类，各归各位：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>事实长什么样&lt;/th>
&lt;th>放哪&lt;/th>
&lt;th>例子&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>少量标量、开关、阈值，环境相关&lt;/td>
&lt;td>config.toml 段&lt;/td>
&lt;td>风险门的 &lt;code>enabled=false&lt;/code>、超时、URL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>长文本、ops 拥有、要评审、条目会增多&lt;/td>
&lt;td>声明目录&lt;/td>
&lt;td>内置 Agent 定义、MCP server 启动声明、SKILL.md&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>行为实现、绑定关系、每行可变状态&lt;/td>
&lt;td>代码 + DB 行&lt;/td>
&lt;td>Runtime adapter、连接登记、启停 override&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>判断触发信号从来不是「它是内置的」，而是三个条件是否同时成立：&lt;strong>内容是文本、改它不该发版、条目会长多&lt;/strong>。风险门控全是数字和开关，一条都不中，所以它老老实实待在 config.toml；Runtime 实例的 endpoint 每个部署环境一个值，进 Git 只会造成配置漂移，所以它待在管理面维护的 DB 里；内置 Agent 的 prompt 三条全中，所以它进声明目录。&lt;/p>
&lt;p>还有两条纪律，防止这个模式长歪：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>第二个真实案例出现之前，不再开新目录。&lt;/strong> 边界靠需求撑开，不靠类比——否则三个月后会有七八个名义上「声明式」实际没人维护的目录。&lt;/li>
&lt;li>&lt;strong>目录加载器要收敛成一套约定。&lt;/strong> 现在 MCP declarations 和 Skill loader 已经是两套平行的「扫目录、解析、fail-fast、算内容哈希」实现，内置 Agent 是第三个。这次应该把共享部分抽出来——尤其是路径安全校验（目录必须 ops 拥有、运行时只读、不能落在 Agent 可写区），这种安全边界最容易在「照着上一个抄一份」时漏抄其中一行。&lt;/li>
&lt;/ol>
&lt;h2 id="几点收获">几点收获
&lt;/h2>&lt;ul>
&lt;li>「新增一类实体来源」的任务里，最值钱的检查不是画新页面，而是把现存的硬校验函数（如 &lt;code>is_runnable&lt;/code>）逐条过一遍「新来源下这一项怎么办」。原型验证的是界面，领域模型里的 AND 才是会坏的地方。&lt;/li>
&lt;li>承担审计语义的字段（checksum 类）永远给内容哈希，不留空、不造占位符。只要每类来源都能给出「定义内容的哈希」，表就不用分叉，审计问题就有统一句式的答案。&lt;/li>
&lt;li>权限要不要随组合传递，答案基本永远是不要。授权粒度应该对齐审计粒度——挂在人身上的权限，才回答得了「谁能用这个」。&lt;/li>
&lt;li>管理面填的字段，执行链路不读是最隐蔽的一类断裂。任何「配置了但没生效」的排查，都应该从配置一路走到 &lt;code>start_run&lt;/code> 那一刻，看参数到底有没有出现在请求里。&lt;/li>
&lt;li>「放哪」类决策（config / 目录 / DB）不是风格偏好，把「谁改它、内容是不是文本、有没有每行可变状态」三个问题一问，答案通常自己浮出来。问完记得再问一句：仓库里有没有人已经这么干过？先例比原则更有约束力。&lt;/li>
&lt;/ul>
&lt;p>相关阅读：这个链路里 Runtime、Adapter、Harness 的分工，之前整理过一篇&lt;a class="link" href="https://www.zata.cc/p/agent-runtime-explained/" target="_blank" rel="noopener"
>《Agent Runtime 详解》&lt;/a>，可以和本文对照着看。&lt;/p></description></item><item><title>Agent 决策审计：它与 Tracing 的关系</title><link>https://www.zata.cc/p/agent-decision-audit-and-tracing/</link><pubDate>Tue, 22 Sep 2026 11:30:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-decision-audit-and-tracing/</guid><description>&lt;img src="https://www.zata.cc/p/agent-decision-audit-and-tracing/images/index/index.svg" alt="Featured image of post Agent 决策审计：它与 Tracing 的关系" />&lt;p>假设一个代码 Agent 改了数据库连接配置。它跑过单元测试，然后决定跳过真实数据库验证，给出“可以合并”的结论。几天后线上出现连接池故障。此时仅看到一条 &lt;code>tool.pytest succeeded&lt;/code> 的 trace，并不能回答关键问题：&lt;strong>它当时看到了哪些改动，为什么认为真实数据库验证可以跳过？&lt;/strong>&lt;/p>
&lt;p>这正是决策审计（decision audit）要解决的问题。它与 Agent tracing 可以出现在同一条运行时间线上，但两者回答的问题不同：&lt;strong>tracing 还原执行过程；决策审计保存判断依据与责任边界。&lt;/strong> 当 Agent 的判断会影响放行、权限或资源使用时，后者就不能只是几行自由文本日志。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-and-tracing/images/decision-and-trace.svg"
loading="lazy"
alt="一次任务中的执行轨迹与决策记录"
>&lt;/p>
&lt;h2 id="一先把四种记录分清">一、先把四种记录分清
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>记录&lt;/th>
&lt;th>核心问题&lt;/th>
&lt;th>典型内容&lt;/th>
&lt;th>常见用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Trace&lt;/td>
&lt;td>经过了哪些步骤，耗时和错误在哪里？&lt;/td>
&lt;td>Agent、模型、工具、命令的 span 与父子关系&lt;/td>
&lt;td>排障、性能分析&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Log&lt;/td>
&lt;td>某一刻发生了什么？&lt;/td>
&lt;td>命令退出码、异常、状态变化的文本或结构化事件&lt;/td>
&lt;td>定位具体故障&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Decision record&lt;/td>
&lt;td>基于哪些输入作了什么判断？&lt;/td>
&lt;td>风险、候选项、选择/跳过及理由、决策版本&lt;/td>
&lt;td>复核判断&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Evidence&lt;/td>
&lt;td>判断是否得到事实支持？&lt;/td>
&lt;td>命令结果、测试报告、截图、审查报告及其摘要&lt;/td>
&lt;td>验收与复验&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一份 trace 完全可以包含 &lt;code>validation.plan&lt;/code>、&lt;code>validation.execute&lt;/code> 之类的 span，也可以链接决策记录。但&lt;strong>把 span 存下来，不等于完成审计&lt;/strong>：span 常为排障服务，可能被采样、截断或按较短期限清理；审计记录则要明确必填字段、留存策略、版本关联与完整性状态。反过来，只有决策表而没有 trace，也很难诊断工具超时、重试和上下文断裂。&lt;/p>
&lt;p>这里的“审计”不是声称记录模型的全部内心推理。我们能记录和复核的是&lt;strong>可观察的输入、显式给出的理由、执行器实际取得的结果，以及最终授权动作&lt;/strong>。自由文本解释可以帮助人理解，但不能替代结果和证据。&lt;/p>
&lt;h2 id="二决策审计是-tracing-的一部分吗">二、决策审计是 tracing 的一部分吗？
&lt;/h2>&lt;p>从&lt;strong>用户界面&lt;/strong>看，可以是一部分：点开一次 Agent run，沿着时间线看到“分析改动 → 制订验证计划 → 执行 → verifier 复核 → 放行”。从&lt;strong>数据责任&lt;/strong>看，最好是独立记录，再通过标识关联。它们可以共用一次任务的 &lt;code>run_id&lt;/code>，而 &lt;code>trace_id&lt;/code> 指向这一轮执行的诊断轨迹。&lt;/p>
&lt;p>需要特别区分 &lt;code>run_id&lt;/code> 与 &lt;code>trace_id&lt;/code>。一次长期任务可能暂停、恢复或重试，仍属于同一个业务 run，却产生多个 trace；一次 trace 也可能只覆盖其中一段执行。因此审计主键应围绕业务对象与代码版本设计，不能把“当前 trace 恰好存在”当作放行条件。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">PRD / Issue / change
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ run_id：业务任务身份
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ decision_id：一次验证计划或放行判断
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ attempt_id：一次执行尝试
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ trace_id：该次尝试的诊断轨迹
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一种实用做法是：&lt;strong>审计记录保留最小且稳定的事实，trace 保留丰富的执行细节&lt;/strong>。审计记录可以引用 trace；trace 丢失或过期后，仍能知道当时基于哪个代码版本、采用了哪些检查、结果如何。若审计存储本身也可能故障，必须显式标记 &lt;code>history_complete=false&lt;/code>，不能默默显示“通过”。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-and-tracing/images/identity-model.svg"
loading="lazy"
alt="四种标识的关联关系"
>&lt;/p>
&lt;h2 id="三一条可复核的决策记录长什么样">三、一条可复核的决策记录长什么样
&lt;/h2>&lt;p>以“Agent 自主选择验证项”为例，至少要分开记录&lt;strong>计划、执行与裁决&lt;/strong>。这是本文最重要的边界：Agent 写下“我将运行测试”，不代表测试真的运行过。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;schema_version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;decision_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;dec-42&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;run_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;run-17&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;attempt_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;attempt-2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_revision&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;git-tree-sha-or-worktree-fingerprint&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;policy_version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;validation-policy-v3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;actor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;validation-planner&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;risk_facts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;changed: database pool configuration&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;checks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;unit-tests&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;selected&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;reason&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;配置解析路径已变更&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;check_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;real-db-smoke&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;disposition&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;selected&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;reason&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;连接池行为依赖真实数据库&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;created_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2026-09-22T03:30:00Z&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这条记录只是&lt;strong>计划&lt;/strong>。执行器随后追加每项检查的 &lt;code>started&lt;/code>、&lt;code>passed&lt;/code>、&lt;code>failed&lt;/code>、&lt;code>timed_out&lt;/code> 或 &lt;code>unavailable&lt;/code> 结果，并附命令摘要、退出码、环境说明、产物引用和摘要值。verifier 再引用同一个 &lt;code>decision_id&lt;/code>，说明它是否认可计划覆盖面和实际证据。最终放行记录引用 verifier 结论及代码版本，避免“检查的是 A，合并的是 B”。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字段&lt;/th>
&lt;th>为什么需要&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>source_revision&lt;/code>&lt;/td>
&lt;td>防止旧证据给新代码放行；未提交改动也需要工作区指纹&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>policy_version&lt;/code>&lt;/td>
&lt;td>以后规则变化时，还能解释当时采用哪套标准&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>disposition&lt;/code> 与 &lt;code>reason&lt;/code>&lt;/td>
&lt;td>把跳过项显式化，便于统计误判与复核&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>result&lt;/code> 与 &lt;code>evidence_ref&lt;/code>&lt;/td>
&lt;td>区分 Agent 承诺和执行器观察到的事实&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>actor&lt;/code>&lt;/td>
&lt;td>区分 planner、执行器、verifier 和人工签核&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>history_complete&lt;/code>&lt;/td>
&lt;td>告诉读者记录缺失，避免把沉默误读为成功&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>原始 prompt、源码、密钥和完整终端输出不应默认写进长期审计表。可以将原始产物放入受控存储，审计表只保留路径、摘要、脱敏摘要和访问范围。**哈希能证明后来查看的是同一份产物，但不能证明产物当初真实或结论正确。**真实性仍需靠可信执行器、环境记录和必要时的复跑。&lt;/p>
&lt;h2 id="四它怎样帮助替代固定门禁">四、它怎样帮助替代固定门禁
&lt;/h2>&lt;p>固定门禁适合确定、便宜、误报低且失误代价高的规则，比如迁移链完整性或禁止提交密钥。Agent 适合根据改动上下文决定验证深度，例如是否需要浏览器真实入口、真实数据库或特定回归场景。决策审计让后者的自由度能够被复核。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-decision-audit-and-tracing/images/validation-loop.svg"
loading="lazy"
alt="验证计划、执行与复核的闭环"
>&lt;/p>
&lt;p>可落地的执行顺序是：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>识别风险&lt;/strong>：从 diff、任务要求、依赖边界和历史故障提取具体事实，而不是只给“高/中/低”标签。&lt;/li>
&lt;li>&lt;strong>列出候选检查&lt;/strong>：同时记录选择与跳过；跳过需说明前提，例如“仅改文案，没有可执行行为变化”。&lt;/li>
&lt;li>&lt;strong>执行并留证&lt;/strong>：由执行器记录实际命令、环境、退出状态和产物；失败、超时、无凭据分别处理。&lt;/li>
&lt;li>&lt;strong>独立复核&lt;/strong>：verifier 检查计划是否漏掉关键边界，以及证据是否对应当前版本。它的意见也应作为一条新记录，而不是覆盖原计划。&lt;/li>
&lt;li>&lt;strong>形成最终裁决&lt;/strong>：只有与当前代码版本匹配、记录完整且满足必要硬约束的结果，才能支持自动放行；其余情况升级给人。&lt;/li>
&lt;/ol>
&lt;p>这不是要求每次都运行最昂贵的验证。它要求&lt;strong>验证深度的取舍有可见依据&lt;/strong>。例如前端仅调整按钮文案，可以跳过端到端流程，但要说明未改交互或请求结果；修改 Dialog、Portal 或跨页流程时，单独的组件截图就不足以证明真实入口可用。&lt;/p>
&lt;h3 id="一个常见失败结果是绿的判断仍然错">一个常见失败：结果是绿的，判断仍然错
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>症状&lt;/th>
&lt;th>原因&lt;/th>
&lt;th>修复&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>单元测试通过，线上连接池仍故障&lt;/td>
&lt;td>计划漏了真实数据库边界&lt;/td>
&lt;td>verifier 审查“覆盖了哪些风险”，而不只看退出码&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>证据文件存在，实际测试没跑完&lt;/td>
&lt;td>Agent 把计划或旧文件当结果&lt;/td>
&lt;td>执行器写入退出码、时间与产物摘要；绑定代码版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>verifier 判绿，但新提交改变了代码&lt;/td>
&lt;td>裁决没有绑定修订版本&lt;/td>
&lt;td>代码版本变化时使旧裁决失效并重新评估&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="五从最小实现开始不必先建平台">五、从最小实现开始，不必先建平台
&lt;/h2>&lt;p>已有 runner、日志文件和生命周期账本的项目，可以先增加一张追加式 &lt;code>decision_events&lt;/code> 表或等价的 JSONL 存储：&lt;code>decision_id&lt;/code>、&lt;code>run_id&lt;/code>、&lt;code>attempt_id&lt;/code>、&lt;code>event_type&lt;/code>、&lt;code>actor&lt;/code>、&lt;code>source_revision&lt;/code>、&lt;code>occurred_at&lt;/code>、&lt;code>detail&lt;/code>。事件类型先控制在 &lt;code>plan_created&lt;/code>、&lt;code>check_started&lt;/code>、&lt;code>check_finished&lt;/code>、&lt;code>verdict_issued&lt;/code>、&lt;code>decision_invalidated&lt;/code>。原始输出继续落文件，通过 &lt;code>evidence_ref&lt;/code> 关联。&lt;/p>
&lt;p>第一阶段让 Agent 决策与现有门禁&lt;strong>并行运行&lt;/strong>，只记录分歧，不自动改变放行。挑选常误拦、执行成本高的门禁，统计它拦住过哪些真实问题、Agent 漏掉了哪些检查、verifier 能否识别。只有在这些反例上表现稳定，才逐项把固定门禁降为提示。否则只是把可见的脚本误报，换成不易发现的判断漏报。&lt;/p>
&lt;p>这个方案也有边界。独立 verifier 可能与 planner 共享同一种盲点；本机 SQLite 可能丢失；外部服务验证可能受凭据和环境限制。审计不能神奇地消除这些风险，它的价值是把&lt;strong>依据、缺口与责任&lt;/strong>留在可复核的位置，让后续改进有真实样本。&lt;/p>
&lt;h2 id="速查遇到一个问题该看哪里">速查：遇到一个问题该看哪里
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>你要问的问题&lt;/th>
&lt;th>先看&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>为什么这次 run 很慢？&lt;/td>
&lt;td>Trace 的 span 树与耗时&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>命令究竟报了什么错？&lt;/td>
&lt;td>关联 &lt;code>trace_id&lt;/code> 的日志及原始产物&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>为什么跳过真实入口验证？&lt;/td>
&lt;td>决策记录中的候选项、理由和政策版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>跳过是否合理？&lt;/td>
&lt;td>风险事实、任务要求、verifier 结论&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>测试通过是否能支持当前合并？&lt;/td>
&lt;td>执行结果、证据引用、代码版本与最终裁决&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>我的判断是：&lt;strong>决策审计可以嵌进 Agent tracing 的浏览体验，但应有独立的数据契约和留存责任。&lt;/strong> 当 Agent 只是辅助写代码，trace 通常足以排障；当 Agent 开始决定“哪些门禁可以不跑、这次是否放行”，决策记录就成为运行时的必要组成部分。&lt;/p>
&lt;p>继续阅读：&lt;a class="link" href="https://www.zata.cc/p/agent-decision-audit-implementation/" >Agent 决策审计落地：写入点、复核器与门禁降级判据&lt;/a>——本文的落地实现篇；&lt;a class="link" href="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/" >Agent Tracing 基础：Trace、Span 与 OpenTelemetry 埋点&lt;/a>；&lt;a class="link" href="https://www.zata.cc/p/agent-runtime-explained/" >Agent Runtime 详解&lt;/a>。&lt;/p></description></item><item><title>Agent Runtime 详解：从模型循环到可恢复的企业执行系统</title><link>https://www.zata.cc/p/agent-runtime-explained/</link><pubDate>Tue, 22 Sep 2026 10:17:24 +0800</pubDate><guid>https://www.zata.cc/p/agent-runtime-explained/</guid><description>&lt;img src="https://www.zata.cc/p/agent-runtime-explained/images/index/index.svg" alt="Featured image of post Agent Runtime 详解：从模型循环到可恢复的企业执行系统" />&lt;p>用户说：“查询这票货的费用，解释异常，并生成一份待确认的调整建议。”模型可以决定调用什么工具，但模型本身不会验证用户是否有权查看这票货，也不会替你处理工具超时、断线重连、重复提交或进程崩溃。负责把一次模型决策变成受控执行过程的部分，就是 &lt;strong>Agent Runtime&lt;/strong>。&lt;/p>
&lt;p>这篇文章把 Runtime 当成工程概念来解释。它没有全行业统一的产品边界：有的框架把模型循环、工具和会话都叫 Runtime；有的平台把队列、沙箱、恢复和审批也纳入 Runtime。讨论具体系统时，必须说清楚它实际承担哪些职责。&lt;/p>
&lt;h2 id="一先给出准确的定义">一、先给出准确的定义
&lt;/h2>&lt;p>&lt;strong>Agent Runtime 是执行一次 Agent 任务的控制循环及其运行环境。&lt;/strong> 它接收任务输入和被授权的能力，调用模型，解析模型输出，调度工具或其他 Agent，将执行结果送回模型，并在成功、失败、取消、暂停或预算耗尽时结束本次运行。&lt;/p>
&lt;p>最小循环如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">输入与授权上下文
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 调用模型
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 判断输出：最终回答 / 工具请求 / 委派 / 需要批准
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 执行动作并取得结果
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 更新本次运行状态
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 再次调用模型，直到满足终止条件
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenAI Agents SDK 的 &lt;code>Runner&lt;/code> 就实现了这类循环：模型返回最终输出时结束；返回工具调用时执行工具并继续；发生 handoff 时切换执行 Agent 并继续。实际产品通常还要在它外面加上自己的权限、任务持久化和业务审计。&lt;a class="link" href="https://openai.github.io/openai-agents-python/running_agents/" target="_blank" rel="noopener"
>官方运行机制&lt;/a>&lt;/p>
&lt;h3 id="与普通模型调用的差别">与普通模型调用的差别
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>能力&lt;/th>
&lt;th>一次模型 API 调用&lt;/th>
&lt;th>Agent Runtime&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>决定回复文字&lt;/td>
&lt;td>可以&lt;/td>
&lt;td>可以，通过模型完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>根据模型意图执行工具&lt;/td>
&lt;td>调用方自行编写&lt;/td>
&lt;td>Runtime 负责调度与回填&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>多轮工具循环&lt;/td>
&lt;td>调用方自行编写&lt;/td>
&lt;td>Runtime 管理循环与终止&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>取消、审批、恢复&lt;/td>
&lt;td>调用方自行设计&lt;/td>
&lt;td>视 Runtime 能力而定&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>权限、成本、审计&lt;/td>
&lt;td>模型不负责&lt;/td>
&lt;td>Runtime 或外层平台负责&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>所以“接上 LLM API”只是一个部件；Agent Runtime 的核心问题是&lt;strong>谁控制下一步执行，以及错误发生后系统处于什么状态&lt;/strong>。&lt;/p>
&lt;h2 id="二五个容易混淆的对象">二、五个容易混淆的对象
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>它回答的问题&lt;/th>
&lt;th>货运场景例子&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>模型（Model）&lt;/td>
&lt;td>如何生成判断或下一步意图？&lt;/td>
&lt;td>判断应查询费用明细&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Business Agent&lt;/td>
&lt;td>谁负责某类稳定业务任务？&lt;/td>
&lt;td>报价 Agent、单证审核 Agent&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Runtime&lt;/td>
&lt;td>一次任务怎样逐步执行？&lt;/td>
&lt;td>调模型、调工具、回填结果、结束 Run&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Adapter&lt;/td>
&lt;td>怎样接入某个具体 Runtime？&lt;/td>
&lt;td>将外部执行器事件转为平台统一事件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Harness / 平台&lt;/td>
&lt;td>多个 Agent 怎样被统一治理？&lt;/td>
&lt;td>身份、授权、路由、Run、审计、预算&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>在企业平台的一种常见设计中，Business Agent 是稳定身份；模型、Adapter、endpoint 和外部 Runtime 是可替换的绑定。两个 Business Agent 可以共用一个 Runtime，却保留不同任务契约、知识、Skill、负责人和历史。换 Runtime 也不应抹掉 Agent 的业务身份。&lt;/p>
&lt;p>这里的“Runtime”也不要与&lt;strong>模型推理服务&lt;/strong>混为一谈。推理服务负责运行模型并返回 token；Agent Runtime 负责决定何时请求推理、如何处理工具结果和何时停止。一个 Agent Runtime 可以调用远程模型推理服务，也可以调用本地模型。&lt;/p>
&lt;h2 id="三一次真实-run-怎样执行">三、一次真实 Run 怎样执行
&lt;/h2>&lt;p>仍以货运费用任务为例。下图描述的是一种合理的企业系统分工，不表示所有框架都内置了这些步骤。&lt;/p>
&lt;div class="mermaid">
sequenceDiagram
actor U as 用户
participant H as Harness / API
participant R as Agent Runtime
participant M as 模型
participant T as 费用查询工具
U-&amp;gt;&amp;gt;H: 请求解释费用异常
H-&amp;gt;&amp;gt;H: 鉴权、创建 Run、冻结权限和资源引用
H-&amp;gt;&amp;gt;R: start(run, input, event_sink, cancel_signal)
R-&amp;gt;&amp;gt;M: 发送允许的上下文和工具定义
M--&amp;gt;&amp;gt;R: 请求查询费用工具
R-&amp;gt;&amp;gt;H: 工具调用候选事件
R-&amp;gt;&amp;gt;T: 授权后执行查询
T--&amp;gt;&amp;gt;R: 费用明细或错误
R-&amp;gt;&amp;gt;H: 工具结果候选事件
R-&amp;gt;&amp;gt;M: 回填工具结果并继续
M--&amp;gt;&amp;gt;R: 异常解释与调整建议
R-&amp;gt;&amp;gt;H: 最终输出与终态事件
H--&amp;gt;&amp;gt;U: 流式结果与可回读记录
&lt;/div>
&lt;p>逐步看，有六个关键点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>创建 Run&lt;/strong>：为这一轮工作分配稳定身份。会话（Conversation/Thread）可包含多个 Run；一个 Run 也可能包含多次模型调用。不要把“一个聊天窗口”“一条消息”和“一次 Run”混为一谈。&lt;/li>
&lt;li>&lt;strong>冻结本轮可用资源&lt;/strong>：平台先解析用户身份、数据权限、Skill 版本和工具范围。模型只能在授权边界内建议动作；不能靠生成文本扩大权限。&lt;/li>
&lt;li>&lt;strong>执行模型循环&lt;/strong>：Runtime 调模型，识别最终回答、工具调用、委派或暂停信号。每一轮都应有轮数、时间或费用上限。&lt;/li>
&lt;li>&lt;strong>执行工具&lt;/strong>：工具参数需要校验，读写操作需要不同授权。查询失败可以按策略重试；“创建调整单”这类外部副作用不能盲目重放。&lt;/li>
&lt;li>&lt;strong>产出事件&lt;/strong>：文本增量、工具开始/完成、错误与终态由 Runtime 报告，再由平台排序、持久化并交付前端。&lt;/li>
&lt;li>&lt;strong>确定终态&lt;/strong>：成功、失败、取消和中断含义不同。进程停止不等于业务动作被撤销；取消信号发出也不等于外部工具已经停止。&lt;/li>
&lt;/ol>
&lt;h2 id="四runtime-内部最少有哪些模块">四、Runtime 内部最少有哪些模块
&lt;/h2>&lt;h3 id="1-执行循环与状态机">1. 执行循环与状态机
&lt;/h3>&lt;p>一个简单实现可以是 &lt;code>while&lt;/code> 循环，但状态仍要明确：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 教学伪代码：展示职责，不是可直接运行的 SDK 示例。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">policy&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">load_or_create_state&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">policy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">check_deadline_turns_and_budget&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_input&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">allowed_for&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;model.completed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_metadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_final&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;run.succeeded&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output_ref&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">save_output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">requests_tool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">call&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">policy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">validate_tool_call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tool_call&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">execute_with_timeout_and_approval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">state&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append_tool_result&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">call&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tool.completed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_metadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">requests_handoff&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="n">delegate_under_policy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="n">UnsupportedModelOutput&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>模型决定“建议做什么”，Runtime/平台决定“是否允许、怎样执行、何时结束”。这一分工是安全边界。&lt;/p>
&lt;h3 id="2-状态与上下文">2. 状态与上下文
&lt;/h3>&lt;p>Runtime 至少要知道当前输入、已发生的模型和工具结果、使用的 Agent 配置，以及终止条件。长期运行还要考虑哪些状态能持久化。&lt;strong>聊天历史、Run 状态、用户长期记忆是三种不同的数据&lt;/strong>：聊天历史帮助续谈；Run 状态帮助恢复这次执行；长期记忆跨任务存在并需要独立授权和纠错。&lt;/p>
&lt;p>把全部历史聊天直接塞回模型既昂贵，也可能带入过期权限或不相关信息。较稳妥的方式是按本次任务和当前授权组装 ContextPack，记录实际引用与版本。若暂停后恢复，应明确哪些输入沿用快照、哪些权限必须重新校验。&lt;/p>
&lt;h3 id="3-工具调度与副作用">3. 工具调度与副作用
&lt;/h3>&lt;p>工具执行不只是 &lt;code>functions[name](args)&lt;/code>。Runtime 或其外层平台需要回答：参数是否符合 schema？用户是否有权访问目标对象？是否允许联网？工具超时后能否重试？工具已提交业务数据但响应丢失怎么办？&lt;/p>
&lt;p>对有副作用的操作，应使用业务幂等键和执行前授权校验。&lt;strong>“队列至少一次投递”不能推导出“业务动作恰好执行一次”&lt;/strong>。恢复时要能区分“未开始”“已经提交但未收到结果”和“已确认完成”。敏感动作可暂停等待用户批准，批准应绑定具体动作和参数；参数改变后需重新判断批准是否有效。&lt;a class="link" href="https://openai.github.io/openai-agents-python/human_in_the_loop/" target="_blank" rel="noopener"
>官方 human-in-the-loop 示例&lt;/a>&lt;/p>
&lt;h3 id="4-调度隔离与资源限制">4. 调度、隔离与资源限制
&lt;/h3>&lt;p>Agent Runtime 不必天然等于沙箱。沙箱是它可能使用的一种执行环境。读取数据库的业务 Agent 可以运行在服务进程里；Coding Agent 执行 shell、安装依赖、修改文件时，通常需要更强的进程、文件系统、网络与密钥隔离。&lt;/p>
&lt;p>当任务变长或用户增加，还要有队列、Worker、并发上限、租约、心跳、取消传播和预算。此时 Runtime 可以拆成控制面与执行面：控制面保存 Run、调度和策略，执行面在 Worker/沙箱中做模型与工具循环。系统不应因某个 Worker 退出而丢失所有任务事实。&lt;/p>
&lt;h2 id="五取消重试和恢复最能检验-runtime-的地方">五、取消、重试和恢复：最能检验 Runtime 的地方
&lt;/h2>&lt;h3 id="取消不是把-http-连接关掉">取消不是“把 HTTP 连接关掉”
&lt;/h3>&lt;p>用户离开页面与取消任务是不同动作。取消应有明确命令和状态：平台接收请求、通知执行器、执行器停止可停止的工作、记录外部动作是否已真正停止，最后写入终态。如果外部系统不支持取消，就应如实报告，不要把“已发送取消请求”显示成“已取消成功”。&lt;/p>
&lt;h3 id="重试要区分层级">重试要区分层级
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>重试对象&lt;/th>
&lt;th>常见触发&lt;/th>
&lt;th>主要风险&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>模型调用&lt;/td>
&lt;td>限流、超时&lt;/td>
&lt;td>重复计费或不同输出&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>只读工具调用&lt;/td>
&lt;td>网络暂时故障&lt;/td>
&lt;td>结果可能已变化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入工具调用&lt;/td>
&lt;td>响应丢失&lt;/td>
&lt;td>重复创建业务记录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>整个 Run&lt;/td>
&lt;td>Worker 崩溃&lt;/td>
&lt;td>重放已完成步骤和副作用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>因此“失败后重试一次”不是完整策略。需要保留每次 attempt 的原因、输入版本、已完成动作和幂等身份。对于无法安全重放的动作，应进入待核对或人工处理状态。&lt;/p>
&lt;h3 id="持久化也不等于恢复">持久化也不等于恢复
&lt;/h3>&lt;p>保存聊天记录只能证明“知道过去说了什么”。真正的恢复还要知道执行到哪个步骤、哪些工具已成功、哪些回调可能重复、当时用的是哪个 Agent/Skill/权限快照。LangGraph 的持久化机制区分线程内的 checkpoint 与跨线程的 store，checkpoint 可用于中断后继续和故障恢复；但外部写入工具是否能安全重放，仍需由业务系统设计幂等与核对机制。&lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/persistence" target="_blank" rel="noopener"
>LangGraph 持久化文档&lt;/a>&lt;/p>
&lt;h2 id="六tracing-与事件日志在-runtime-中的位置">六、Tracing 与事件日志在 Runtime 中的位置
&lt;/h2>&lt;p>一次 Agent Run 需要两类容易混淆的记录：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>业务执行事件&lt;/strong>：Run 创建、工具调用、批准、终态等可审计事实，常需可靠持久化、顺序号和权限控制。&lt;/li>
&lt;li>&lt;strong>观测 Trace&lt;/strong>：模型/工具步骤的耗时、父子关系、token 和错误，帮助定位慢点、失败点与成本。&lt;/li>
&lt;/ul>
&lt;p>二者可以关联，也可以由同一组可靠事件投影出诊断视图，但不能默认认为一条采样 Trace 就能承担完整业务审计。Trace 可能被采样、丢弃或限期删除。敏感的 prompt、源码、邮件和工具结果也不应为了排障而默认全文写入观测系统。进一步阅读本站的 &lt;a class="link" href="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/" >Agent Tracing 基础&lt;/a>。&lt;/p>
&lt;h2 id="七一个平台设计示例runtimeadapterharness-如何分工">七、一个平台设计示例：Runtime、Adapter、Harness 如何分工
&lt;/h2>&lt;p>可以为不同执行器定义一个最小的执行适配接口，例如 &lt;code>start(run, input, event_sink, cancel_signal)&lt;/code> 与 &lt;code>cancel(run_id)&lt;/code>。Adapter 包装实际执行器，把来源事件映射为统一的 Run 事件；平台的 Run/Event 层负责持久化、序号、查询与 SSE 交付。这样业务 Agent 身份就不依赖某个执行器的事件格式。这里是说明边界的设计示例，并非通用标准接口。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Business Agent 目录：这是谁、负责什么、当前绑定哪个 Runtime
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Harness Core：谁能调用、可用哪些资产、创建哪个 Run、保存什么事实
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Runtime Adapter：怎样启动该来源、怎样翻译事件和取消信号
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">实际 Runtime：怎样调用模型、执行工具、维护循环
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Run/Event 投影：怎样回读、流式展示、追踪和审计
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一个具体的排障问题是：如果某 Runtime 只报告“工具 A 完成”，没有稳定调用 ID，就不能可靠地区分两次并行调用的同名工具。Adapter 应报告来源能力不足，而不是按工具名或完成顺序猜测父子关系。这是设计 Agent Run tracing 时必须处理的运行事实完整性问题。&lt;/p>
&lt;h3 id="最容易犯的边界错误">最容易犯的边界错误
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>把模型当 Runtime&lt;/strong>：以为换模型就能解决任务恢复、工具授权或重复写入。&lt;/li>
&lt;li>&lt;strong>把 Adapter 当 Runtime&lt;/strong>：接口能翻译事件，不代表来源真的支持取消、恢复或子 Agent。&lt;/li>
&lt;li>&lt;strong>把业务身份绑定到 Runtime&lt;/strong>：换底层框架后，历史 Agent 身份、评测基线和组织规则全部丢失。&lt;/li>
&lt;li>&lt;strong>把日志当状态&lt;/strong>：日志能辅助排障，不能可靠判定一次业务动作是否已经提交。&lt;/li>
&lt;li>&lt;strong>把多 Agent 当成多个 Prompt&lt;/strong>：真实委派需要独立的子任务身份、权限衰减、预算和结果归属。&lt;/li>
&lt;/ol>
&lt;h2 id="八怎样判断一个-runtime-是否能用于生产">八、怎样判断一个 Runtime 是否“能用于生产”
&lt;/h2>&lt;p>拿一项真实业务任务做验收，比看功能列表有效：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>问题&lt;/th>
&lt;th>需要看到的证据&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>执行过程能解释吗？&lt;/td>
&lt;td>模型轮次、工具调用、输入来源和终态可按 Run 还原&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>越权能被挡住吗？&lt;/td>
&lt;td>构造跨用户、禁用工具、过期资源访问，均在实际调用前失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>重复请求安全吗？&lt;/td>
&lt;td>同一幂等请求不产生两个 Run 或两次业务写入&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Worker 崩溃怎么办？&lt;/td>
&lt;td>重启后能判定已完成与待处理步骤，不盲目重放副作用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>取消是否真实？&lt;/td>
&lt;td>能区分取消已请求、执行器已停止和外部动作无法取消&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>成本可控吗？&lt;/td>
&lt;td>轮数、token、时间、工具费用有上限并可归属任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>能替换底层实现吗？&lt;/td>
&lt;td>换 Runtime 后业务 Agent 身份、权限与历史保持稳定&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>如果现在只能做“模型调用工具再返回答案”，那是一个可用的&lt;strong>执行循环&lt;/strong>。当上述证据逐步齐备，它才成为能承担企业任务的 Runtime 与运行平台。&lt;/p>
&lt;h2 id="九建议的学习和动手顺序">九、建议的学习和动手顺序
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>先写最小循环&lt;/strong>：一个模型、两个只读工具、最大轮数和最终输出。观察每轮输入与工具结果如何变化。&lt;/li>
&lt;li>&lt;strong>加入状态机&lt;/strong>：明确 queued、running、waiting_approval、succeeded、failed、cancelled 等状态及合法转换。&lt;/li>
&lt;li>&lt;strong>加入事件与回读&lt;/strong>：每个 Run 有稳定 ID、按序事件，刷新页面仍能看到已完成步骤。&lt;/li>
&lt;li>&lt;strong>注入故障&lt;/strong>：让模型超时、工具响应丢失、Worker 退出；验证重试和副作用边界。&lt;/li>
&lt;li>&lt;strong>接第二个 Runtime&lt;/strong>：通过 Adapter 转成同一套 Run/Event 契约，验证业务 Agent 身份不随底层变化。&lt;/li>
&lt;li>&lt;strong>再做委派和路由&lt;/strong>：让父子 Run 有独立身份与权限，证明子任务确实执行，而不是仅在 Prompt 中声称委派。&lt;/li>
&lt;/ol>
&lt;p>学完后应该能回答一个具体问题：&lt;strong>某次 Agent 失败时，是模型判断错、工具返回错、权限拒绝、Runtime 中断，还是平台恢复策略出了问题？&lt;/strong> 能沿着一次 Run 给出证据，就真正理解了 Agent Runtime。&lt;/p>
&lt;h2 id="参考资料">参考资料
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://openai.github.io/openai-agents-python/running_agents/" target="_blank" rel="noopener"
>OpenAI Agents SDK：Running agents&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://openai.github.io/openai-agents-python/human_in_the_loop/" target="_blank" rel="noopener"
>OpenAI Agents SDK：Human-in-the-loop&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://docs.langchain.com/oss/python/langgraph/persistence" target="_blank" rel="noopener"
>LangGraph：Persistence&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Agent Tracing 基础：Trace、Span 与 OpenTelemetry 埋点</title><link>https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/</link><pubDate>Tue, 22 Sep 2026 09:51:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/</guid><description>&lt;img src="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/images/index/index.svg" alt="Featured image of post Agent Tracing 基础：Trace、Span 与 OpenTelemetry 埋点" />&lt;p>用户说：这个 Agent 回答一个问题要 40 秒。&lt;/p>
&lt;p>我去 grep 日志，符合条件的只有一行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">2026-09-20 14:02:11 INFO run finished run_id=run-7f3a elapsed=41.3s
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>41.3 秒，和用户说的对得上。然后呢？没有然后了——&lt;strong>日志告诉你&amp;quot;慢&amp;quot;，但告诉不了&amp;quot;慢在哪一步&amp;quot;&lt;/strong>。&lt;/p>
&lt;p>于是我加 print：在每轮 LLM 调用前后打一行，在每次工具调用前后打一行。半小时后日志变成这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">14:01:30 LLM #1 start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">14:01:37 LLM #1 done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">14:01:37 tool web_search start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">14:02:01 tool web_search done
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">14:02:01 LLM #2 start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">14:02:16 LLM #2 done
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>能看了，但问题也很明显：这些行&lt;strong>彼此之间没有关系&lt;/strong>。换个用户并发进来，两组行就交织成一片；想知道&amp;quot;哪一步占了这 41 秒&amp;quot;，还得靠人肉减时间戳。更别说线上没有 print，只有一条 &lt;code>run finished&lt;/code>。&lt;/p>
&lt;p>这篇文章要解决的，就是把这个&amp;quot;人肉减时间戳&amp;quot;的过程，换成一套有结构、能自动关联的数据。这正是 &lt;strong>Tracing&lt;/strong> 干的事。&lt;/p>
&lt;h2 id="一trace-与-span一次-agent-run-就是一棵树">一、Trace 与 Span：一次 Agent Run 就是一棵树
&lt;/h2>&lt;p>先建立两个最基础的概念，用上面那个 run 当例子。&lt;/p>
&lt;p>&lt;strong>Trace（链路）&lt;/strong>：一次完整请求的全过程，有一个全局唯一的 &lt;code>trace_id&lt;/code>。上面这次调用对应一条 trace。&lt;/p>
&lt;p>&lt;strong>Span（跨度）&lt;/strong>：trace 里的一段工作，有名字、有开始时间、有结束时间，可以有属性和事件。上面每一行 print，本质上都是一个 span。&lt;/p>
&lt;p>Span 之间靠 &lt;strong>父子关系&lt;/strong> 组织成树。上面那次 run 画成 span 树是这样的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">agent.run run.id=run-7f3a session.id=s-12 [t=0.0 → 41.3s]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ gen_ai.chat 第 1 轮 gen_ai.request.model=某模型 [t=0.2 → 6.8s]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ tool.web_search tool.name=web_search [t=7.0 → 24.1s] ← 元凶
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ gen_ai.chat 第 2 轮 gen_ai.usage.input_tokens=4821 [t=24.3 → 39.6s]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ agent.finalize [t=39.7 → 41.2s]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>树的形状一出来，答案就自己浮上来了：&lt;strong>24.1 秒花在 &lt;code>web_search&lt;/code> 上，占掉一半以上&lt;/strong>。不需要减时间戳，不需要猜。&lt;/p>
&lt;p>这里有三点值得单独说清楚，因为后面所有的坑都出在这三件事上。&lt;/p>
&lt;p>&lt;strong>第一，父子关系是&amp;quot;包含&amp;quot;关系，不是&amp;quot;先后&amp;quot;关系。&lt;/strong> 子 span 的时间区间落在父 span 区间内。父 span 的耗时不是子 span 的加总——&lt;code>agent.run&lt;/code> 是 41.3 秒，四个子项加起来也差不多，但如果两个子 span 时间重叠，说明它们在并行，加总就会超过父 span。&lt;strong>时间重叠 = 并发&lt;/strong>，这是 span 树比日志强的最直观的一点。&lt;/p>
&lt;p>&lt;strong>第二，根 span 只有一个。&lt;/strong> 一次 run 对应一个 &lt;code>agent.run&lt;/code> span（root span），其余都是它的后代。&lt;code>trace_id&lt;/code> 相同、&lt;code>parent_span_id&lt;/code> 串起来，就构成了这次 run 的完整路径。排查时从 root 往下看，就是一条推理链。&lt;/p>
&lt;p>&lt;strong>第三，span 可以跨进程。&lt;/strong> 一次 Agent Run 里，&lt;code>tool.web_search&lt;/code> 很可能不是本地函数，而是打到一个远程服务甚至别人的 MCP Server 上。只要上下文传得过去（第五节讲），那边产生的 span 会挂到你这棵树上，变成你 trace 的一部分。&lt;/p>
&lt;h2 id="二属性与事件span-上该记什么">二、属性与事件：span 上该记什么
&lt;/h2>&lt;p>有了 tree 骨架，还得有内容。span 上能挂两类东西：&lt;strong>属性（Attributes）&lt;/strong> 和 &lt;strong>事件（Events）&lt;/strong>。这两个词经常被混用，但分工很清楚。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>属性 Attributes&lt;/th>
&lt;th>事件 Events&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>形态&lt;/td>
&lt;td>键值对，附着在 span 上&lt;/td>
&lt;td>带时间戳的独立记录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>回答的问题&lt;/td>
&lt;td>这个 span &lt;strong>是什么&lt;/strong>&lt;/td>
&lt;td>这个 span &lt;strong>过程中发生了什么&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>典型时机&lt;/td>
&lt;td>span 存续期间的任何时刻&lt;/td>
&lt;td>某个确定的时间点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>主要用途&lt;/td>
&lt;td>筛选、分组、聚合&lt;/td>
&lt;td>还原时间线、记录离散动作&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Agent 里的例子&lt;/td>
&lt;td>&lt;code>gen_ai.request.model&lt;/code>、&lt;code>gen_ai.usage.output_tokens&lt;/code>、&lt;code>tool.name&lt;/code>&lt;/td>
&lt;td>重试一次、首个 token 到达、工具返回、抛异常&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一个够用的判断口诀：&lt;strong>能被拿来当筛选条件的，放属性；只在某一刻发生、要看&amp;quot;什么时候&amp;quot;的，放事件。&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&amp;ldquo;所有用了 A 模型的 run 平均耗时多少&amp;rdquo; → 模型名必须是属性（要聚合)。&lt;/li>
&lt;li>&amp;ldquo;这次 run 重试了 3 次，每次间隔多久&amp;rdquo; → 重试是事件（要看时刻）。&lt;/li>
&lt;/ul>
&lt;p>在 Agent 语境里，属性这块已经有一份现成的答案：&lt;strong>OpenTelemetry 的 GenAI 语义约定&lt;/strong>（&lt;code>gen_ai.*&lt;/code>）。模型名、token 用量、工具名、会话 id 该叫什么，规范里都钉死了，照着填就能被各种观测后端正确解析。这份约定的字段清单和几个已知的坑，我在 &lt;a class="link" href="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/" >Agent 埋点接 ARMS&lt;/a> 里已经逐条整理过，这里只补一条最容易踩的边界：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>属性会被当成聚合维度，所以基数要有意识。&lt;/strong> &lt;code>run.id&lt;/code>、&lt;code>session.id&lt;/code> 这种每次都不一样的值，挂在 span 上当属性没问题（trace 是按 &lt;code>trace_id&lt;/code> 检索的，不存在&amp;quot;按属性建索引&amp;quot;的压力）；但如果这些属性被顺手套进了 Metrics 的维度里，一张指标卡片就会被炸成几万条时间线。分界线不是&amp;quot;能不能当属性&amp;quot;，而是&amp;quot;这个字段会不会流进指标聚合&amp;quot;。同样地，&lt;code>gen_ai.input.messages&lt;/code> 这类可能含用户隐私的字段，规范明确标成了 Opt-In，不该默认记录。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>事件这块有一件新变化，值得单独提醒。&lt;/strong> OTel 在 &lt;strong>2026 年 3 月&lt;/strong>宣布弃用 &lt;strong>Span Event API&lt;/strong>（也就是 &lt;code>Span.AddEvent&lt;/code> / &lt;code>Span.RecordException&lt;/code>），原因是&amp;quot;span 事件&amp;quot;和&amp;quot;日志事件&amp;quot;两套并行的机制造成了重复和困惑。新的事件应该走 &lt;strong>Logs API&lt;/strong>，通过上下文与当前 span 关联。需要注意边界：&lt;/p>
&lt;ul>
&lt;li>这是&lt;strong>弃用 API，不是删除能力&lt;/strong>。&lt;code>add_event()&lt;/code> 现在还能用，存量数据和在 trace 视图里看事件也照常工作。&lt;/li>
&lt;li>建议是：新写的埋点别再加对 &lt;code>add_event()&lt;/code> 的新依赖；自己封装的异常上报，优先走日志库 + OTel 日志桥接，它会自动带上 &lt;code>trace_id&lt;/code> / &lt;code>span_id&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>如果你现在就在写埋点，实操上可以这么记：&lt;strong>属性照旧填在 span 上；&amp;ldquo;某一刻发生了什么&amp;quot;优先记一条结构化日志&lt;/strong>——既符合新方向，也天然可搜索。&lt;/p>
&lt;h2 id="三tracesmetricslogs三根柱子各自回答一个问题">三、Traces、Metrics、Logs：三根柱子各自回答一个问题
&lt;/h2>&lt;p>到这可能会有一个误解：既然 tracing 这么好，是不是日志和指标就不需要了？&lt;/p>
&lt;p>不是。它们是三种不同粒度的问题，互相不能替代：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>信号&lt;/th>
&lt;th>回答&lt;/th>
&lt;th>Agent 里的典型用法&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Metrics&lt;/strong>&lt;/td>
&lt;td>&amp;ldquo;&lt;strong>多少 / 趋势&lt;/strong>&amp;rdquo;&lt;/td>
&lt;td>成功率、P95 延迟、token 消耗与成本、工具错误率&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Traces&lt;/strong>&lt;/td>
&lt;td>&amp;ldquo;&lt;strong>哪里 / 路径&lt;/strong>&amp;rdquo;&lt;/td>
&lt;td>单次 run 的完整执行路径、哪一步慢、哪一步失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Logs&lt;/strong>&lt;/td>
&lt;td>&amp;ldquo;&lt;strong>具体是什么&lt;/strong>&amp;rdquo;&lt;/td>
&lt;td>prompt/response 全文、工具入参出参、异常堆栈&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>换个说法：&lt;strong>指标告诉你&amp;quot;出问题了&amp;rdquo;，trace 告诉你&amp;quot;出在哪个环节&amp;quot;，日志告诉你&amp;quot;那个环节具体发生了什么&amp;quot;。&lt;/strong>&lt;/p>
&lt;p>三者真正的威力在&lt;strong>关联&lt;/strong>。Agent 场景里最常见的一条排查路径长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">① 指标：今天 token 消耗比昨天涨了 40% ← Metrics 发现异常
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">② 点开这张图的某个时间点 ← 靠 exemplar 带出 trace_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">③ 落到那一次 run 的 span 树 ← 看到工具被循环调用了 12 次
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">④ 跳到对应 span 的日志 ← 看到检索关键词空字符串，导致反复搜
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这条链要成立，靠的是一个共同的锚点：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&lt;code>trace_id&lt;/code> 是把三根柱子缝在一起的线。&lt;/strong> 指标里带 exemplar 引用 trace，日志记录里带 &lt;code>trace_id&lt;/code>/&lt;code>span_id&lt;/code>，trace 上挂结构化的 usage 属性——三条路径都指向同一根线，你才能从&amp;quot;指标异常&amp;quot;一路点到&amp;quot;具体那次请求的日志&amp;quot;。&lt;/p>
&lt;/blockquote>
&lt;p>反过来说，如果日志里没有 &lt;code>trace_id&lt;/code>，这套关联就断了，你又要回到&amp;quot;人肉 grep + 减时间戳&amp;quot;。&lt;/p>
&lt;h2 id="四opentelemetry-埋点从-api-到-span-树">四、OpenTelemetry 埋点：从 API 到 span 树
&lt;/h2>&lt;p>OTel 的埋点 API 其实只有三层，理解了这个层次就不会写乱：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">TracerProvider 一个进程一个，负责&amp;#34;把 span 送去哪里&amp;#34;（exporter / processor）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Tracer 按模块命名，比如 &amp;#34;agent.runtime&amp;#34;、&amp;#34;agent.tools&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Span 一次具体操作，就是树上的一个节点
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最小可运行的接线（&lt;code>opentelemetry-sdk 1.44.0&lt;/code>，2026-09 的版本）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">opentelemetry&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">trace&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">opentelemetry.sdk.trace&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TracerProvider&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">opentelemetry.sdk.trace.export&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BatchSpanProcessor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">opentelemetry.exporter.otlp.proto.http.trace_exporter&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OTLPSpanExporter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">provider&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TracerProvider&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">provider&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_span_processor&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BatchSpanProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OTLPSpanExporter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">endpoint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://localhost:4318/v1/traces&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trace&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_tracer_provider&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tracer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">trace&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_tracer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent.runtime&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>埋点本身，推荐用上下文管理器——它保证&lt;strong>异常也会结束 span&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent.run&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">span&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;run.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">run_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.conversation.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">session_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.chat&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">chat&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.request.model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.usage.input_tokens&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">usage&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">input_tokens&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意 &lt;code>start_as_current_span&lt;/code> 里的 &lt;strong>current&lt;/strong> 两个字——它做的事情是&amp;quot;把新 span 设为当前 span 并放进上下文&amp;quot;，下一个 span 创建时就会自动认它当爹。&lt;strong>父子关系不是手写的，是从上下文里自动读出来的&lt;/strong>。这也解释了为什么上下文一丢，span 树就会散成一片（下一节）。&lt;/p>
&lt;p>如果用现成的框架，通常不需要手写这些。LangChain / LangGraph 走 callback，OTel 有 instrumentation 包能自动建 span；纯手写的 Agent Runtime 才需要自己决定&amp;quot;哪些动作值得单独一个 span&amp;quot;。我的经验是：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>值得建 span&lt;/strong>：一次 LLM 调用、一次工具调用、一次检索、一次子 Agent 委派、一次 run 的起止。&lt;/li>
&lt;li>&lt;strong>不值得建 span&lt;/strong>：纯粹的参数拼装、字符串裁剪。这类动作建 span 只会让树变胖，真要记录就记事件/日志。&lt;/li>
&lt;/ul>
&lt;h2 id="五上下文传递让父子关系不断链">五、上下文传递：让父子关系不断链
&lt;/h2>&lt;p>这是入门到能用之间最容易被绊倒的一节。OTel 的上下文（Context）在 Python 里建立在 &lt;code>contextvars&lt;/code> 之上，含义是&amp;quot;当前执行流里，此刻的当前 span 是谁&amp;quot;。它有两个天然的断裂点。&lt;/p>
&lt;h3 id="51-进程内异步任务与线程池">5.1 进程内：异步任务与线程池
&lt;/h3>&lt;p>&lt;code>asyncio&lt;/code> 里 &lt;code>await&lt;/code> 前后的上下文是连续的，一般不会丢。但下面这些会丢：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 断链：新线程不会继承父线程的上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">submit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">do_work&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># do_work 里 get_current_span() 拿到的是 INVALID_SPAN（不记录的占位）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 补救：显式把上下文复制过去&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">contextvars&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">contextvars&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">copy_context&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">submit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">do_work&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同理，&lt;code>process&lt;/code> 池、Celery 之类的任务队列，都必须&lt;strong>显式把上下文当参数传过去&lt;/strong>，再在另一端 &lt;code>attach&lt;/code>。这类 bug 的表现很有辨识度：&lt;strong>span 数量是对的，但全是平铺的——每个都是 root，没有父子关系&lt;/strong>。&lt;/p>
&lt;h3 id="52-跨进程w3c-trace-context">5.2 跨进程：W3C Trace Context
&lt;/h3>&lt;p>跨服务传递靠的是 W3C 标准的 &lt;code>traceparent&lt;/code> 头：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ └── trace_id (32 hex) ─────────┘ └─ parent ──┘ └flags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └ version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注入和提取都是现成的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">opentelemetry&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">propagate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 发送方：把当前上下文写进 HTTP header&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">propagate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">inject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># -&amp;gt; {&amp;#39;traceparent&amp;#39;: &amp;#39;00-...&amp;#39;}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 接收方：从 header 还原上下文，再建 span 就会挂上去&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">propagate&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extract&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">received_headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;remote.tool&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>只要用了带自动埋点的 HTTP/gRPC 客户端，这一步通常是零成本的。&lt;strong>但 Agent 场景有几个地方容易漏&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>自己实现的流式接口&lt;/strong>（SSE 推 run 事件给前端）：手写的 &lt;code>fetch&lt;/code> 或自定义协议不会自动带头，得手动 &lt;code>inject&lt;/code>。&lt;/li>
&lt;li>&lt;strong>调用远程 MCP Server&lt;/strong>：这是最常漏的一处。MCP 的请求里如果没带 &lt;code>traceparent&lt;/code>，那边的 span 就是一棵独立的树，你的 trace 到工具调用这一步就断了。&lt;/li>
&lt;li>&lt;strong>后台任务&lt;/strong>：异步评估、异步摘要、定时任务，这些不在请求链路里，需要&lt;strong>显式&lt;/strong>把发起时的上下文带过去，否则它们永远是孤儿 span。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>一个自查方法：跑一次完整 run，数一下 trace 里 root span 有几个。&lt;strong>健康的 trace 只有一个 root&lt;/strong>；出现多个 root，说明某处上下文断了，去上面三个地方找。&lt;/p>
&lt;/blockquote>
&lt;h2 id="六三个最常见的坑">六、三个最常见的坑
&lt;/h2>&lt;p>按&amp;quot;症状 → 原因 → 修复&amp;quot;记，方便回头查。&lt;/p>
&lt;p>&lt;strong>症状一：树是平的，所有 span 都是 root。&lt;/strong>
原因是上下文在异步/线程边界断了。
修复：线程池用 &lt;code>contextvars.copy_context()&lt;/code> 包一层；跨进程检查 &lt;code>traceparent&lt;/code> 是否真的发出去了（抓一次包最快）。&lt;/p>
&lt;p>&lt;strong>症状二：某个 span 永远不结束，trace 一直在长。&lt;/strong>
原因是走了 &lt;code>start_span()&lt;/code> 手动模式却忘了 &lt;code>end()&lt;/code>，或者异常路径上没走到 &lt;code>end()&lt;/code>。
修复：默认用 &lt;code>start_as_current_span()&lt;/code> 上下文管理器；确需手动模式就套 &lt;code>try/finally&lt;/code>。&lt;/p>
&lt;p>&lt;strong>症状三：属性没上去，或者上报报类型错误。&lt;/strong>
原因是 OTel 的属性只接受&lt;strong>基本类型&lt;/strong>（字符串、数字、布尔和它们的数组），结构化对象塞不进去。
修复：结构化内容先 &lt;code>json.dumps()&lt;/code> 成字符串；同时想清楚这个字段是不是该进指标维度。&lt;/p>
&lt;h2 id="七速查表">七、速查表
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>概念&lt;/th>
&lt;th>一句话&lt;/th>
&lt;th>Agent 里对应什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Trace&lt;/td>
&lt;td>一次完整请求的全过程，有唯一 &lt;code>trace_id&lt;/code>&lt;/td>
&lt;td>一次 Agent Run&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Span&lt;/td>
&lt;td>一段有起止时间的工作，树上的一个节点&lt;/td>
&lt;td>一次 LLM 调用 / 工具调用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>父子关系&lt;/td>
&lt;td>子 span 时间落在父 span 内，靠上下文自动建立&lt;/td>
&lt;td>&lt;code>agent.run&lt;/code> → &lt;code>tool.web_search&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>属性&lt;/td>
&lt;td>键值对，描述 span&amp;quot;是什么&amp;quot;&lt;/td>
&lt;td>&lt;code>gen_ai.request.model&lt;/code>、&lt;code>tool.name&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>事件&lt;/td>
&lt;td>带时间戳，记录&amp;quot;某一刻发生了什么&amp;quot;&lt;/td>
&lt;td>重试、首个 token 到达、异常&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Metrics&lt;/td>
&lt;td>回答&amp;quot;多少 / 趋势&amp;quot;&lt;/td>
&lt;td>成本、成功率、P95 延迟&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Logs&lt;/td>
&lt;td>回答&amp;quot;具体是什么&amp;quot;&lt;/td>
&lt;td>prompt/response、工具入参出参&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>上下文传递&lt;/td>
&lt;td>&lt;code>contextvars&lt;/code>（进程内）+ &lt;code>traceparent&lt;/code>（跨进程）&lt;/td>
&lt;td>MCP 调用、SSE 流式、后台任务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="几点收获">几点收获
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>先问&amp;quot;我要回答什么问题&amp;quot;，再决定用什么信号。&lt;/strong> &amp;ldquo;慢在哪一步&amp;quot;是 trace 的问题，&amp;ldquo;涨了多少&amp;quot;是指标的问题，硬用日志去回答前两个，就会退化成打印追踪。&lt;/li>
&lt;li>&lt;strong>属性填得对不对，看它能不能被拿来筛。&lt;/strong> 能筛的放属性，只能看时刻的放事件，这个口诀能解掉八成的纠结。&lt;/li>
&lt;li>&lt;strong>上下文是隐式的全局状态，隐式的东西最容易断。&lt;/strong> 凡是跨了线程、跨了进程、跨了任务队列的边界，都要问一句&amp;quot;当前 span 传过去了吗&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>&lt;code>trace_id&lt;/code> 是整个可观测性的粘合剂。&lt;/strong> 日志里印上它、指标 exemplar 里带上它，三根柱子才真正连成一张网。&lt;/li>
&lt;li>&lt;strong>规范在变，别把 &lt;code>add_event()&lt;/code> 写进新代码。&lt;/strong> Span Event API 已经在 2026 年 3 月弃用，新的事件走 Logs API，能力不减，方向更统一。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;blockquote>
&lt;p>基础篇到此，概念就这些。下一步是把它接到具体的观测后端上——&lt;code>gen_ai.*&lt;/code> 字段怎么填、上报成功但看不到数据怎么办，这些实战里的坑见下一篇 &lt;a class="link" href="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/" >Agent 埋点接 ARMS&lt;/a>。&lt;/p>
&lt;/blockquote></description></item><item><title>E2B 迁到阿里云云沙箱：能跑通，但别急着上生产</title><link>https://www.zata.cc/p/e2b-%E8%BF%81%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E4%BA%91%E6%B2%99%E7%AE%B1%E8%83%BD%E8%B7%91%E9%80%9A%E4%BD%86%E5%88%AB%E6%80%A5%E7%9D%80%E4%B8%8A%E7%94%9F%E4%BA%A7/</link><pubDate>Mon, 21 Sep 2026 19:00:00 +0800</pubDate><guid>https://www.zata.cc/p/e2b-%E8%BF%81%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E4%BA%91%E6%B2%99%E7%AE%B1%E8%83%BD%E8%B7%91%E9%80%9A%E4%BD%86%E5%88%AB%E6%80%A5%E7%9D%80%E4%B8%8A%E7%94%9F%E4%BA%A7/</guid><description>&lt;img src="https://www.zata.cc/p/e2b-%E8%BF%81%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91%E4%BA%91%E6%B2%99%E7%AE%B1%E8%83%BD%E8%B7%91%E9%80%9A%E4%BD%86%E5%88%AB%E6%80%A5%E7%9D%80%E4%B8%8A%E7%94%9F%E4%BA%A7/images/index/index.svg" alt="Featured image of post E2B 迁到阿里云云沙箱：能跑通，但别急着上生产" />&lt;p>写《Agent 沙箱选型指南》那篇时，我把 E2B 放在「要做 Code Interpreter 就优先试」那一档。文章发出去以后有人在评论里追问：国内有没有对应的方案，网络和合规能不能绕过去。&lt;/p>
&lt;p>当时我的回答挺敷衍的，大意是自托管虽然开源，但底下压着 Firecracker、快照、调度、对象存储和一整套控制面，不是周五下午 &lt;code>docker compose up&lt;/code> 一下就能收工的。&lt;/p>
&lt;p>后来才发现，阿里云函数计算（FC）已经把这件事做完了，而且做法相当取巧。&lt;/p>
&lt;p>&lt;strong>它没有另起一套 SDK，而是直接兼容了 E2B 的数据面协议。&lt;/strong>&lt;/p>
&lt;p>于是问题就变成了一个特别诱人的形式：已有 E2B 应用，能不能只改几个环境变量就接上去？&lt;/p>
&lt;p>我把一个跑在 E2B 上的小 Runtime 搬了一遍。结论是：&lt;strong>能跑通，而且真的就三个环境变量。但「跑通」和「能上生产」之间，隔着一页清单。&lt;/strong>&lt;/p>
&lt;p>这一页清单才是这篇想讲的东西。&lt;/p>
&lt;blockquote>
&lt;p>本文对应的官方文档共 38 页，逐页的对照表放在最后一节「文档地图」，你可以按需跳转。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一先把它跑起来">一、先把它跑起来
&lt;/h2>&lt;p>对象是阿里云函数计算的&lt;strong>云沙箱（FC Agent Sandbox）&lt;/strong>——面向 AI Agent 和代码执行场景的云端隔离运行环境，按需创建，任务完成后释放。&lt;/p>
&lt;p>它的&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/product-overview-of-fc-agent-sandbox" target="_blank" rel="noopener"
>产品简介&lt;/a>写得挺克制：适合承载「不应该直接运行在业务服务进程内」的任务。举的例子是 AI 生成代码执行、数据分析、自动化脚本、依赖复杂的工具调用、临时 Web 服务。这句话其实已经划出了它的边界——&lt;strong>它不是一个通用计算平台，是一个给不可信代码用的执行槽。&lt;/strong>&lt;/p>
&lt;p>官方把可做的事分成五类：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>运行 Agent 工具&lt;/td>
&lt;td>给 Agent 一个独立执行环境，运行命令、处理文件、调用工具链&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>构建代码解释器&lt;/td>
&lt;td>执行 Python / Shell 等代码，返回 stdout、stderr、文本结果或文件产物&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>处理临时数据任务&lt;/td>
&lt;td>上传数据文件，在 Sandbox 内清洗、转换、分析、生成报告&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>启动临时服务&lt;/td>
&lt;td>在 Sandbox 内起 HTTP 服务或开发服务器，通过端口访问地址调用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>固化运行环境&lt;/td>
&lt;td>用模板预装依赖、运行时和工具链，减少每次任务的初始化成本&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="核心对象先认一遍">核心对象先认一遍
&lt;/h3>&lt;p>迁移之前值得先花十分钟把这几个对象对上号，因为后面所有的兼容性讨论都是围绕它们展开的：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>对象&lt;/th>
&lt;th>作用&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Sandbox&lt;/strong>&lt;/td>
&lt;td>一次远端隔离执行环境。创建、用、销毁&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Template&lt;/strong>&lt;/td>
&lt;td>定义 Sandbox 启动时的运行环境（基础镜像、语言运行时、依赖、工具链）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Commands&lt;/strong>&lt;/td>
&lt;td>在 Sandbox 中执行命令或进程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Filesystem&lt;/strong>&lt;/td>
&lt;td>管理 Sandbox 内的文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Code Interpreter&lt;/strong>&lt;/td>
&lt;td>执行代码片段并在多次执行间保持上下文，常用模板 &lt;code>code-interpreter-v1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Network&lt;/strong>&lt;/td>
&lt;td>访问 Sandbox 暴露的端口（&lt;code>getHost(port)&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Storage&lt;/strong>&lt;/td>
&lt;td>本地文件系统只服务当前任务；长期数据走 NAS / OSS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>FC Extensions&lt;/strong>&lt;/td>
&lt;td>云上扩展：VPC、OSS 挂载、自定义域名、日志监控、Team 配额&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="先分清teamapi-key-和-ram-权限是三件事">先分清：Team、API Key 和 RAM 权限是三件事
&lt;/h3>&lt;p>这张表建议在看任何配置步骤之前先过一遍，因为它能省掉一段弯路：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Team&lt;/th>
&lt;th>API Key&lt;/th>
&lt;th>RAM 权限策略&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>是什么&lt;/td>
&lt;td>资源隔离单元&lt;/td>
&lt;td>数据面凭据&lt;/td>
&lt;td>控制台 / OpenAPI 的操作授权&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>绑在谁身上&lt;/td>
&lt;td>账号下的资源组&lt;/td>
&lt;td>一个 Team&lt;/td>
&lt;td>RAM 用户 / 用户组 / Role&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用在哪&lt;/td>
&lt;td>划分项目和环境&lt;/td>
&lt;td>E2B SDK、CLI、兼容 HTTP API&lt;/td>
&lt;td>控制台、OpenAPI&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>在哪里建&lt;/td>
&lt;td>控制台&lt;/td>
&lt;td>控制台&lt;/td>
&lt;td>RAM 控制台&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Team 是「项目 × 环境」的边界&lt;/strong>，不是随便起的名字。Template、Sandbox、API Key、Volume 全都挂在 Team 下面，共用一个 Team 就等于这些资源互相全都可见，所以官方建议不同项目、测试和生产各用一个 Team，资源组则留给部门当边界。&lt;/p>
&lt;p>然后是这次真正卡住我的地方。&lt;/p>
&lt;p>&lt;strong>我一开始以为要跑通沙箱，得先把 RAM 权限配齐。&lt;/strong> 这个判断是错的——但我错得挺有迷惑性，因为它「看起来」更安全。官方在&lt;a class="link" href="https://help.aliyun.com/zh/agent-sandbox/getting-started/configure-ram-user-permissions" target="_blank" rel="noopener"
>配置 RAM 用户权限&lt;/a>里把两套鉴权分得很干脆：&lt;/p>
&lt;blockquote>
&lt;p>通过 E2B SDK、E2B CLI 或兼容 HTTP API 创建和访问 Sandbox 时，使用 API Key，不需要配置 RAM 权限。&lt;/p>
&lt;/blockquote>
&lt;p>再换个说法：&lt;strong>RAM 权限管控制台和 OpenAPI，API Key 管数据面。&lt;/strong> RAM 权限能让你在控制台管 Team、管 API Key、管 Volume；但你想创建一个 Sandbox 并往里跑代码，只认 API Key。文档的「常见误区」里专门点了这一条——&lt;strong>混淆 RAM 权限和 API Key&lt;/strong>。&lt;/p>
&lt;p>顺带一句，这份文档现在挂在 &lt;code>help.aliyun.com/zh/agent-sandbox/&lt;/code> 下（产品名从「云沙箱」往「智能体沙箱 Agent Sandbox」上靠了），旧链接仍然能打开，但搜的时候别只按老名字搜。&lt;/p>
&lt;p>&lt;strong>但如果你确实要用 OpenAPI 建模板，就会撞上我撞的那面墙。&lt;/strong>&lt;/p>
&lt;p>我在 RAM 控制台里翻权限策略，按 &lt;code>fcsandbox&lt;/code> 搜，什么都搜不到；换「服务」下拉列表一个个翻，也没有。第一反应是「是不是我的账号权限太小，看不见这一项」——不是。&lt;/p>
&lt;p>&lt;strong>原因很朴素：&lt;code>fcsandbox&lt;/code> 不在可视化编辑器的服务下拉列表里。&lt;/strong> 那个列表只收录注册过 RAM 元数据的服务，这个新产品没进去。而 RAM 创建策略时&lt;strong>并不校验 action 白名单&lt;/strong>——你写什么它就存什么。所以正路是根本不要走可视化编辑：&lt;/p>
&lt;ol>
&lt;li>RAM 控制台 → 权限策略 → 创建权限策略&lt;/li>
&lt;li>编辑方式选 &lt;strong>脚本编辑&lt;/strong>，不要选可视化编辑&lt;/li>
&lt;li>手写 JSON&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Statement&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Effect&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Allow&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Action&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;fcsandbox:*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Resource&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这一份是「管理账号下全部 Agent Sandbox 资源」，不含函数计算或其他云服务权限。想收紧到指定地域，&lt;code>Resource&lt;/code> 改成 &lt;code>acs:fcsandbox:&amp;lt;region&amp;gt;:&amp;lt;account-id&amp;gt;:*&lt;/code>。&lt;/p>
&lt;p>&lt;code>Action&lt;/code> 的命名规则是 &lt;code>fcsandbox:&amp;lt;接口名&amp;gt;&lt;/code>——你要找的「建模板、查模板、列模板、删模板」就是这四个字符串，&lt;strong>在控制台里是搜不到的，得直接写进策略&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Action&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fcsandbox:CreateTemplate&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fcsandbox:GetTemplate&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fcsandbox:ListTemplates&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fcsandbox:DeleteTemplate&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接口名去哪儿查？去 &lt;a class="link" href="https://api.aliyun.com/document/FCSandbox/2026-05-09/overview" target="_blank" rel="noopener"
>Agent Sandbox 的 OpenAPI 文档&lt;/a>找到业务要调的那个接口，页面上「授权信息」一节会直接告诉你对应的 action 名。&lt;strong>别猜名字&lt;/strong>——写错不会报错，只会在调用时静默变成 403。&lt;/p>
&lt;p>想收紧到单个 Team 的话：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Resource&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;acs:fcsandbox:cn-beijing:&amp;lt;account-id&amp;gt;:teams/&amp;lt;team-id&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;acs:fcsandbox:cn-beijing:&amp;lt;account-id&amp;gt;:teams/&amp;lt;team-id&amp;gt;/*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>两条 Resource 都要写，少一条会出那种「看得见但动不了」的怪状态&lt;/strong>（文档专门提醒了这一条）：&lt;code>teams/&amp;lt;team-id&amp;gt;&lt;/code> 是 Team 本身，&lt;code>teams/&amp;lt;team-id&amp;gt;/*&lt;/code> 才是它下面的 Template / Sandbox / API Key。只给前者，用户能看见 Team，但建模板、建 Key 全都失败。&lt;/p>
&lt;p>ARN 的完整形态是 &lt;code>acs:fcsandbox:&amp;lt;region&amp;gt;:&amp;lt;account-id&amp;gt;:&amp;lt;resource-path&amp;gt;&lt;/code>，层级比想象的深一层：&lt;strong>Sandbox 没有带自己 ID 的 ARN&lt;/strong>，它挂在 Template 下面，某个模板创建的沙箱是 &lt;code>teams/&amp;lt;team-id&amp;gt;/templates/&amp;lt;template-id&amp;gt;/*&lt;/code>。所以「只允许用某个模板」这种粒度是能做出来的。&lt;/p>
&lt;p>还有一条：&lt;strong>&lt;code>fcsandbox&lt;/code> 没有配套的系统策略，只能自定义。&lt;/strong> 如果你在「系统策略」里也搜不到，那是对的，不是漏配了什么。文档在「常见误区」里也点名了别图省事挂 &lt;code>AdministratorAccess&lt;/code>——那会把函数计算和其他云服务的权限一起给出去。&lt;/p>
&lt;p>所以回到开头：&lt;strong>先确认你走哪条路。&lt;/strong> 只是拿 SDK / CLI 跑沙箱，这一节可以整段跳过，去控制台建 Team 和 API Key 就行；要在 OpenAPI 侧管 Team、Template、API Key 或 Volume，才需要上面这段策略。两种都做的话，记住它俩是&lt;strong>平行的两套凭据&lt;/strong>，权限模型、轮换节奏和泄露影响面都不一样。&lt;/p>
&lt;h3 id="前置先把-api-key-建出来">前置：先把 API Key 建出来
&lt;/h3>&lt;p>这一步没有捷径，必须去控制台。按&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-api-key" target="_blank" rel="noopener"
>创建 API Key&lt;/a>的步骤，创建时有两个字段要填：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>描述&lt;/strong>：用来标识用途（开发环境 / 生产环境 / 某个应用 / 某个团队）。文档特别点了一句「不要只写 &lt;code>test&lt;/code> 或 &lt;code>default&lt;/code>」——因为后面要按描述筛 Key。&lt;/li>
&lt;li>&lt;strong>过期时间&lt;/strong>：可以选永不过期，也可以自定义。&lt;strong>生产环境建议设明确的过期时间并建立轮换机制。&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>创建完把完整 Key 复制出来存好。那句「不要写入代码仓库、镜像、模板、日志、截图、工单或前端页面」我不重复了，只说一个容易忽略的：&lt;strong>不要写进模板&lt;/strong>——模板是会被复用和分发的。&lt;/p>
&lt;p>Key 的管理操作有四个：编辑（改描述、改过期时间、启停）、&lt;strong>重置&lt;/strong>（生成新值，旧值立刻失效，用旧值的应用会认证失败）、删除（必须先禁用，建议禁用后观察一段时间再删）。&lt;/p>
&lt;h3 id="三个环境变量">三个环境变量
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;your-api-key&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_API_URL&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://api.&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_DOMAIN&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于这三个变量，有几件事值得单独说清楚（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-integration-parameter-description" target="_blank" rel="noopener"
>接入参数说明&lt;/a>里有完整对应关系）：&lt;/p>
&lt;p>&lt;strong>第一，SDK 会自动读它们。&lt;/strong> 所以你会看到官方示例里有的显式传参、有的什么都不传——两种都对。显式传参的好处是排查时一眼能看到连的是哪个 endpoint，我在迁移阶段是坚持显式传的。&lt;/p>
&lt;p>&lt;strong>第二，&lt;code>E2B_API_URL&lt;/code> 和 &lt;code>E2B_DOMAIN&lt;/code> 必须显式配置。&lt;/strong> 官方 E2B 的示例代码通常不写这两个，因为默认就走 E2B 自己的服务。云沙箱是 FC 侧提供的&lt;strong>兼容端点&lt;/strong>，不配就是连到 E2B 官方去了——你的阿里云 Key 在那边自然认不出来。&lt;/p>
&lt;p>&lt;code>E2B_API_URL&lt;/code> 是 SDK 访问云沙箱 API 的地址，&lt;code>E2B_DOMAIN&lt;/code> 是 SDK 拼接沙箱服务访问地址时用的基础域名。两个都要给，因为控制链路和数据链路是分开的。&lt;/p>
&lt;p>&lt;strong>第三，地域必须四处一致。&lt;/strong> &lt;code>E2B_API_URL&lt;/code>、&lt;code>E2B_DOMAIN&lt;/code>、模板、Sandbox，任意一个不在同一账号或同一地域，现象都是「认证失败 / 模板不可见 / 创建失败 / 连接失败」。按&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/usage-constraints-of-fc-agent-sandbox" target="_blank" rel="noopener"
>使用约束&lt;/a>，当前支持这八个：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>地域&lt;/th>
&lt;th>region 值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>华北 2（北京）&lt;/td>
&lt;td>&lt;code>cn-beijing&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>华东 2（上海）&lt;/td>
&lt;td>&lt;code>cn-shanghai&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>华东 1（杭州）&lt;/td>
&lt;td>&lt;code>cn-hangzhou&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>华南 1（深圳）&lt;/td>
&lt;td>&lt;code>cn-shenzhen&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>中国（香港）&lt;/td>
&lt;td>&lt;code>cn-hongkong&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>新加坡&lt;/td>
&lt;td>&lt;code>ap-southeast-1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>美国（弗吉尼亚）&lt;/td>
&lt;td>&lt;code>us-east-1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>美国（硅谷）&lt;/td>
&lt;td>&lt;code>us-west-1&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>第四，鉴权细节。&lt;/strong> 如果你不走 SDK、直接调数据面 HTTP 接口，API Key 是放在 &lt;code>X-API-KEY&lt;/code> 请求头里的。走 SDK 或 CLI 时，同一个 Key 传给 &lt;code>api_key&lt;/code> 参数或设成 &lt;code>E2B_API_KEY&lt;/code> 就行。&lt;/p>
&lt;h3 id="最小验证python">最小验证：Python
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">e2b_code_interpreter&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Sandbox&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">require_env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">RuntimeError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;缺少环境变量: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;code-interpreter-v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">require_env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;E2B_API_KEY&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">require_env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;E2B_API_URL&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">domain&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">require_env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;E2B_DOMAIN&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;python3 -c &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">print(&amp;#39;hello from sandbox&amp;#39;)&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">finally&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sandbox&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kill&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="最小验证typescript">最小验证：TypeScript
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Sandbox&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;@e2b/code-interpreter&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">main() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;code-interpreter-v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.E2B_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiUrl&lt;/span>: &lt;span class="kt">process.env.E2B_API_URL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">domain&lt;/span>: &lt;span class="kt">process.env.E2B_DOMAIN&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">timeoutMs&lt;/span>: &lt;span class="kt">300_000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commands&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;python3 -c \&amp;#34;print(&amp;#39;hello from sandbox&amp;#39;)\&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stdout&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">finally&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kill&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">main&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>看到 &lt;code>hello from sandbox&lt;/code>，说明 SDK、API Key、Endpoint、域名和内置模板这五样东西全都通了。&lt;/p>
&lt;h3 id="版本会被钉住">版本会被钉住
&lt;/h3>&lt;p>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-cloud-sandbox-via-the-sdk" target="_blank" rel="noopener"
>通过 SDK 使用云沙箱&lt;/a>里明说了：&lt;strong>方法名和参数形态以你正在使用的 E2B SDK 版本为准。&lt;/strong> Python 和 TypeScript 的命名风格还不一样。所以「兼容」这两个字是有版本前提的。&lt;/p>
&lt;p>快速入门验证过的固定组合是：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>语言&lt;/th>
&lt;th>安装命令&lt;/th>
&lt;th>运行时要求&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Python&lt;/td>
&lt;td>&lt;code>pip install e2b==2.31.0 e2b-code-interpreter==2.8.1&lt;/code>&lt;/td>
&lt;td>Python 3.10+&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TypeScript&lt;/td>
&lt;td>&lt;code>npm install e2b@^2.31.0 @e2b/code-interpreter@^2.6.1&lt;/code>&lt;/td>
&lt;td>Node.js 20.18.1+&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>别用 &lt;code>latest&lt;/code>。&lt;/strong> 生产项目应该把 lockfile 提交上去，升级依赖后重新跑一遍验证脚本。&lt;/p>
&lt;p>还有一个具体的差异必须记住：&lt;strong>Python 的参数是 snake_case，TypeScript 是 camelCase。&lt;/strong> Python 是 &lt;code>api_key&lt;/code> / &lt;code>api_url&lt;/code>，TypeScript 是 &lt;code>apiKey&lt;/code> / &lt;code>apiUrl&lt;/code>；Python 的 &lt;code>timeout&lt;/code> 单位通常是&lt;strong>秒&lt;/strong>，TypeScript 的 &lt;code>timeoutMs&lt;/code> 是&lt;strong>毫秒&lt;/strong>。排查时以当前语言 SDK 的类型定义为准，别把另一个语言的字段名复制过来——这种错不报错，只是悄悄用了默认值。&lt;/p>
&lt;p>顺带一句：&lt;code>E2B_ACCESS_TOKEN&lt;/code> 是 E2B 已经废弃的旧认证变量。新版本 CLI 和 SDK 统一用 &lt;code>E2B_API_KEY&lt;/code>。如果旧版 CLI 还在要求那个变量，先升级 CLI 再重试。&lt;/p>
&lt;h2 id="二这套兼容比我想的更深">二、这套兼容比我想的更深
&lt;/h2>&lt;p>一开始我以为所谓「兼容」就是照着 E2B 的接口抄了一遍，包一层自家 API。&lt;/p>
&lt;p>不是的。&lt;strong>它兼容的是 E2B 的数据面协议。&lt;/strong> 这句话的分量在于：只要协议对得上，第三方完全可以自己写 SDK。&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-integration-parameter-description" target="_blank" rel="noopener"
>接入参数说明&lt;/a>里写得很清楚：云沙箱数据面以兼容 E2B SDK / CLI 为主，&lt;strong>未提供独立的数据面 SDK&lt;/strong>；而 Team、API Key、Quota 这些&lt;strong>控制面资源&lt;/strong>可以走原生 OpenAPI、阿里云 SDK 或阿里云 CLI。&lt;/p>
&lt;p>阿里云函数计算团队就顺手把 Java 和 Go 的 SDK 写了——因为 E2B 官方只出 Python 和 TypeScript。见 &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-java-and-go" target="_blank" rel="noopener"
>E2B SDK（Java 与 Go）&lt;/a>：&lt;/p>
&lt;ul>
&lt;li>Java：&lt;a class="link" href="https://github.com/aliyun-fc/e2b-java-sdk" target="_blank" rel="noopener"
>aliyun-fc/e2b-java-sdk&lt;/a>&lt;/li>
&lt;li>Go：&lt;a class="link" href="https://github.com/aliyun-fc/e2b-go-sdk" target="_blank" rel="noopener"
>aliyun-fc/e2b-go-sdk&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>两个都还处于「以源码形式提供」的阶段，接入方式有点原生态：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Java：2.1.0 还没发到 Maven Central，得先克隆并 mvn clean install 到本地仓库 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.alibaba.serverless&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>e2b-java-sdk&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.1.0&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/aliyun-fc/e2b-java-sdk.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> e2b-java-sdk
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mvn clean install -DskipTests
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Go：go.mod 里声明的还是旧模块路径 github.com/e2b-dev/e2b-go-sdk，&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 该路径已经取不到了，得用 replace 指到本地目录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/aliyun-fc/e2b-go-sdk.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> myapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go mod edit -replace&lt;span class="o">=&lt;/span>github.com/e2b-dev/e2b-go-sdk&lt;span class="o">=&lt;/span>../e2b-go-sdk
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Go 这边有个细节挺反直觉：&lt;strong>虽然有本地替换，import 路径仍然要写旧模块路径&lt;/strong>，才能和 SDK 当前 &lt;code>go.mod&lt;/code> 的声明对齐。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nx">e2b&lt;/span> &lt;span class="s">&amp;#34;github.com/e2b-dev/e2b-go-sdk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Java 版有个挺讨喜的设计：&lt;code>Sandbox&lt;/code> 实现了 &lt;code>AutoCloseable&lt;/code>，退出 &lt;code>try&lt;/code> 块就自动 &lt;code>kill()&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ConnectionConfig&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ConnectionConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">builder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">apiKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;E2B_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">apiUrl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;E2B_API_URL&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">domain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;E2B_DOMAIN&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Sandbox&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sandbox&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;code-interpreter-v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">CommandResult&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getCommands&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;python3 -c \&amp;#34;print(&amp;#39;hello from sandbox&amp;#39;)\&amp;#34;&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">out&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getStdout&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">trim&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Go 版则是 &lt;code>defer sandbox.Kill(...)&lt;/code>。两边都在用语言本身的机制替你兜住「忘记释放」这件事——这其实是一个挺重要的信号：&lt;strong>写这两个 SDK 的人知道，真正的生产事故里，「忘了 kill」比「调不通接口」更常见。&lt;/strong>&lt;/p>
&lt;p>但官方也提示了一句：&lt;strong>两个 SDK 实现的是 E2B 数据面协议，接口和默认值与 Python / TypeScript 并不完全一致。&lt;/strong> 协议兼容不等于 API 表面对齐，接入前还是得看各自仓库的版本说明。&lt;/p>
&lt;h2 id="三一页清单什么能用什么别用">三、一页清单：什么能用，什么别用
&lt;/h2>&lt;p>这是我建议在动手之前先读完的东西——&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-compatibility-explanation" target="_blank" rel="noopener"
>E2B 兼容说明&lt;/a>。它把能力分成四档，其中最有价值的不是「兼容」，而是后面三档。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>能力模块&lt;/th>
&lt;th>状态&lt;/th>
&lt;th>关键说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Sandbox&lt;/td>
&lt;td>兼容&lt;/td>
&lt;td>创建、连接、查询、超时、终止、上传下载地址、端口访问；&lt;strong>暂停/恢复与 Snapshot 需白名单&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Commands&lt;/td>
&lt;td>兼容&lt;/td>
&lt;td>命令执行、进程管理、标准输入、PTY&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Filesystem&lt;/td>
&lt;td>部分兼容&lt;/td>
&lt;td>读写、目录管理、重命名、删除、存在性检查、目录监听；&lt;strong>不支持文件自定义元数据&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Code Interpreter&lt;/td>
&lt;td>兼容&lt;/td>
&lt;td>代码执行、上下文管理、流式输出、跨次执行状态保持；&lt;strong>不支持 Java 和 R&lt;/strong>；上下文管理&lt;strong>仅 Python SDK&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Template&lt;/td>
&lt;td>兼容&lt;/td>
&lt;td>模板 CRUD、构建、标签、别名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CLI&lt;/td>
&lt;td>部分兼容&lt;/td>
&lt;td>常用 Sandbox / Template 命令&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Metrics&lt;/td>
&lt;td>兼容&lt;/td>
&lt;td>CPU、内存可用；&lt;strong>磁盘/页缓存字段是占位值&lt;/strong>，按 1 分钟粒度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Logs / Network Config Update&lt;/td>
&lt;td>&lt;strong>受限&lt;/strong>&lt;/td>
&lt;td>接口可调用，但返回结果或实际效果存在限制&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Snapshots&lt;/td>
&lt;td>兼容（需白名单）&lt;/td>
&lt;td>仅第二代运行时可用，默认保留 7 天&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Volume / Access Token&lt;/td>
&lt;td>&lt;strong>暂不兼容&lt;/strong>&lt;/td>
&lt;td>不建议作为接入路径；Team 请在控制台管理&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>方法级的完整清单在 &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-compatible-api-list" target="_blank" rel="noopener"
>E2B SDK 兼容 API 清单&lt;/a>里。下面按模块展开，方便你对着自己的代码库勾一遍。&lt;/p>
&lt;h3 id="sandbox覆盖了完整生命周期">Sandbox：覆盖了完整生命周期
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Sandbox.create() 创建
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sandbox.connect(sandboxId) 连接已有 Sandbox（已暂停时自动恢复）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sandbox.list() 列出 Sandbox
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sandbox.getInfo(sandboxId) 查询指定 Sandbox 信息
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.getInfo() 查询当前 Sandbox 信息
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.isRunning() 判断是否运行中
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sandbox.setTimeout(sandboxId, ms) 调整指定 Sandbox 超时
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.setTimeout(ms) 调整当前 Sandbox 超时
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.pause() 暂停（需白名单）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.kill() / Sandbox.kill(id) 终止
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.uploadUrl(path) 获取上传地址
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.downloadUrl(path) 获取下载地址
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.getHost(port) 获取端口访问地址
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>Sandbox.create()&lt;/code> 支持的常用参数（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-a-sandbox" target="_blank" rel="noopener"
>创建沙箱&lt;/a>）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>template&lt;/code>&lt;/td>
&lt;td>模板名 / 模板 ID / Snapshot ID / 命名 Snapshot 全名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>timeout&lt;/code> / &lt;code>timeoutMs&lt;/code>&lt;/td>
&lt;td>沙箱超时，Python 秒 / TS 毫秒&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>envs&lt;/code>&lt;/td>
&lt;td>写入沙箱运行环境的环境变量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>metadata&lt;/code>&lt;/td>
&lt;td>写入沙箱&lt;strong>控制面&lt;/strong>的自定义元数据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>secure&lt;/code>&lt;/td>
&lt;td>控制端点访问保护强度，见第四节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>沙箱实例在生命周期内只有三个状态（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/code-interpreter-v1-template" target="_blank" rel="noopener"
>code-interpreter-v1 模板&lt;/a>）：&lt;code>running&lt;/code>（就绪）、&lt;code>paused&lt;/code>（已暂停，可恢复）、&lt;code>terminated&lt;/code>（已终止）。&lt;/p>
&lt;p>&lt;strong>关于超时&lt;/strong>（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/timeout" target="_blank" rel="noopener"
>超时&lt;/a>）：可以在创建时设，也可以创建后用 &lt;code>setTimeout()&lt;/code> 调。文档的提醒很短但很实在——设置过短任务会被提前回收，设置过长则增加资源占用和费用风险。以及老规矩：&lt;strong>超时不是释放。&lt;/strong> 任务做完就 &lt;code>kill()&lt;/code>，别指望超时回收，那个窗口期里资源一直在计费。&lt;/p>
&lt;h3 id="commands批处理走-run交互走-pty长任务走后台">Commands：批处理走 run，交互走 pty，长任务走后台
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sandbox.commands.run() 启动进程并等待结果
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.commands.list() 列出运行中的进程
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.commands.connect() 连接到已有进程
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.commands.sendStdin() 发送标准输入
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.commands.kill() 终止进程
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三类任务三种走法（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/run-the-command" target="_blank" rel="noopener"
>运行命令&lt;/a> / &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/backend-command" target="_blank" rel="noopener"
>后台命令&lt;/a> / &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/pty" target="_blank" rel="noopener"
>PTY&lt;/a>）：&lt;/p>
&lt;p>&lt;strong>批处理&lt;/strong>用 &lt;code>commands.run()&lt;/code>，同步拿结果。这是我见过的 95% 场景。&lt;/p>
&lt;p>&lt;strong>后台进程&lt;/strong>加 &lt;code>background=True&lt;/code>，SDK 立刻返回进程对象，之后可以继续访问端口、连接进程或终止进程：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">process&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;python3 -m http.server 8000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">background&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">host&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">8000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;https://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">running&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">list&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">running&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">process&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kill&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">finally&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kill&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里有个&lt;strong>必须知道的默认值&lt;/strong>：&lt;strong>命令执行超时默认通常只有 60 秒。&lt;/strong> 后台进程不会因为 SDK 调用返回就自动结束，但&lt;strong>仍受命令超时约束&lt;/strong>。所以起个长跑服务而不显式设 &lt;code>timeout&lt;/code> / &lt;code>timeoutMs&lt;/code>，它会在 60 秒后被掐。&lt;/p>
&lt;p>另外文档的建议也值得照做：&lt;strong>需要持续读取输出的任务，用后台进程 + 连接进程，别用长超时的同步命令阻塞主流程。&lt;/strong>&lt;/p>
&lt;p>&lt;strong>交互式终端&lt;/strong>用独立的 &lt;code>sandbox.pty&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">e2b&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PtySize&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Sandbox&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">terminal&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pty&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PtySize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rows&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">24&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cols&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">80&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pty&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">send_stdin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">terminal&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">b&lt;/span>&lt;span class="s2">&amp;#34;python3 - &amp;lt;&amp;lt;&amp;#39;PY&amp;#39;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">import sys&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">print(sys.stdout.isatty())&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">PY&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pty&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">send_stdin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">terminal&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">b&lt;/span>&lt;span class="s2">&amp;#34;exit&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">terminal&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">wait&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">on_pty&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">lambda&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">end&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exit_code&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>pty.create()&lt;/code> 开一个伪终端会话，TypeScript 用 &lt;code>sendInput()&lt;/code>、Python 用 &lt;code>send_stdin()&lt;/code>，&lt;code>terminal.wait()&lt;/code> 等退出。要断开后重连就保存 &lt;code>terminal.pid&lt;/code>，再用 &lt;code>sandbox.pty.connect(pid)&lt;/code>；窗口大小变了用 &lt;code>resize()&lt;/code>。&lt;/p>
&lt;p>PTY 的取舍文档列得很干脆：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>适合 PTY&lt;/th>
&lt;th>不适合 PTY&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>需要模拟真实终端行为的命令&lt;/td>
&lt;td>只需要稳定解析 stdout/stderr 的批处理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具在非 TTY 环境下会关掉颜色/进度/交互&lt;/td>
&lt;td>需要严格区分 stdout 和 stderr 的任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要给交互式进程发标准输入&lt;/td>
&lt;td>大量结构化日志输出（PTY 会改格式，解析成本变高）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>一句话：默认用 &lt;code>commands.run()&lt;/code>，只有命令明确依赖终端行为时才升到 PTY。&lt;/strong> 我见过有人为了「保险」全部走 PTY，结果输出里混进一堆 ANSI 控制字符，正则全废。&lt;/p>
&lt;h3 id="filesystem够用但不要当存储">Filesystem：够用，但不要当存储
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sandbox.files.list() 列出目录内容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.exists() 判断路径是否存在
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.getInfo() / get_info() 元信息
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.read() 读文件（默认文本，可读为 bytes / 流）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.write() 写文件（文本 / bytes / 流；TS 支持批量）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.makeDir() / make_dir() 创建目录
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.remove() 删除
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.rename() 移动或重命名
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.files.watchDir() / watch_dir() 目录监听
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>细节见&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/read-and-write-files" target="_blank" rel="noopener"
>读写文件&lt;/a>。几个实用行为值得记：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>write()&lt;/code> 会自动创建缺失的父目录&lt;/strong>，写入已存在文件时直接覆盖。所以 Python 那边写两个文件其实不用先 &lt;code>make_dir&lt;/code>——官方示例里先建目录只是习惯。&lt;/li>
&lt;li>&lt;strong>TypeScript 支持一次写入多个文件&lt;/strong>，适合把 Agent 生成的代码、测试文件、配置一起丢进去：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">await&lt;/span> &lt;span class="nx">sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">write&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;/tmp/project/main.py&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;print(&amp;#39;hello&amp;#39;)\n&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;/tmp/project/README.md&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;# Demo\n&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>二进制也支持&lt;/strong>：Python 写 &lt;code>bytes&lt;/code> 和文件对象，读的时候 &lt;code>format=&amp;quot;bytes&amp;quot;&lt;/code>；TypeScript 写 &lt;code>ArrayBuffer&lt;/code>、&lt;code>Blob&lt;/code>、&lt;code>ReadableStream&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>目录监听&lt;/strong>是个被低估的能力——等 Agent 输出文件、同步任务产物，比轮询 &lt;code>exists()&lt;/code> 优雅得多。只等单个文件时，可以退回 &lt;code>exists()&lt;/code> 做有限次数轮询。&lt;/p>
&lt;h3 id="code-interpreter核心能力齐全但减法不止一个">Code Interpreter：核心能力齐全，但减法不止一个
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sandbox.runCode() / run_code()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.createCodeContext() / create_code_context()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.listCodeContexts() / list_code_contexts()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.restartCodeContext() / restart_code_context()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sandbox.removeCodeContext() / remove_code_context()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出侧有 stdout、stderr、execution count、裸表达式结果，以及 &lt;code>execution.results&lt;/code> 富结果；流式场景下还有 stdout / stderr / 结果回调。&lt;strong>同一 Context 内变量和执行状态会保持&lt;/strong>——这也是 Code Interpreter 和「每次新起一个进程跑脚本」的本质区别。&lt;/p>
&lt;p>但&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/code-interpreter-v1-template" target="_blank" rel="noopener"
>code-interpreter-v1 模板&lt;/a>那页列出的减法比兼容说明里写的更多：&lt;/p>
&lt;p>&lt;strong>一是不支持 Java 和 R。&lt;/strong> &lt;code>language&lt;/code> 参数只认 Python、JavaScript、TypeScript、Bash；&lt;code>run_code&lt;/code> 的 &lt;code>language&lt;/code> 默认是 &lt;code>python&lt;/code>。&lt;/p>
&lt;p>&lt;strong>二是图表只有降级方案。&lt;/strong> 官方原话是「在 Sandbox 内生成图片文件后，通过 Filesystem 或下载 URL 取回」。没有「直接返回一个图片对象」那条路。如果你原来的实现依赖 SDK 直接吐出 png，这里要改。&lt;/p>
&lt;p>&lt;strong>三是上下文管理当前只有 Python SDK 能用。&lt;/strong> 这条我觉得是整页里最容易被忽略的：TypeScript 的 &lt;code>runCode&lt;/code> 能正常执行代码，但 &lt;code>createCodeContext&lt;/code> / &lt;code>listCodeContexts&lt;/code> / &lt;code>restartCodeContext&lt;/code> / &lt;code>removeCodeContext&lt;/code> &lt;strong>当前在本平台不可用&lt;/strong>，需要管理独立上下文只能换 Python SDK。做跨语言迁移的话，这一条会直接把方案卡住。&lt;/p>
&lt;p>&lt;strong>四是 &lt;code>logs.stdout&lt;/code> / &lt;code>logs.stderr&lt;/code> 是字符串列表&lt;/strong>，不是字符串。要 &lt;code>&amp;quot;&amp;quot;.join(...)&lt;/code>（Python）或 &lt;code>.join(&amp;quot;&amp;quot;)&lt;/code>（TypeScript）拼起来才是完整文本。&lt;/p>
&lt;p>&lt;code>run_code&lt;/code> 的主要参数和默认值：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>code&lt;/code>&lt;/td>
&lt;td>要执行的代码&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>language&lt;/code>&lt;/td>
&lt;td>&lt;code>python&lt;/code> / &lt;code>javascript&lt;/code>，未指定默认 &lt;code>python&lt;/code>；&lt;strong>与 &lt;code>context&lt;/code> 互斥&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>context&lt;/code>&lt;/td>
&lt;td>指定在哪个代码上下文执行；&lt;strong>与 &lt;code>language&lt;/code> 互斥&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>timeout&lt;/code> / &lt;code>timeoutMs&lt;/code>&lt;/td>
&lt;td>执行超时，&lt;strong>Python 默认 300 秒，TypeScript 默认 60000 毫秒&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>envs&lt;/code>&lt;/td>
&lt;td>自定义环境变量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>on_stdout&lt;/code> / &lt;code>onStdout&lt;/code> 等&lt;/td>
&lt;td>流式回调，逐行接收 stdout / stderr / 结果 / 错误&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>执行结果 &lt;code>Execution&lt;/code> 包含：&lt;code>logs&lt;/code>（stdout / stderr 列表）、&lt;code>results&lt;/code>（末表达式结果，含 &lt;code>text&lt;/code> 文本表示）、&lt;code>error&lt;/code>（执行异常）、执行计数（Python &lt;code>execution_count&lt;/code> / TS &lt;code>executionCount&lt;/code>）。&lt;/p>
&lt;p>上下文隔离的行为也很直观：默认上下文里 &lt;code>x = 42&lt;/code> 之后另一个 &lt;code>run_code(&amp;quot;print(x)&amp;quot;)&lt;/code> 能读到；但你 &lt;code>create_code_context()&lt;/code> 出来的独立上下文里定义的变量，默认上下文&lt;strong>读不到&lt;/strong>（会拿到 &lt;code>NameError&lt;/code>）；&lt;code>restart_code_context()&lt;/code> 之后变量被清空。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create_code_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">language&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cwd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/home/user&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_code&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;y = 100&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_code&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;print(y)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 100&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_code&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;print(y)&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># NameError：默认上下文看不到 ctx 的变量&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>create_code_context&lt;/code> 需要指定 &lt;code>language&lt;/code>，&lt;code>cwd&lt;/code> 默认 &lt;code>/home/user&lt;/code>。&lt;code>run_code&lt;/code> 的 &lt;code>context&lt;/code> 参数要传 &lt;code>Context&lt;/code> 对象；&lt;code>restart_code_context&lt;/code> / &lt;code>remove_code_context&lt;/code> 传对象或 ID 字符串都行。&lt;/p>
&lt;p>另外从构建模板的官方脚本里还能看到更细的返回值形态，注意这里&lt;strong>有两个超时参数&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">execution&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_code&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;print(&amp;#39;hello&amp;#39;)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">60&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request_timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stdout&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">execution&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">logs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="p">[])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stderr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">execution&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">logs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stderr&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="p">[])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">execution&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>timeout&lt;/code> 管代码执行，&lt;code>request_timeout&lt;/code> 管这次请求本身。&lt;strong>这两个别混。&lt;/strong> 后面讲 Snapshot 的时候会看到，混了会很贵。&lt;/p>
&lt;h3 id="template-与-cli">Template 与 CLI
&lt;/h3>&lt;p>模板侧支持 CRUD、构建、标签、别名。CLI 侧的兼容表（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-cloud-sandbox-via-the-cli" target="_blank" rel="noopener"
>通过 CLI 使用云沙箱&lt;/a>）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>命令&lt;/th>
&lt;th>状态&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>sandbox create &amp;lt;template&amp;gt;&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>创建沙箱并连接交互式终端，&lt;strong>退出终端后自动终止该沙箱&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sandbox list&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>默认返回运行中的；可用 &lt;code>--state&lt;/code>、&lt;code>--metadata&lt;/code>、&lt;code>--limit&lt;/code>、&lt;code>--format&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sandbox kill&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>按 ID 终止，也支持 &lt;code>--all&lt;/code> 批量终止&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sandbox connect&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>连接已有沙箱；&lt;strong>退出终端不会自动终止&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sandbox exec&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>在运行中的沙箱内执行命令，输出回到本地终端&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sandbox metrics&lt;/code>&lt;/td>
&lt;td>支持&lt;/td>
&lt;td>CPU、内存，分钟级粒度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>template list&lt;/code>&lt;/td>
&lt;td>云沙箱扩展&lt;/td>
&lt;td>E2B 官方 CLI 文档未单独说明该命令&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>&lt;code>create&lt;/code> 和 &lt;code>connect&lt;/code> 在退出终端时的行为是相反的&lt;/strong>，这一点特别容易咬人：&lt;code>e2b sandbox create&lt;/code> 退出就把沙箱杀了，&lt;code>e2b sandbox connect&lt;/code> 退出则留着。调试时用错一个，要么白等一遍启动，要么留下一堆僵尸沙箱在账单上。&lt;/p>
&lt;p>CLI 的安装与配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install e2b &lt;span class="c1"># macOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm i -g @e2b/cli@2.20.0 &lt;span class="c1"># 或者用 npm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">e2b --version &lt;span class="c1"># 装完先确认版本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;your-api-key&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_API_URL&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://api.&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">E2B_DOMAIN&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">e2b sandbox list &lt;span class="c1"># 用一条只读命令验证配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">e2b template list &lt;span class="c1"># 看当前账号有哪些模板&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CLI 不固定单一版本，官方建议用最新兼容版本。&lt;strong>如果命令行为和文档不一致，先跑 &lt;code>e2b --version&lt;/code> 和对应命令的 &lt;code>--help&lt;/code>&lt;/strong>，确认本机 CLI 的版本和参数形态，而不是先怀疑文档。&lt;/p>
&lt;h2 id="四五个会静默骗你的坑">四、五个会静默骗你的坑
&lt;/h2>&lt;p>这部分是我觉得最有信息量的。它们有的抛异常、有的不抛，但共同点是：&lt;strong>都不会告诉你「你要的功能其实没生效」。&lt;/strong>&lt;/p>
&lt;h3 id="坑一fileswrite-的-metadata-参数">坑一：&lt;code>files.write()&lt;/code> 的 &lt;code>metadata&lt;/code> 参数
&lt;/h3>&lt;p>云沙箱当前不支持文件自定义元数据。但 SDK 的方法签名&lt;strong>是接受 &lt;code>metadata&lt;/code> 的&lt;/strong>——Python SDK 会在请求发出去之前自己拦下来，报的错长这样（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-metadata" target="_blank" rel="noopener"
>自定义元数据&lt;/a>）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">e2b.exceptions.TemplateException:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">File metadata requires envd 0.6.2 or later.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>原因写得很清楚：Sandbox 当前上报 &lt;code>envd 0.5.2&lt;/code>，而文件自定义元数据需要 &lt;code>envd 0.6.2&lt;/code> 或以上。普通文件读写完全不受影响。&lt;/p>
&lt;p>那页文档还补了一句很关键的：&lt;strong>「升级 SDK 或仅修改 &lt;code>envd&lt;/code> 版本号声明无法绕过限制」&lt;/strong>——该能力需要运行环境完整支持元数据校验、键名小写化和扩展属性持久化。所以这不是一个「等版本」的问题，是后端还没实现。&lt;/p>
&lt;p>顺手澄清一个容易混的概念，这个混淆我自己也犯过：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>文件自定义元数据&lt;/th>
&lt;th>Sandbox &lt;code>metadata&lt;/code>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>挂在哪&lt;/td>
&lt;td>单个文件上&lt;/td>
&lt;td>沙箱控制面&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入方式&lt;/td>
&lt;td>&lt;code>files.write(..., metadata=...)&lt;/code>&lt;/td>
&lt;td>&lt;code>Sandbox.create(..., metadata=...)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>当前可用&lt;/td>
&lt;td>&lt;strong>否&lt;/strong>&lt;/td>
&lt;td>是&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>典型用途&lt;/td>
&lt;td>给文件打业务标签&lt;/td>
&lt;td>标记任务、用户、场景、版本，便于列表过滤和审计&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>控制面 &lt;code>metadata&lt;/code> 的用法是这样的（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/metadata" target="_blank" rel="noopener"
>元数据&lt;/a>），创建时写、&lt;code>getInfo()&lt;/code> 读，也能用来过滤 &lt;code>Sandbox.list()&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;code-interpreter-v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;task_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;task-001&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;code-reviewer&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_info&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有个细节文档专门强调了：&lt;strong>元数据不会自动变成沙箱内的环境变量。&lt;/strong> 如果同一个字段既要管理面可见、又要沙箱内进程能读到，得同时写进 &lt;code>metadata&lt;/code> 和 &lt;code>envs&lt;/code>。&lt;/p>
&lt;p>元数据的取值建议用短字符串，只放可索引的关联 ID。大段上下文、用户隐私、凭证都不要塞进去。&lt;/p>
&lt;p>&lt;strong>那文件标签怎么做？&lt;/strong> 文档给了三条替代路径，我按推荐度排了一下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>标签存业务库或对象存储&lt;/strong>，用「Sandbox ID + 文件路径」做关联键。这是最干净的。&lt;/li>
&lt;li>&lt;strong>同目录写一个 JSON 清单文件&lt;/strong>，记录文件路径和业务标签。需要跨 Sandbox 保留时，把文件和清单一起写进 NAS / OSS。&lt;/li>
&lt;li>&lt;strong>如果只是想标记 Sandbox 本身&lt;/strong>，直接用控制面 &lt;code>metadata&lt;/code>——但别把它当文件元数据用。&lt;/li>
&lt;/ol>
&lt;h3 id="坑二run_code-用错了-sdk">坑二：&lt;code>run_code&lt;/code> 用错了 SDK
&lt;/h3>&lt;p>这个坑卡了我一会儿。现象是：Sandbox 创建成功，&lt;code>commands.run()&lt;/code> 正常，但 &lt;code>run_code&lt;/code> 就是不行。&lt;/p>
&lt;p>原因是 SDK 装错了。&lt;strong>通用 &lt;code>e2b&lt;/code> SDK 只能使用文件、命令和进程这些基础能力&lt;/strong>——即使你指定了 &lt;code>code-interpreter-v1&lt;/code> 模板，也一样调不了 &lt;code>runCode&lt;/code> / &lt;code>run_code&lt;/code>。&lt;/p>
&lt;p>而这个坑有一半责任在&lt;strong>默认值&lt;/strong>上。官方模板文档列得很清楚：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>SDK&lt;/th>
&lt;th>&lt;code>template&lt;/code> 要不要传&lt;/th>
&lt;th>不传时的默认模板&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>e2b_code_interpreter&lt;/code>（Python）/ &lt;code>@e2b/code-interpreter&lt;/code>（TS）&lt;/td>
&lt;td>&lt;strong>不用传&lt;/strong>&lt;/td>
&lt;td>&lt;code>code-interpreter-v1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>e2b&lt;/code>（通用）&lt;/td>
&lt;td>&lt;strong>必须传&lt;/strong>&lt;/td>
&lt;td>&lt;strong>&lt;code>base&lt;/code>&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>通用 SDK 不传模板拿到的是 &lt;code>base&lt;/code>，而 &lt;code>base&lt;/code> 不提供 Code Interpreter 服务。&lt;/strong> 所以「我用通用 SDK + 不指定模板 + 调 run_code」和「我用通用 SDK + 指定 code-interpreter-v1 + 调 run_code」，失败原因其实不同：前者连模板都不对，后者模板对了但 SDK 不对。&lt;/p>
&lt;p>判断顺序也简单：先用内置 &lt;code>code-interpreter-v1&lt;/code> 模板跑通代码执行、命令执行、文件读写。内置模板正常而自定义模板不行，再去查自定义模板的 Code Interpreter 依赖、启动命令、监听端口和就绪条件。&lt;/p>
&lt;h3 id="坑三上传下载-url-需要-securefalse">坑三：上传下载 URL 需要 &lt;code>secure=false&lt;/code>
&lt;/h3>&lt;p>这个坑不报错，但它会让一个「看起来已经写好的」浏览器直传链路在最后一跳 403。见&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/upload-and-download-files" target="_blank" rel="noopener"
>上传和下载文件&lt;/a>。&lt;/p>
&lt;p>&lt;code>sandbox.downloadUrl(path)&lt;/code> 和 &lt;code>sandbox.uploadUrl(path)&lt;/code> 生成的是带签名的访问地址，适合浏览器直传、把结果文件交给未持有 SDK 鉴权信息的环境，或者交给后续系统处理。但文档里有一句加粗的注意：&lt;/p>
&lt;blockquote>
&lt;p>如果要使用 &lt;code>downloadUrl()&lt;/code> 和 &lt;code>uploadUrl()&lt;/code> 返回的 URL 上传下载文件，&lt;strong>确保在创建 Sandbox 时显式设置了 &lt;code>secure=false&lt;/code>&lt;/strong>。否则，通过 URL 上传下载文件仍需要请求方携带 &lt;code>X-Access-Token&lt;/code>。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">secure&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>而 &lt;code>secure=false&lt;/code> 的代价文档也写得很直白：&lt;strong>会降低 Sandbox 暴露端点的访问保护强度。&lt;/strong> 所以要配合「控制 Sandbox 生命周期、文件路径、数据敏感性」一起用。&lt;/p>
&lt;p>我自己的取舍是：如果只是后端之间传文件，宁可不省这一下，直接用 &lt;code>files.write()&lt;/code> / &lt;code>files.read()&lt;/code>。只有「浏览器直传」这种真的拿不到 SDK 凭证的场景，才开 &lt;code>secure=false&lt;/code>，并且把路径写死在业务侧、不让它由模型决定。&lt;/p>
&lt;p>选型标准文档给得也挺清楚，直接抄：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>用什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>小文件、文本、二进制、流式内容、Agent 生成代码&lt;/td>
&lt;td>&lt;code>files.write()&lt;/code> / &lt;code>files.read()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>浏览器直传、下载结果文件、交给无 SDK 鉴权环境&lt;/td>
&lt;td>&lt;code>uploadUrl()&lt;/code> / &lt;code>downloadUrl()&lt;/code> + &lt;code>secure=false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>目录遍历、重命名、删除&lt;/td>
&lt;td>Filesystem API&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>浏览器直传的接收端用 &lt;code>multipart/form-data&lt;/code>，字段名是 &lt;code>file&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">requests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input.csv&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;rb&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">upload_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">files&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;file&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">})&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">raise_for_status&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>大文件上传后建议用 &lt;code>sandbox.files.exists()&lt;/code> 或命令确认文件可读&lt;/strong>——文档专门提了这句，说明「上传成功但文件不可读」是有过的。&lt;/p>
&lt;h3 id="坑四logs-和-network-config-update">坑四：Logs 和 Network Config Update
&lt;/h3>&lt;p>这两个是官方明确标记为「受限」的：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Sandbox Logs&lt;/strong>：当前返回空数组。&lt;/li>
&lt;li>&lt;strong>Network Config Update&lt;/strong>：当前返回成功，但&lt;strong>不进行实际的网络变更&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>官方对它们的定位说得很坦诚——「用于保持 SDK 调用兼容」。所以它们可以留着，让老代码不至于跑挂；但&lt;strong>不能作为生产日志采集或网络治理的控制面依赖&lt;/strong>。接入参数说明那页也把它们列进了「不作为接入参数的能力」。&lt;/p>
&lt;p>第二个尤其阴。返回 &lt;code>success&lt;/code> 的操作什么都不做，这种设计在本地联调时完全看不出来，只有到线上要改网络策略、发现改了没生效的时候才会撞上。&lt;/p>
&lt;p>正路是：&lt;strong>日志走函数计算日志采集 + 日志服务，网络变更走云沙箱控制面&lt;/strong>（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/monitoring-and-logging" target="_blank" rel="noopener"
>监控与日志&lt;/a>）。而且更进一步——「生产排查应依赖业务侧结构化日志、函数计算日志采集、云监控和日志服务中的数据」。&lt;/p>
&lt;p>配合这个结论，文档给了一段挺实用的日志约定：在业务侧记录 &lt;code>sandbox_created&lt;/code> / &lt;code>command_finished&lt;/code> 这类事件，字段带上 &lt;code>taskId&lt;/code>、&lt;code>sandboxId&lt;/code>、&lt;code>exitCode&lt;/code>、stdout / stderr 摘要，并且&lt;strong>业务系统要保存任务 ID 与 &lt;code>sandboxId&lt;/code> 的映射&lt;/strong>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;event&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;command_finished&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;taskId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">task_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;sandboxId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sandbox_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;exitCode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exit_code&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;stdout&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;stderr&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stderr&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果希望沙箱内的程序日志也能被日志服务检索，就让程序直接输出结构化 JSON、并带上 &lt;code>sandboxId&lt;/code> 和任务 ID。两条日志流最后在日志服务里能按 &lt;code>taskId&lt;/code> 关联起来。&lt;/p>
&lt;p>&lt;strong>关于日志采集配置，文档提醒了三件事要先确认&lt;/strong>：配置入口（以云沙箱控制台 / 函数计算控制台 / 日志服务为准，控制台没展示入口就联系产品支持）、生效范围（对账号 / 地域 / 模板 / Sandbox / 会话生效，以及是否只影响新建的 Sandbox）、日志目标（Project、Logstore、索引字段、保存时间和费用策略）。第三项最容易被忽略——&lt;strong>日志费用是那种平时不痛、月底很痛的东西&lt;/strong>。&lt;/p>
&lt;h3 id="坑五metrics-的磁盘字段">坑五：Metrics 的磁盘字段
&lt;/h3>&lt;p>&lt;code>e2b sandbox metrics &amp;lt;sandbox-id&amp;gt;&lt;/code> 能看 CPU 和内存，但&lt;strong>磁盘 / 页缓存字段返回的是占位值&lt;/strong>，不能用来判断容量。数据按 1 分钟粒度返回。&lt;/p>
&lt;p>官方给的建议是：计费、告警、容量治理都要以函数计算控制台、云监控或日志服务里的正式数据为准。指标接口适合调试和看趋势，别拿去搭容量大盘——&lt;strong>1 分钟粒度配占位字段，做成大盘只会得到一条看起来很有道理、实际上是假的曲线。&lt;/strong>&lt;/p>
&lt;h2 id="五snapshot-和-pause白名单能力别写进架构">五、Snapshot 和 pause：白名单能力，别写进架构
&lt;/h2>&lt;h3 id="snapshot-比兼容两个字复杂得多">Snapshot 比「兼容」两个字复杂得多
&lt;/h3>&lt;p>我一开始是照「不兼容」处理的，后来翻&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/snapshots" target="_blank" rel="noopener"
>快照（邀测）&lt;/a>发现已经变了。&lt;strong>Snapshots 现在兼容，但要加白名单，而且仅限第二代运行时（&lt;code>micro-sandbox&lt;/code>）。&lt;/strong>&lt;/p>
&lt;p>它的心智模型很好理解：&lt;strong>保存运行中沙箱在某一时刻的文件系统与内存状态，之后用这份快照秒级启动一个同样状态的新沙箱，不必重新构建模板。&lt;/strong> 典型场景就是 Agent 任务断点续跑、环境预热、并行克隆探索。&lt;/p>
&lt;p>几个关键属性：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Snapshot 独立于源沙箱和源模板。&lt;/strong> 删除源沙箱或源模板不会删掉 Snapshot；删 Snapshot 也不会影响它们。创建成功后即使源模板被删，仍能用该 Snapshot 恢复沙箱。&lt;/li>
&lt;li>&lt;strong>默认保留 7 天&lt;/strong>，到期自动过期，过期后不出现在列表里、也不能用于创建沙箱。&lt;/li>
&lt;li>&lt;strong>留存独立于源沙箱生命周期&lt;/strong>——源沙箱终止或超时回收后，未过期的 Snapshot 仍可用。&lt;/li>
&lt;/ul>
&lt;p>API 表（注意 Python 和 TypeScript 的名字差异）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>操作&lt;/th>
&lt;th>TypeScript&lt;/th>
&lt;th>Python&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>创建&lt;/td>
&lt;td>&lt;code>sandbox.createSnapshot({ name? })&lt;/code> / &lt;code>Sandbox.createSnapshot(sandboxId, { name? })&lt;/code>&lt;/td>
&lt;td>&lt;code>sandbox.create_snapshot(name=...)&lt;/code> / &lt;code>Sandbox.create_snapshot(sandbox_id, name=...)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>列表&lt;/td>
&lt;td>&lt;code>Sandbox.listSnapshots({ sandboxId?, name?, limit?, nextToken? })&lt;/code>&lt;/td>
&lt;td>&lt;code>Sandbox.list_snapshots(sandbox_id=..., name=..., limit=..., next_token=...)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>从快照建沙箱&lt;/td>
&lt;td>&lt;code>Sandbox.create(snapshotId | &amp;quot;&amp;lt;TeamName&amp;gt;/&amp;lt;短名&amp;gt;&amp;quot;)&lt;/code>&lt;/td>
&lt;td>&lt;code>Sandbox.create(template=snapshot_id | &amp;quot;&amp;lt;TeamName&amp;gt;/&amp;lt;短名&amp;gt;&amp;quot;)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>删除&lt;/td>
&lt;td>&lt;code>Sandbox.deleteSnapshot(snapshotId | qualifiedName)&lt;/code>&lt;/td>
&lt;td>&lt;code>Sandbox.delete_snapshot(snapshot_id | qualified_name)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>实例方法和静态方法语义相同——实例方法隐含当前沙箱，不用再传 ID；&lt;strong>列表和删除只提供静态方法&lt;/strong>。&lt;/p>
&lt;p>返回字段里有两个：&lt;code>snapshotId&lt;/code> / &lt;code>snapshot_id&lt;/code> 是 UUID，创建、删除、恢复时&lt;strong>优先用它&lt;/strong>；&lt;code>names&lt;/code> 是有名快照的全名列表，无名快照是空数组。&lt;strong>两者相互独立&lt;/strong>——全名只出现在 &lt;code>names&lt;/code> 字段和按名解析里，不替代 ID。&lt;/p>
&lt;p>&lt;strong>命名规则是这里最容易踩的地方。&lt;/strong> 全名格式是 &lt;code>&amp;lt;当前 Team 名&amp;gt;/&amp;lt;短名&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code>：&lt;/p>
&lt;ul>
&lt;li>短名和 tag 的规则是 &lt;code>^[_a-zA-Z][-_a-zA-Z0-9]*$&lt;/code>，最长 64，必须以字母或下划线开头。&lt;/li>
&lt;li>Team 名前缀与 Team 展示名相同，最长 32，允许数字开头和空格、点、下划线、连字符（例如默认 Team 的数字名、&lt;code>My Team&lt;/code>）。&lt;/li>
&lt;li>段内不能含 &lt;code>/&lt;/code> 或 &lt;code>:&lt;/code>，所以「含且仅含一个 &lt;code>/&lt;/code> 的字符串」只可能是 Snapshot 全名，不会和模板名冲突。&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>传入的 &lt;code>name&lt;/code>&lt;/th>
&lt;th>结果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>省略 / 空&lt;/td>
&lt;td>无名，&lt;code>names = []&lt;/code>，只能通过 Snapshot ID 访问&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>deps-ready&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;当前 Team 名&amp;gt;/deps-ready:default&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>deps-ready:v1&lt;/code>&lt;/td>
&lt;td>&lt;code>&amp;lt;当前 Team 名&amp;gt;/deps-ready:v1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>demo-team/deps-ready&lt;/code>&lt;/td>
&lt;td>&lt;code>demo-team/deps-ready:default&lt;/code>（前缀必须等于当前 Team 名，大小写不敏感）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>省略 tag 等价于 &lt;code>default&lt;/code>。同一 Team 内同一个 &lt;code>(短名, tag)&lt;/code> 同时只能存在一份可用 Snapshot，&lt;strong>名称冲突时创建失败，不覆盖已有快照&lt;/strong>，删除后名称立即释放。&lt;strong>Team 没有名字时只能创建无名 Snapshot&lt;/strong>。&lt;/p>
&lt;p>&lt;strong>然后是那个我觉得最实用的坑：请求超时。&lt;/strong>&lt;/p>
&lt;p>创建可能持续数分钟。文档明确建议 &lt;strong>SDK 请求超时不少于 300 秒&lt;/strong>（TypeScript &lt;code>requestTimeoutMs: 300_000&lt;/code>；Python &lt;code>request_timeout=300&lt;/code>）。并且特意点破了那个容易混的点：&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>timeoutMs&lt;/code> / &lt;code>timeout&lt;/code> 控制的是沙箱生命周期，&lt;strong>不是 Snapshot 请求超时&lt;/strong>；默认请求超时通常为 60 秒，不加大时可能在创建完成前失败。&lt;/p>
&lt;/blockquote>
&lt;p>60 秒默认值 + 数分钟的实际耗时 = 一个会间歇性失败的接口。而更麻烦的是它失败之后的处理：&lt;/p>
&lt;blockquote>
&lt;p>创建超时：创建失败，但&lt;strong>服务端可能已建成 Snapshot&lt;/strong>。先增大超时。有名 Snapshot 按 &lt;code>name&lt;/code> 或全名列表确认后再决定是否重试；无名 Snapshot 的 &lt;code>name&lt;/code> 过滤无效，应先按源沙箱 &lt;code>sandboxId&lt;/code> 列表核对；&lt;strong>若已存在目标 Snapshot，直接使用其 ID，不要无条件重试。&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>「不要无条件重试」这六个字是整篇文档里我最想圈出来的一句。它意味着一个朴素的重试装饰器在这里会持续制造垃圾快照，而每一份都在算存储费。&lt;/p>
&lt;p>还有个更隐蔽的：&lt;strong>创建成功后立即调用列表接口，由于索引延迟，可能暂时看不到刚写入的记录。&lt;/strong> 但按 ID 或全名恢复、删除都不受影响，可以直接用创建接口返回的值——所以别用「列表里没有」来判断创建失败。&lt;/p>
&lt;p>失败条件我整理成表：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>条件&lt;/th>
&lt;th>结果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>源沙箱不存在或不属于当前调用方&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>沙箱运行时不是 &lt;code>MicroVM&lt;/code>&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>名称非法，或全名前缀不是当前 Team 名&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Team 无名却传了 &lt;code>name&lt;/code>&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>同名同 tag 已存在&lt;/td>
&lt;td>创建失败，不覆盖&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>源沙箱已暂停 / 正在创建 Snapshot / 被其他操作占用&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>从 Snapshot 创建时覆盖环境变量、挂载或镜像&lt;/td>
&lt;td>创建失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Snapshot 仍被已恢复沙箱占用&lt;/td>
&lt;td>删除失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>按全名恢复或删除但 Snapshot 不存在&lt;/td>
&lt;td>失败，&lt;strong>不回退为模板&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>创建过程中源沙箱的 &lt;code>state&lt;/code> 是 &lt;code>snapshotting&lt;/code>，此时&lt;strong>不能删除该沙箱&lt;/strong>；失败则是 &lt;code>snapshot_failed&lt;/code>，这个状态&lt;strong>默认不出现在沙箱列表里&lt;/strong>，得显式按状态过滤才看得到。这一点挺关键——排查「为什么快照没建出来」时，默认列表里是找不到线索的。失败后源沙箱仍可使用或删除。&lt;/p>
&lt;p>从 Snapshot 创建沙箱时，哪些参数能覆盖也有明确清单：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>允许覆盖&lt;/strong>：超时、空闲超时、&lt;code>secure&lt;/code>、&lt;code>allowInternetAccess&lt;/code>、用户 metadata、&lt;code>autoPause&lt;/code>、&lt;code>autoResume&lt;/code>、网络、自定义沙箱 ID。&lt;/li>
&lt;li>&lt;strong>不允许覆盖（传入即失败）&lt;/strong>：环境变量、卷 / 文件系统挂载、函数配置、构建 / 镜像、&lt;code>fc.&lt;/code> 前缀的系统 metadata。&lt;/li>
&lt;/ul>
&lt;p>以及一个 TS 特有的坑：&lt;strong>&lt;code>listSnapshots&lt;/code> / &lt;code>createSnapshot&lt;/code> / &lt;code>deleteSnapshot&lt;/code> 的静态调用要走环境变量配 &lt;code>E2B_API_URL&lt;/code>，不要传不被支持的 &lt;code>apiUrl&lt;/code> 字段。&lt;/strong>&lt;/p>
&lt;p>最后一条使用建议我觉得挺值得照做：&lt;strong>没用的 Snapshot 及时删，避免持续产生存储费用；能短时暂停就优先用暂停与恢复，不要动不动就快照。&lt;/strong> 7 天保留期是一个「会悄悄花钱」的默认值。&lt;/p>
&lt;h3 id="pause只为短期保留上下文">pause：只为短期保留上下文
&lt;/h3>&lt;p>&lt;code>sandbox.pause()&lt;/code> 同样需要白名单（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/pause-and-resume" target="_blank" rel="noopener"
>暂停与恢复&lt;/a>）。它的语义是「在一段时间内保留状态，之后连同一个 Sandbox 继续用」。&lt;code>Sandbox.connect(sandboxId)&lt;/code> 连一个已暂停的沙箱时会自动恢复。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pause&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 之后&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">connect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sandbox_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E2B_API_KEY&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E2B_API_URL&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">domain&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;E2B_DOMAIN&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>文档里有三句提醒，我原样保留，因为它们都很容易忽略：&lt;/p>
&lt;ul>
&lt;li>恢复后要&lt;strong>重新确认长连接、进程状态和业务层状态&lt;/strong>是否符合预期。暂停期间那些东西的存活不能想当然。&lt;/li>
&lt;li>&lt;strong>暂停不等于终止。&lt;/strong> 任务完成后仍然要 &lt;code>kill()&lt;/code>。&lt;/li>
&lt;li>对需要短期保留上下文的任务才用暂停，不要拿它替代资源释放。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>我的整体判断是：白名单能力不要写进架构。&lt;/strong> 白名单是账号级的开关，今天开了明天能关，把它当成设计前提，等于把系统的可用性挂在一个后台配置上。Snapshot 可以当作「运维手段」和「性能优化」，但不要把「断点续跑」做成唯一路径——至少留一条「重建环境」的兜底。&lt;/p>
&lt;h2 id="六模板三个档位和一个必须自己构建的">六、模板：三个档位，和一个必须自己构建的
&lt;/h2>&lt;p>模板是这次迁移里我改动最大的一块。内置模板不是「一堆可选镜像」，而是三个能力档位，而且&lt;strong>默认值会咬人&lt;/strong>。&lt;/p>
&lt;h3 id="内置模板清单">内置模板清单
&lt;/h3>&lt;p>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/built-in-templates" target="_blank" rel="noopener"
>内置模板&lt;/a>一共三个，另有若干需要自行构建：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>模板&lt;/th>
&lt;th>需要构建&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/base-template" target="_blank" rel="noopener"
>&lt;code>base&lt;/code>&lt;/a>&lt;/td>
&lt;td>否，开通即用&lt;/td>
&lt;td>基础命令、文件访问、SDK 连通性验证&lt;/td>
&lt;td>&lt;strong>不提供 Code Interpreter 服务&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/code-interpreter-v1-template" target="_blank" rel="noopener"
>&lt;code>code-interpreter-v1&lt;/code>&lt;/a>&lt;/td>
&lt;td>否，开通即用&lt;/td>
&lt;td>AI Agent 代码执行、数据分析、文件处理&lt;/td>
&lt;td>快速入门和代码解释器示例默认用它&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/browser-template" target="_blank" rel="noopener"
>&lt;code>browser&lt;/code>&lt;/a>&lt;/td>
&lt;td>&lt;strong>是&lt;/strong>&lt;/td>
&lt;td>浏览器自动化、截图、动态页面抓取&lt;/td>
&lt;td>要从官方 browser 镜像构建&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/all-in-one-template" target="_blank" rel="noopener"
>All-In-One&lt;/a>&lt;/td>
&lt;td>&lt;strong>是&lt;/strong>&lt;/td>
&lt;td>浏览器 + 代码执行协同&lt;/td>
&lt;td>要从官方 all-in-one 镜像构建&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>其他（Desktop、Claude Code、OpenClaw 等）&lt;/td>
&lt;td>&lt;strong>是&lt;/strong>&lt;/td>
&lt;td>桌面环境、编码 Agent&lt;/td>
&lt;td>从对应官方镜像自行构建&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>base&lt;/code> 和 &lt;code>code-interpreter-v1&lt;/code> 在账号开通云沙箱后自动就绪，直接用模板名创建沙箱即可。&lt;/p>
&lt;h3 id="三者的默认配置对比">三者的默认配置对比
&lt;/h3>&lt;p>这部分我建议直接抄进自己的容量规划里：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>配置项&lt;/th>
&lt;th>&lt;code>base&lt;/code>&lt;/th>
&lt;th>&lt;code>code-interpreter-v1&lt;/code>&lt;/th>
&lt;th>&lt;code>browser&lt;/code>&lt;/th>
&lt;th>All-In-One&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CPU&lt;/td>
&lt;td>2 vCPU（最低要求）&lt;/td>
&lt;td>2 vCPU（最低要求）&lt;/td>
&lt;td>4 vCPU（推荐起始）&lt;/td>
&lt;td>4 vCPU（推荐规格）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>内存&lt;/td>
&lt;td>2048 MB（最低要求）&lt;/td>
&lt;td>2048 MB（最低要求）&lt;/td>
&lt;td>8192 MB（推荐起始）&lt;/td>
&lt;td>8192 MB（推荐规格）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>磁盘&lt;/td>
&lt;td>10240 MB&lt;/td>
&lt;td>10240 MB&lt;/td>
&lt;td>10240 MB&lt;/td>
&lt;td>10240 MB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>端口&lt;/td>
&lt;td>无业务端口（只有 envd 基础服务）&lt;/td>
&lt;td>5000（沙箱服务监听）&lt;/td>
&lt;td>3000（browser 服务）&lt;/td>
&lt;td>3000（浏览器）+ 5000（代码/文件）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Code Interpreter&lt;/td>
&lt;td>不支持&lt;/td>
&lt;td>支持 Python / JavaScript&lt;/td>
&lt;td>不支持&lt;/td>
&lt;td>支持&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>base&lt;/code> 的定位写得很清楚：提供最小化运行环境，内置 E2B envd 兼容基础服务，是 &lt;code>code-interpreter-v1&lt;/code>、&lt;code>browser&lt;/code>、All-In-One 三个模板的&lt;strong>共同能力基础&lt;/strong>。它不预装数据科学库，也不预置浏览器自动化服务。&lt;/p>
&lt;p>&lt;strong>回到坑二那句话&lt;/strong>——通用 &lt;code>e2b&lt;/code> SDK 不传 &lt;code>template&lt;/code> 时默认创建的就是 &lt;code>base&lt;/code> 沙箱。而 &lt;code>base&lt;/code> 的 TypeScript 写法还不太一样，没有模板时 options 是第一个参数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">sbx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Sandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">timeoutMs&lt;/span>: &lt;span class="kt">600_000&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="code-interpreter-的硬限制">Code Interpreter 的硬限制
&lt;/h3>&lt;p>&lt;code>code-interpreter-v1&lt;/code> 那页除了能力介绍，还给了一组迁移时一定会用到的数字：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>限制项&lt;/th>
&lt;th>约束&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>沙箱生命周期&lt;/td>
&lt;td>单个实例&lt;strong>最长 24 小时&lt;/strong>（&lt;code>timeout&lt;/code> 上限 &lt;strong>86400 秒&lt;/strong>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>空闲超时&lt;/td>
&lt;td>&lt;code>sandboxIdleTimeoutSeconds&lt;/code>，&lt;strong>有效下限 60 秒&lt;/strong>（低于 60 秒按 60 秒生效）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>代码执行超时&lt;/td>
&lt;td>单次同步执行默认 &lt;strong>Python 300 秒 / TypeScript 60000 毫秒&lt;/strong>，可通过 &lt;code>timeout&lt;/code> / &lt;code>timeoutMs&lt;/code> 调整&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>语言支持&lt;/td>
&lt;td>Python、JavaScript（&lt;code>language&lt;/code> 默认 &lt;code>python&lt;/code>）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>24 小时这个上限值得单独记住。&lt;/strong> 如果你的 Runtime 设计里有一个「常驻沙箱」的角色，它在这里是有天花板的。&lt;/p>
&lt;h3 id="code-interpreter-sandbox-的生产形态">Code Interpreter Sandbox 的生产形态
&lt;/h3>&lt;p>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-code-interpreter-sandbox" target="_blank" rel="noopener"
>使用 Code Interpreter Sandbox&lt;/a>那页其实在讲一件更重要的方法论：&lt;strong>交互式 &lt;code>run_code&lt;/code> 适合开发，固定脚本入口适合生产。&lt;/strong>&lt;/p>
&lt;p>它建议把分析任务拆成四步：&lt;/p>
&lt;ol>
&lt;li>写入输入数据和分析脚本。&lt;/li>
&lt;li>使用&lt;strong>固定入口&lt;/strong>执行脚本，例如 &lt;code>python3 analyze.py&lt;/code>。&lt;/li>
&lt;li>要求脚本输出 JSON 摘要，必要时生成文件产物。&lt;/li>
&lt;li>读取结果并销毁沙箱。&lt;/li>
&lt;/ol>
&lt;p>理由说得很直白：&lt;strong>固定脚本入口更容易做审计、超时和输出校验&lt;/strong>，因为脚本版本、输入目录、超时时间和输出格式都更容易固化。&lt;code>run_code&lt;/code> 更适合模型逐步生成和修正代码的交互式任务。&lt;/p>
&lt;p>它还顺手给了一份上线建议，我挑几条最实用的：&lt;/p>
&lt;ul>
&lt;li>输入文件要限制大小、类型和路径，避免一次任务占用过多内存或磁盘。&lt;/li>
&lt;li>&lt;strong>输出优先用 JSON 摘要&lt;/strong>，图表、表格、报告文件写 &lt;code>/tmp&lt;/code> 后再下载。&lt;/li>
&lt;li>对 &lt;code>stdout&lt;/code>、&lt;code>stderr&lt;/code>、退出码和结果文件做&lt;strong>统一封装&lt;/strong>，避免上层 Agent 直接解析非结构化日志。&lt;/li>
&lt;li>&lt;strong>对分析代码做版本化&lt;/strong>——「生产系统不要只保存模型生成的自然语言解释」，这句挺重的。&lt;/li>
&lt;li>常用依赖（pandas、openpyxl、绘图库、业务 SDK）放进模板。&lt;/li>
&lt;li>用户可见结果和排障日志分开处理，别把完整堆栈或敏感数据直接返回。&lt;/li>
&lt;/ul>
&lt;h3 id="browser-模板不是开通即用cdp-之外还要过鉴权">browser 模板：不是开通即用，CDP 之外还要过鉴权
&lt;/h3>&lt;p>&lt;code>browser&lt;/code> 模板提供云原生浏览器环境，通过&lt;strong>标准 Chrome DevTools Protocol（CDP）over WebSocket&lt;/strong> 远程控制，原生兼容 Puppeteer、Playwright。它内置 VNC 服务，可以实时看浏览器桌面。&lt;strong>但它的使用分两个阶段：先构建模板，再运行模板。&lt;/strong>&lt;/p>
&lt;p>&lt;strong>默认配置&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>配置项&lt;/th>
&lt;th>值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>容器镜像&lt;/td>
&lt;td>&lt;code>fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>默认端口&lt;/td>
&lt;td>3000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU&lt;/td>
&lt;td>4 vCPU（推荐起始）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>内存&lt;/td>
&lt;td>8192 MB（推荐起始）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>磁盘&lt;/td>
&lt;td>10240 MB（建议 10 GB）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>第一步：构建。&lt;/strong> 从官方镜像固化出一个带名称的模板：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dotenv&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dotenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">e2b&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Template&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">default_build_logger&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_dotenv&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">FROM_IMAGE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">build&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Template&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Template&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_image&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FROM_IMAGE&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;my-browser-template&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpu_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_mb&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8192&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">on_build_logs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">default_build_logger&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;template_id: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">template_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>第二步：运行。&lt;/strong> 创建沙箱后&lt;strong>先轮询 &lt;code>/health&lt;/code> 等 browser 服务就绪&lt;/strong>，再连 CDP：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sbx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;my-browser-template&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">900&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">host&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BROWSER_PORT&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># BROWSER_PORT = 3000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_envd_access_token&lt;/span> &lt;span class="c1"># 公网网关要求 X-Access-Token，否则 403&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">headers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;X-Access-Token&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">token&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">token&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wait_until_healthy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sbx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">host&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">token&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">sync_playwright&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chromium&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">connect_over_cdp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;wss://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/ws/automation&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三个 WebSocket 端点，&lt;strong>全部需要在请求头带 &lt;code>X-Access-Token&lt;/code> 鉴权&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>端点&lt;/th>
&lt;th>路径&lt;/th>
&lt;th>用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>健康检查&lt;/td>
&lt;td>&lt;code>https://&amp;lt;sandbox-host&amp;gt;/health&lt;/code>&lt;/td>
&lt;td>判断 browser 服务是否启动完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CDP 自动化&lt;/td>
&lt;td>&lt;code>wss://&amp;lt;sandbox-host&amp;gt;/ws/automation&lt;/code>&lt;/td>
&lt;td>浏览器自动化，兼容 Puppeteer 和 Playwright&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VNC 实时流&lt;/td>
&lt;td>&lt;code>wss://&amp;lt;sandbox-host&amp;gt;/ws/livestream&lt;/code>&lt;/td>
&lt;td>实时查看浏览器桌面，可用 noVNC 客户端&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>这里的坑密度比前面几个模块高，我总结成四条：&lt;/strong>&lt;/p>
&lt;p>&lt;strong>① &lt;code>X-Access-Token&lt;/code> 的取法是非公开 API。&lt;/strong> Python 用 &lt;code>sbx._envd_access_token&lt;/code>（下划线前缀，说明是内部属性），TypeScript 用 &lt;code>sbx.envdAccessToken&lt;/code>。文档自己标注了「后续版本可能重命名或移除」。&lt;strong>这意味着这条链路在 SDK 升级时是有断裂风险的&lt;/strong>，值得在代码里加一层封装并写测试。&lt;/p>
&lt;p>&lt;strong>② noVNC 这种纯浏览器客户端连不上。&lt;/strong> 原因很实在：&lt;strong>浏览器 WebSocket API 不支持在握手时设置自定义请求头&lt;/strong>，所以带不了 &lt;code>X-Access-Token&lt;/code>，直连就是 403。要看画面得换支持自定义 header 的客户端（&lt;code>wscat&lt;/code>、Python &lt;code>websockets&lt;/code>）。&lt;strong>如果只是想看结果，用 CDP 连接后 &lt;code>page.screenshot()&lt;/code> 截图更省事&lt;/strong>——我觉得这是官方给的最务实的一句建议。&lt;/p>
&lt;p>&lt;strong>③ 改窗口尺寸要重新烤镜像，&lt;code>envs&lt;/code> 不管用。&lt;/strong> 浏览器窗口和虚拟屏幕由镜像里三个环境变量控制：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>环境变量&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>默认值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>RESOLUTION&lt;/code>&lt;/td>
&lt;td>Xvfb 虚拟屏幕分辨率（宽x高x色深）&lt;/td>
&lt;td>&lt;code>1680x1050x24&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>BROWSER_WINDOW_SIZE&lt;/code>&lt;/td>
&lt;td>Chrome 启动窗口大小（&lt;code>--window-size&lt;/code>）&lt;/td>
&lt;td>取 &lt;code>RESOLUTION&lt;/code> 的宽高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>VNC_CLIP&lt;/code>&lt;/td>
&lt;td>VNC 实时流的画面裁剪区域&lt;/td>
&lt;td>与窗口大小一致&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>而 &lt;code>Sandbox.create&lt;/code> 的 &lt;code>envs&lt;/code> &lt;strong>只注入沙箱内的命令执行进程，不会影响浏览器栈&lt;/strong>。所以想改尺寸，得先用 Dockerfile 把环境变量烤进自定义镜像：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">RESOLUTION&lt;/span>&lt;span class="o">=&lt;/span>1920x1080x24
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">ENV&lt;/span> &lt;span class="nv">BROWSER_WINDOW_SIZE&lt;/span>&lt;span class="o">=&lt;/span>1920x1080
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">ENV&lt;/span> &lt;span class="nv">VNC_CLIP&lt;/span>&lt;span class="o">=&lt;/span>1920x1080
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>推送到镜像仓库后，再用 &lt;code>from_image&lt;/code> 构建模板。注意&lt;strong>窗口尺寸同时决定 VNC 实时流的画面范围&lt;/strong>，但页面内视口仍由 Playwright / Puppeteer 通过 CDP 自己设。&lt;/p>
&lt;p>&lt;strong>④ 探测 WebSocket 时会「假失败」。&lt;/strong> 在沙箱内用 curl 测 CDP 握手：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -sS -m &lt;span class="m">4&lt;/span> -i &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -H &lt;span class="s1">&amp;#39;Connection: Upgrade&amp;#39;&lt;/span> -H &lt;span class="s1">&amp;#39;Upgrade: websocket&amp;#39;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -H &lt;span class="s1">&amp;#39;Sec-WebSocket-Version: 13&amp;#39;&lt;/span> -H &lt;span class="s1">&amp;#39;Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==&amp;#39;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> http://localhost:3000/ws/automation
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>收到 &lt;code>101 Switching Protocols&lt;/code> 之后，服务端会继续发 WebSocket 数据帧，&lt;strong>curl 会一直等到 &lt;code>-m 4&lt;/code> 超时并以退出码 28 结束&lt;/strong>。文档专门解释了这不代表握手失败——&lt;strong>判断依据是响应里有没有 &lt;code>101&lt;/code>，不是 curl 的退出码。&lt;/strong>&lt;/p>
&lt;h3 id="all-in-one浏览器--代码执行同一沙箱">All-In-One：浏览器 + 代码执行，同一沙箱
&lt;/h3>&lt;p>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/all-in-one-template" target="_blank" rel="noopener"
>All-In-One&lt;/a> 就是在 browser 的基础上叠加 Code Interpreter 服务。差异很简洁：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>对比项&lt;/th>
&lt;th>browser&lt;/th>
&lt;th>All-In-One&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>核心定位&lt;/td>
&lt;td>轻量浏览器自动化环境&lt;/td>
&lt;td>浏览器自动化 + 代码执行一体化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Code Interpreter&lt;/td>
&lt;td>不支持&lt;/td>
&lt;td>支持 Python / JavaScript，含上下文保持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>端口&lt;/td>
&lt;td>3000&lt;/td>
&lt;td>3000（浏览器）+ 5000（代码与文件）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>默认镜像&lt;/td>
&lt;td>&lt;code>runtime/browser&lt;/code>&lt;/td>
&lt;td>&lt;code>runtime/all-in-one&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>默认镜像示例是新加坡地域：&lt;code>fc-e2b-registry.ap-southeast-1.cr.aliyuncs.com/runtime/all-in-one:v0.0.44&lt;/code>。&lt;strong>注意跨地域镜像构建会失败&lt;/strong>——镜像地址里的地域必须换成云沙箱接入地域。&lt;/p>
&lt;p>浏览器部分（建沙箱、等 &lt;code>/health&lt;/code>、CDP、截图）和 browser 模板完全一致，只是多了一段：&lt;strong>浏览器阶段产生的截图、HTML、下载文件，可以直接在同一沙箱里交给 Code Interpreter 处理。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 承接 browser 流程中的同一个 sbx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">execution&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sbx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_code&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;import json&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;print(json.dumps({&amp;#39;status&amp;#39;: &amp;#39;ok&amp;#39;, &amp;#39;source&amp;#39;: &amp;#39;all-in-one&amp;#39;}, ensure_ascii=False))&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">execution&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">logs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>用 Code Interpreter 时，SDK 要换回 &lt;code>e2b_code_interpreter&lt;/code> / &lt;code>@e2b/code-interpreter&lt;/code>，沙箱由你自己的 All-In-One 模板创建。&lt;/p>
&lt;p>&lt;strong>选型标准文档给得很清楚，我直接抄&lt;/strong>（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/use-browser-use-sandbox" target="_blank" rel="noopener"
>使用 Browser Use Sandbox&lt;/a> / &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-aio-sandbox" target="_blank" rel="noopener"
>使用 AIO Sandbox&lt;/a>）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>任务形态&lt;/th>
&lt;th>选择&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>只访问网页、点击、截图、下载、轻量提取&lt;/td>
&lt;td>&lt;strong>Browser Use Sandbox&lt;/strong>（browser 模板）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>浏览器产物还要在同一会话里清洗、分析、生成报告&lt;/td>
&lt;td>&lt;strong>AIO Sandbox&lt;/strong>（All-In-One 模板）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>只有代码执行&lt;/td>
&lt;td>&lt;strong>Code Interpreter Sandbox&lt;/strong>（code-interpreter-v1）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>只要基础命令和文件&lt;/td>
&lt;td>&lt;strong>base&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>AIO 那两页的「推荐流程」都是 8 步，结构很像，我合并一下：&lt;strong>构建业务模板 → 建沙箱并设足够超时 → &lt;code>get_host(3000)&lt;/code> 拿 host → 轮询 &lt;code>/health&lt;/code> → 连 CDP 执行操作 → 产物写入沙箱文件系统 → 交给 Code Interpreter 或命令继续处理 → 下载结果、销毁沙箱&lt;/strong>。&lt;/p>
&lt;p>中间有个细节值得抄：&lt;strong>用固定的任务目录&lt;/strong>，例如 &lt;code>/tmp/aio-task/&amp;lt;task-id&amp;gt;&lt;/code>，把截图、HTML、下载文件、脚本和结果文件放同一目录。另外官方明确提了**「浏览器阶段和代码阶段分别记录输入、输出、日志和错误」**——排查时先确认是页面操作失败，还是后续脚本处理失败，否则这两种错在同一个沙箱里长得一模一样。&lt;/p>
&lt;h3 id="browseruse-这类框架怎么接">BrowserUse 这类框架怎么接
&lt;/h3>&lt;p>这块是很多 Agent 框架落地的关键：&lt;strong>让 BrowserUse 连接沙箱暴露的 CDP 地址。&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">browser_session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BrowserSession&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cdp_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;wss://&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/ws/automation&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser_profile&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">BrowserProfile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">headless&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">keep_alive&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;X-Access-Token&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_envd_access_token&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Agent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;访问 https://example.com，提取页面标题并总结首屏正文&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browser_session&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">browser_session&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_vision&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>职责划分很清楚：&lt;strong>业务服务负责创建和销毁沙箱，BrowserUse 只连接这个沙箱中的浏览器会话。&lt;/strong> 用 Puppeteer Core 也一样，&lt;code>browserWSEndpoint&lt;/code> 填 &lt;code>wss://&amp;lt;host&amp;gt;/ws/automation&lt;/code>，headers 带 token。&lt;/p>
&lt;p>上线建议里有两条我特别认同，顺便呼应了前面沙箱选型那篇的老话题：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>页面内容可能包含 prompt injection。不要让 Agent 未经校验地执行网页中的指令。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>登录态、Cookie、账号凭证和业务 Token 应按任务隔离，通过运行时注入，不写进模板。&lt;/strong>&lt;/li>
&lt;li>每个浏览器任务限制在明确的 URL 范围内，必要时加域名白名单；限制下载文件类型、单文件大小、总输出大小和任务生命周期。&lt;/li>
&lt;/ul>
&lt;h3 id="自定义模板从自己的镜像构建">自定义模板：从自己的镜像构建
&lt;/h3>&lt;p>内置模板满足不了时（业务依赖、系统库、运行时版本、企业标准化），才做自定义镜像模板。&lt;strong>生产模板还有几条纪律&lt;/strong>（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/build-a-custom-image-template" target="_blank" rel="noopener"
>构建和管理模板&lt;/a>）：&lt;/p>
&lt;ul>
&lt;li>模板名称应唯一、可读，&lt;strong>便于灰度和回滚&lt;/strong>。&lt;/li>
&lt;li>基础镜像来自云沙箱可访问的镜像仓库。&lt;/li>
&lt;li>&lt;strong>镜像仓库、网络配置和云沙箱要在同一地域。&lt;/strong>&lt;/li>
&lt;li>构建依赖不宜过大，否则可能构建超时或失败。&lt;/li>
&lt;li>&lt;strong>生产环境不应覆盖正在使用的模板&lt;/strong>，建议新建模板、验证后再切换。&lt;/li>
&lt;/ul>
&lt;p>构建流程本身很短——定义模板、提交构建、拿 &lt;code>template_id&lt;/code> 建沙箱（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/templates" target="_blank" rel="noopener"
>快速开始&lt;/a>）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">build&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Template&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Template&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_image&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;FROM_IMAGE&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;template-&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpu_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_mb&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2048&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">on_build_logs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">default_build_logger&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">build&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">template_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">900&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你要用自己的 &lt;strong>ACR EE&lt;/strong> 镜像，前置条件比较硬：&lt;/p>
&lt;ul>
&lt;li>ACR EE 实例要和云沙箱在&lt;strong>同一 UID、同一地域&lt;/strong>（经济版不支持）。&lt;/li>
&lt;li>ACR EE 实例要&lt;strong>至少绑定一个 VPC&lt;/strong>，且该 VPC 下至少有一个 vSwitch 在函数计算支持的可用区。&lt;/li>
&lt;li>镜像仓库、VPC、vSwitch 与云沙箱同地域，且访问控制已放通。&lt;/li>
&lt;li>推送时用 VPC 内网地址，例如 &lt;code>test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1&lt;/code>。&lt;/li>
&lt;li>&lt;strong>不要给不同内容的镜像推同一个 tag。&lt;/strong> 每个不同镜像都要有新且唯一的 tag（版本号、日期或 commit ID），模板引用那个具体 tag。&lt;/li>
&lt;/ul>
&lt;p>镜像本身也有一张要求表，违反任何一条都会让构建或运行失败：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>级别&lt;/th>
&lt;th>要求&lt;/th>
&lt;th>不满足的后果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>必须&lt;/td>
&lt;td>架构为 &lt;code>linux/amd64&lt;/code>（多架构镜像的 manifest list 要包含 amd64）&lt;/td>
&lt;td>模板转换立即失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>必须&lt;/td>
&lt;td>不要开启镜像加速&lt;/td>
&lt;td>模板转换立即失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>必须&lt;/td>
&lt;td>&lt;code>/etc/passwd&lt;/code>、&lt;code>/etc/group&lt;/code> 是标准文件且可写&lt;/td>
&lt;td>gatewayd 无法初始化默认用户，容器启动失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>条件&lt;/td>
&lt;td>固定路径 &lt;code>/bin/bash&lt;/code> 存在（不能只是能从 PATH 解析到）&lt;/td>
&lt;td>&lt;code>commands.run&lt;/code> 和 PTY 失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>条件&lt;/td>
&lt;td>PATH 中有 &lt;code>python3&lt;/code> 或 &lt;code>python&lt;/code>&lt;/td>
&lt;td>Python &lt;code>run_code&lt;/code> 不可用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>条件&lt;/td>
&lt;td>PATH 中有 &lt;code>node&lt;/code>&lt;/td>
&lt;td>JavaScript &lt;code>run_code&lt;/code> 不可用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>按需&lt;/td>
&lt;td>&lt;code>git&lt;/code>、&lt;code>openssh-client&lt;/code>、CA 证书、构建工具链&lt;/td>
&lt;td>对应的 Git / SSH / HTTPS / 源码构建操作不可用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>建议&lt;/td>
&lt;td>系统 PATH 至少包含 &lt;code>/usr/local/bin&lt;/code>、&lt;code>/usr/bin&lt;/code>、&lt;code>/bin&lt;/code>&lt;/td>
&lt;td>登录 shell 里可能缺常用命令&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>/bin/bash&lt;/code> 那一条我建议特别留意：它说的是&lt;strong>固定路径存在&lt;/strong>，不是 PATH 里能找到。很多精简基础镜像（busybox / distroless 派生）会把 bash 放在别处或者干脆没有。还有「不要开镜像加速」这条也挺反直觉——那是个平时看起来纯占便宜的开关。&lt;/p>
&lt;p>&lt;strong>模板的定位也说得很清楚&lt;/strong>：模板固化的是系统依赖、语言运行时、工具链和基础代码；&lt;strong>用户数据、临时文件和频繁变化的业务状态要在运行时写入&lt;/strong>；&lt;strong>密钥、Token 这类敏感凭证不要写进模板，创建沙箱时用环境变量注入&lt;/strong>。最后这句我在前面提过一次，这里再强调一遍，因为它是最容易被「顺手固化一下」的操作。&lt;/p>
&lt;h4 id="一次真的跑通从跨地域失败到新加坡模板可用">一次真的跑通：从跨地域失败到新加坡模板可用
&lt;/h4>&lt;p>上面的要求表看起来像文档摘录，直到我真的推了一次镜像，才发现它更适合当成一条&lt;strong>排错顺序&lt;/strong>。这次镜像里要固化的是 Node.js、openpyxl、pandas、NumPy 和 SciPy；最终跑通的路径不是「把 Dockerfile 写对」这么简单，而是连续解决了地域、账号、API Key、镜像 manifest 和登录 Shell 五层问题。&lt;/p>
&lt;p>先给结论：&lt;strong>自定义模板构建至少有五个必须同时对齐的坐标：Sandbox 地域、E2B Endpoint、E2B API Key 的归属地域、镜像仓库地域、镜像仓库所属账号。&lt;/strong> Dockerfile 只是第六个变量。&lt;/p>
&lt;h5 id="第一步永远用官方镜像建立基线">第一步永远用官方镜像建立基线
&lt;/h5>&lt;p>不要一上来就拿私有镜像测试。先用同地域官方镜像构建模板并创建 Sandbox：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-env" data-lang="env">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">E2B_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">E2B_API_URL&lt;/span>&lt;span class="o">=&lt;/span>https://api.&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">E2B_DOMAIN&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OFFICIAL_IMAGE&lt;/span>&lt;span class="o">=&lt;/span>fc-e2b-registry.&amp;lt;region&amp;gt;.cr.aliyuncs.com/runtime/code-interpreter-v1:&amp;lt;published-tag&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这一步成功，才能证明 Team、API Key、Endpoint、Domain 和模板服务链路都正常。否则后面看到 &lt;code>401&lt;/code>、模板不可见或构建失败时，很容易把凭证问题误判成镜像问题。&lt;/p>
&lt;p>API Key 是地域级凭据。最安全的只读探测是查询 Sandbox 列表，而不是创建 Sandbox：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">httpx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">httpx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;https://api.ap-southeast-1.e2b.fc.aliyuncs.com/sandboxes&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;X-API-KEY&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">api_key&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">status_code&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们这次就碰到过一种很有迷惑性的状态：Endpoint 和 Domain 已经换成新加坡，但 Key 仍然属于北京。结果是新加坡返回 &lt;code>401&lt;/code>，北京返回 &lt;code>200&lt;/code>。&lt;strong>Endpoint 改了，不代表 Key 跟着迁移；目标地域必须重新创建 Team/API Key。&lt;/strong>&lt;/p>
&lt;h5 id="地域不一致不是慢一点而是构建器根本不存在">地域不一致不是“慢一点”，而是构建器根本不存在
&lt;/h5>&lt;p>第一次自定义镜像放在河源 ACR，Sandbox 在北京。构建请求已经被接受，但注入 envd 时失败：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">envd inject failed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lookup &amp;lt;account&amp;gt;.cn-heyuan.fc.aliyuncs.com: no such host
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这不是 DNS 偶发故障。河源不是 FC 云沙箱支持地域，平台根据镜像仓库地域准备 Builder，最终指向了不存在的河源 FC 服务。后来把镜像和 Sandbox 一起迁到新加坡，这一层才消失。&lt;/p>
&lt;p>所以地域检查不要只看 Endpoint。完整矩阵应该是：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>对象&lt;/th>
&lt;th>必须检查的值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>E2B API URL&lt;/td>
&lt;td>&lt;code>api.&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>E2B Domain&lt;/td>
&lt;td>&lt;code>&amp;lt;region&amp;gt;.e2b.fc.aliyuncs.com&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>API Key&lt;/td>
&lt;td>在目标地域 Team 下创建&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>源镜像仓库&lt;/td>
&lt;td>与 Sandbox 同地域&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Template / Sandbox&lt;/td>
&lt;td>与前三者同地域&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VPC / vSwitch / 安全组（ACR EE）&lt;/td>
&lt;td>与仓库和 Sandbox 同地域&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="账号一致可以从错误信息里反推">“账号一致”可以从错误信息里反推
&lt;/h5>&lt;p>镜像迁到新加坡后，第一次仍然失败：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">get personal ACR authorization token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AUTHENTICATION_FAILED
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">user jurisdiction error
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当时地域、镜像地址、仓库用户名密码都没错。真正的问题是 &lt;strong>E2B API Key 所属账号与 ACR 仓库所属账号不一致&lt;/strong>。换成镜像仓库所在账号、同一新加坡 Team 下创建的 Key 后，构建日志依次变成：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">template build accepted
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">starting image conversion
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">registry resolved
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">builder prepared
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">image conversion completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">template function created
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这条日志序列很有用：&lt;code>registry resolved&lt;/code> 之前失败，先查仓库类型、账号归属和凭证；&lt;code>builder prepared&lt;/code> 之前失败，先查地域和 FC Builder；镜像转换完成但 Sandbox 起不来，再查镜像内部结构与运行依赖。&lt;/p>
&lt;p>官方文档把受支持的私有 ACR 主路径写成 &lt;strong>ACR 企业版、同 UID、同地域、绑定 VPC&lt;/strong>。这次实测中，同账号同地域的 ACR 个人版也通过 E2B &lt;code>Template.build()&lt;/code> 跑通了镜像转换和 Sandbox 创建，但这只能记作&lt;strong>当前实测行为，不应提升为生产承诺&lt;/strong>。生产仍然应按官方支持路径使用 ACR EE；否则平台调整授权或构建策略时，没有稳定性保证。&lt;/p>
&lt;h5 id="镜像要同时满足构建器和登录-shell">镜像要同时满足构建器和登录 Shell
&lt;/h5>&lt;p>最终镜像采用 Node 22 slim 作为基础，再安装 Python 3 虚拟环境和数据依赖。这样比在 Python 镜像里通过 Debian 安装 &lt;code>npm&lt;/code> 少拉数百个系统包。核心结构如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> node:22-bookworm-slim&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y --no-install-recommends &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> bash ca-certificates curl git openssh-client &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> python3 python3-pip python3-venv &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> rm -rf /var/lib/apt/lists/*&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> python3 -m venv /opt/venv&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/opt/venv/bin:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PATH&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> requirements-image.txt /tmp/requirements-image.txt&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> python -m pip install --no-cache-dir -r /tmp/requirements-image.txt&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里又踩了一个只有运行时才出现的坑：Docker 构建阶段用 &lt;code>python&lt;/code> 导入依赖正常，但 Sandbox 的 &lt;code>bash -lc&lt;/code> 会重置 PATH，&lt;code>python3&lt;/code> 最后指向系统 Python，报 &lt;code>ModuleNotFoundError&lt;/code>。&lt;/p>
&lt;p>单纯把 &lt;code>/usr/local/bin/python3&lt;/code> 软链接到虚拟环境也不够。Python 会根据启动路径判断虚拟环境，跨目录软链接可能让它重新落回系统环境。最后用一个明确的包装器解决：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">RUN&lt;/span> &lt;span class="nb">printf&lt;/span> &lt;span class="s1">&amp;#39;%s\n&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;#!/bin/sh&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;exec /opt/venv/bin/python3 &amp;#34;$@&amp;#34;&amp;#39;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &amp;gt; /usr/local/bin/python3 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> chmod +x /usr/local/bin/python3&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Docker build 中能 import，不代表 Sandbox 登录 Shell 中也能 import。&lt;/strong> 验收必须用和 Sandbox 接近的命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --rm --platform linux/amd64 &amp;lt;image&amp;gt; bash -lc &lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">python3 -c &amp;#34;import openpyxl, pandas, numpy, scipy&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">node --version
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">test -w /etc/passwd
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">test -w /etc/group
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="推送时关闭附加-manifest">推送时关闭附加 manifest
&lt;/h5>&lt;p>FC 模板排错文档明确建议使用单一 &lt;code>linux/amd64&lt;/code> 并关闭 provenance / SBOM。否则 Buildx 可能同时推送一个 &lt;code>unknown/unknown&lt;/code> 的 attestation manifest，模板构建器可能把它当成镜像平台异常。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker buildx build &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --platform linux/amd64 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --provenance&lt;span class="o">=&lt;/span>&lt;span class="nb">false&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --sbom&lt;span class="o">=&lt;/span>&lt;span class="nb">false&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tag &lt;span class="s2">&amp;#34;&amp;lt;registry&amp;gt;/&amp;lt;namespace&amp;gt;/&amp;lt;image&amp;gt;:&amp;lt;unique-tag&amp;gt;&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --push &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里的 &lt;code>&amp;lt;unique-tag&amp;gt;&lt;/code> 是纪律，不是装饰。不要把不同内容覆盖到已经被模板引用的 tag；使用日期、版本号或 commit ID，让镜像、模板和一次验证结果能够互相追溯。&lt;/p>
&lt;h5 id="最后的验收必须发生在云端-sandbox">最后的验收必须发生在云端 Sandbox
&lt;/h5>&lt;p>本地镜像测试只证明容器能跑，不能证明 envd 注入、模板函数和 Sandbox 生命周期正常。最终验收应当创建一次临时 Sandbox，实际导入依赖，然后在 &lt;code>finally&lt;/code> 中释放：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">template_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;python3 -c &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;import openpyxl, pandas, numpy, scipy; &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;print(openpyxl.__version__, pandas.__version__, &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;numpy.__version__, scipy.__version__)&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2"> &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;amp;&amp;amp; node --version&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exit_code&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">RuntimeError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stderr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">finally&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">kill&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这次云端最终验证通过的组合是 Python 3.11、Node.js 22、openpyxl 3.1.5、pandas 2.3.2、NumPy 2.3.3、SciPy 1.16.2。版本号不是推荐清单，只是一次可复现构建的证据；真正值得复用的是验收顺序：&lt;/p>
&lt;ol>
&lt;li>官方镜像验证控制链路；&lt;/li>
&lt;li>只读探测 API Key 地域；&lt;/li>
&lt;li>单平台、唯一 tag 推送自定义镜像；&lt;/li>
&lt;li>观察构建日志停在哪一阶段；&lt;/li>
&lt;li>在云端 Sandbox 里导入全部关键依赖；&lt;/li>
&lt;li>无论成功失败都释放临时 Sandbox。&lt;/li>
&lt;/ol>
&lt;p>这轮实测最后留下的判断是：&lt;strong>自定义模板最难排查的不是 Dockerfile，而是“地域 × 账号 × 凭据 × 仓库类型”共同决定的构建控制面。&lt;/strong> 先把这四个坐标钉死，再谈镜像内部依赖，排错会快很多。&lt;/p>
&lt;h3 id="模板的版本管理名称--标签">模板的版本管理：名称 + 标签
&lt;/h3>&lt;p>发布纪律这块，官方给的做法是&lt;strong>名称标识运行环境，标签标记某次构建的阶段或版本&lt;/strong>（&lt;code>prod&lt;/code>、&lt;code>staging&lt;/code>、&lt;code>v1&lt;/code>），用来做灰度和回滚（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/template-name" target="_blank" rel="noopener"
>模板名称与版本&lt;/a>）。&lt;/p>
&lt;p>名称建议带上业务场景、语言或基础镜像、关键依赖版本和日期：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">agent-python313-20260704
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">code-review-node22-20260704
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data-analysis-py313-v1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>标签操作有三个：&lt;code>assignTags&lt;/code> 分配、&lt;code>getTags&lt;/code> 查询、&lt;code>removeTags&lt;/code> 移除。有两个坑值得记：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>不能删除 &lt;code>default&lt;/code> 标签&lt;/strong>（返回 400）。&lt;/li>
&lt;li>&lt;strong>Python 查询标签要用模板 ID&lt;/strong>，不是模板名；TypeScript 用模板名就行。分配和移除倒是都按模板名。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Template&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assign_tags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent-python313-20260704&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;staging&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v1&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Template&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_tags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;template-id&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 注意这里是 ID&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>分配和移除标签都是幂等的，重复分配、删不存在的标签都不会报错。&lt;code>assignTags&lt;/code> 第一个参数用 &lt;code>name:tag&lt;/code> 格式指定从哪个构建打标签，不带 &lt;code>:tag&lt;/code> 时默认指向 &lt;code>default&lt;/code> 构建。&lt;/p>
&lt;h3 id="沙箱内的环境变量">沙箱内的环境变量
&lt;/h3>&lt;p>环境变量分两层，用途不同（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/environment-variable" target="_blank" rel="noopener"
>环境变量&lt;/a>）：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>沙箱级 &lt;code>envs&lt;/code>&lt;/strong>：创建时传入，适合多次命令都会用到的配置。&lt;/li>
&lt;li>&lt;strong>命令级 &lt;code>envs&lt;/code>&lt;/strong>：&lt;code>commands.run()&lt;/code> 时传入，适合单次执行参数，会覆盖沙箱级。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">envs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;NODE_ENV&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;TASK_ID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;task-001&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;echo $TASK_ID&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">envs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TASK_ID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;task-002&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>官方建议是&lt;strong>环境变量只放非敏感开关、任务参数和工具配置&lt;/strong>，密钥和 Token 由业务侧控制访问范围。以及一条和 metadata 呼应的边界：&lt;strong>不要依赖环境变量保存业务状态&lt;/strong>，跨沙箱的持久状态写外部存储。&lt;/p>
&lt;h2 id="七fc-extensions云沙箱不是-e2b-的镜像">七、FC Extensions：云沙箱不是 E2B 的镜像
&lt;/h2>&lt;p>如果只是纯「E2B 兼容，换个 endpoint」，那云沙箱的价值就只是「国内有个能连上的 E2B」。但它其实还多了一层 E2B 原生接口里没有的东西：&lt;strong>FC Extensions（云上扩展）&lt;/strong>（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/fc-extensions-overview" target="_blank" rel="noopener"
>概览&lt;/a>）。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>扩展&lt;/th>
&lt;th>作用&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/vpc-network-configuration-1" target="_blank" rel="noopener"
>VPC 网络配置&lt;/a>&lt;/td>
&lt;td>让 Sandbox 访问 VPC 内的数据库、内网 API、镜像仓库或其他云资源&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-domain-name" target="_blank" rel="noopener"
>自定义域名&lt;/a>&lt;/td>
&lt;td>用固定域名和自有证书访问云沙箱 API 及沙箱内服务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/mount-oss-dynamically-1" target="_blank" rel="noopener"
>动态挂载 OSS&lt;/a>&lt;/td>
&lt;td>通过 metadata 把 OSS 路径挂进 Sandbox，按本地路径读写对象&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/monitoring-and-logging" target="_blank" rel="noopener"
>监控与日志&lt;/a>&lt;/td>
&lt;td>查看运行状态与资源指标，把 stdout / stderr 采集到日志服务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/quota-management" target="_blank" rel="noopener"
>Team 配额管理&lt;/a>&lt;/td>
&lt;td>给指定 Team 设置 CPU 和内存配额&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-oss-volume" target="_blank" rel="noopener"
>OSS Volume&lt;/a> / &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-an-agenticfs-volume" target="_blank" rel="noopener"
>AgenticFS Volume&lt;/a>&lt;/td>
&lt;td>保存可复用的挂载配置，创建沙箱时按名称挂载&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>它的接入方式有一个很重要的性质：&lt;strong>FC Extensions 不是 E2B SDK 的新方法，不需要改导入方式。&lt;/strong> 应用侧还是照常 &lt;code>Sandbox.create()&lt;/code>，VPC、OSS、日志、监控这些是在函数计算控制台或云沙箱控制面配好、然后生效的。&lt;/p>
&lt;p>而且它的实际机制比「控制台点一下」更有意思：&lt;strong>VPC 和 OSS 挂载都是通过 Sandbox 的保留 metadata 字段传进去的。&lt;/strong>&lt;/p>
&lt;h3 id="vpc-配置一个保留-metadata-字段">VPC 配置：一个保留 metadata 字段
&lt;/h3>&lt;p>云沙箱通过 &lt;code>fc.sandbox.network.vpc&lt;/code> 传 VPC 配置——把 &lt;code>vpcId&lt;/code>、&lt;code>securityGroupId&lt;/code>、&lt;code>vSwitchIds&lt;/code> 序列化成 &lt;strong>JSON 字符串&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;vpcId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;vpc-xxxxxxxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;securityGroupId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sg-xxxxxxxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;vSwitchIds&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;vsw-xxxxxxxx&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">**&lt;/span>&lt;span class="n">conn_opts&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fc.sandbox.network.vpc&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">vpc_config&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字段&lt;/th>
&lt;th>必填&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>vpcId&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>要接入的专有网络 ID&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>securityGroupId&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>安全组 ID，用于控制&lt;strong>出方向&lt;/strong>访问范围&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>vSwitchIds&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>vSwitch ID 列表，&lt;strong>建议配两个或更多&lt;/strong>，提高可用性并降低单网段 IP 不足的风险&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>前置条件比想象中多，漏一个就是「创建成功但连不上」：&lt;/p>
&lt;ul>
&lt;li>vSwitch 要在函数计算支持的可用区（同 VPC 内不同 vSwitch 默认可私网互通）。&lt;/li>
&lt;li>安全组要是&lt;strong>非云服务托管&lt;/strong>的，且&lt;strong>出方向规则&lt;/strong>允许访问目标资源的协议和端口。&lt;/li>
&lt;li>目标资源自己有白名单的（RDS 白名单、自建服务 ACL），要把 &lt;strong>vSwitch 网段&lt;/strong>加进去。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>「网络可达不等于业务鉴权通过」&lt;/strong>——数据库账号、API Token、RAM Role 这些都还得单独配。这句话文档写得很明白，我觉得是这一节最有价值的一句。&lt;/p>
&lt;p>&lt;strong>验证方式&lt;/strong>也很实在，而且给了一个我觉得很聪明的判别实验：&lt;/p>
&lt;ol>
&lt;li>用 &lt;code>socket.create_connection((host, port), timeout=5)&lt;/code> 在沙箱内测 TCP 连通性，可以拿去测 NAS 2049、RDS 3306、Redis 6379 或内网 HTTP 端口。&lt;/li>
&lt;li>&lt;strong>先建一个不带 &lt;code>fc.sandbox.network.vpc&lt;/code> 的 Sandbox，确认同一个内网地址不可达；再建一个带 VPC metadata 的，确认可达。&lt;/strong> 这样才能证明是 VPC 配置生效，而不是网络本来就通。&lt;/li>
&lt;/ol>
&lt;p>常见问题那张表也能当排错清单用：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>现象&lt;/th>
&lt;th>可能原因&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>创建 Sandbox 失败&lt;/td>
&lt;td>资源 ID 不存在，或与沙箱不在同一地域 / 账号&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>创建成功但内网地址不可达&lt;/td>
&lt;td>安全组出方向未放行、目标资源白名单未含 vSwitch 网段、目标服务未监听&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>提示 vSwitch 可用区不支持&lt;/td>
&lt;td>vSwitch 不在函数计算当前地域支持的可用区内&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>偶发创建 / 连接失败&lt;/td>
&lt;td>vSwitch 网段可用 IP 不足，或只配了单个可用区&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要访问公网和 VPC&lt;/td>
&lt;td>&lt;strong>只配 VPC 不代表固定公网出口&lt;/strong>，公网能力以云沙箱和函数计算网络策略为准&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>还有个表述细节：&lt;strong>&lt;code>fc.sandbox.network.vpc&lt;/code> 的值必须是 JSON 字符串，不能直接传 Python dict 或 JavaScript object。&lt;/strong>&lt;/p>
&lt;h3 id="oss-挂载两个-metadata-字段">OSS 挂载：两个 metadata 字段
&lt;/h3>&lt;p>OSS 动态挂载要传两个字段（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/mount-oss-dynamically-1" target="_blank" rel="noopener"
>动态挂载 OSS&lt;/a>）：&lt;/p>
&lt;ul>
&lt;li>&lt;code>fc.sandbox.storage.oss&lt;/code>：挂载配置，JSON 字符串。&lt;/li>
&lt;li>&lt;code>fc.sandbox.auth.role&lt;/code>：&lt;strong>用于访问 OSS 的 RAM Role ARN&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sandbox&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Sandbox&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">300&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">**&lt;/span>&lt;span class="n">conn_opts&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fc.sandbox.storage.oss&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">oss_config&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fc.sandbox.auth.role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">role_arn&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>挂载点配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mountPoints&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucketName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;example-bucket&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mountDir&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/mnt/oss&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bucketPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/e2b-test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;endpoint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://oss-cn-hangzhou.aliyuncs.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;readOnly&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字段&lt;/th>
&lt;th>必填&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>bucketName&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>OSS Bucket 名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mountDir&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>Sandbox 内挂载目录，必须是绝对路径&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>endpoint&lt;/code>&lt;/td>
&lt;td>是&lt;/td>
&lt;td>OSS Endpoint，&lt;strong>应与 Bucket 所在地域匹配&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>bucketPath&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>Bucket 内子目录，建议绝对路径；&lt;code>/&lt;/code> 或留空表示根目录&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>readOnly&lt;/code>&lt;/td>
&lt;td>否&lt;/td>
&lt;td>&lt;code>true&lt;/code> 时只能读挂载目录&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>几个容易踩的点：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>挂载必须同时配 &lt;code>fc.sandbox.auth.role&lt;/code>，否则沙箱拿不到 OSS 权限。&lt;/strong> 两者缺一不可。&lt;/li>
&lt;li>RAM Role 要授予函数计算服务&lt;strong>可扮演权限&lt;/strong>，并具备访问目标 Bucket 或子目录的 OSS 权限。&lt;/li>
&lt;li>&lt;code>mountDir&lt;/code> 推荐 &lt;code>/mnt/oss&lt;/code> 或 &lt;code>/home/user/oss&lt;/code>，&lt;strong>避免与模板内已有系统目录冲突&lt;/strong>。&lt;/li>
&lt;li>跨地域 &lt;code>endpoint&lt;/code> 会导致延迟升高或访问失败。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>权限建议是这一页最值得抄的部分&lt;/strong>，因为它正好把之前沙箱选型那篇讲的「最小权限」落到了具体 policy 上：&lt;/p>
&lt;ul>
&lt;li>用 &lt;code>readOnly: true&lt;/code> 挂输入数据目录，避免任务误写或删除源数据。&lt;/li>
&lt;li>写入结果用独立前缀，例如 &lt;code>tenants/&amp;lt;tenant-id&amp;gt;/tasks/&amp;lt;task-id&amp;gt;/outputs/&lt;/code>。&lt;/li>
&lt;li>RAM Policy 只授权任务需要的对象前缀：只读任务给 &lt;code>oss:ListObjects&lt;/code> + &lt;code>oss:GetObject&lt;/code>；读写任务再按需加 &lt;code>oss:PutObject&lt;/code>、&lt;code>oss:DeleteObject&lt;/code>、&lt;code>oss:AbortMultipartUpload&lt;/code>、&lt;code>oss:ListParts&lt;/code>。&lt;/li>
&lt;li>&lt;strong>不要在代码、模板或 metadata 中写长期 AK/SK&lt;/strong>，访问 OSS 走 &lt;code>fc.sandbox.auth.role&lt;/code>。&lt;/li>
&lt;li>临时产物配 OSS 生命周期清理规则。&lt;/li>
&lt;/ul>
&lt;h3 id="自定义域名证书要求比想象中严格">自定义域名：证书要求比想象中严格
&lt;/h3>&lt;p>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-domain-name" target="_blank" rel="noopener"
>自定义域名&lt;/a>解决的是「生产环境要固定域名 + 自有证书」。它把域名分成两条链路：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>用途&lt;/th>
&lt;th>示例&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>控制链路域名&lt;/td>
&lt;td>&lt;code>api.example.com&lt;/code>&lt;/td>
&lt;td>创建、查询、删除云沙箱等 API 请求&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>数据链路域名&lt;/td>
&lt;td>&lt;code>*.example.com&lt;/code>&lt;/td>
&lt;td>访问沙箱内指定端口的服务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>对应到 SDK 参数就是 &lt;code>apiUrl&lt;/code> = &lt;code>https://api.example.com&lt;/code>、&lt;code>domain&lt;/code> = &lt;code>example.com&lt;/code>。之后 &lt;code>sandbox.getHost(8000)&lt;/code> 返回的就变成 &lt;code>8000-&amp;lt;sandbox-id&amp;gt;.example.com&lt;/code>，&lt;code>https://{port}-{sandboxId}.example.com&lt;/code> 这种形式。&lt;/p>
&lt;p>限制条件我列一下，因为这几条挺容易在配置阶段反复：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>必须选云沙箱所在地域&lt;/strong>，否则域名解析和证书校验可能不生效。&lt;/li>
&lt;li>&lt;strong>控制链路只支持 &lt;code>api.&lt;/code> 开头的单域名&lt;/strong>；数据链路是控制链路去掉 &lt;code>api.&lt;/code> 前缀后的泛域名。&lt;/li>
&lt;li>&lt;strong>必须 HTTPS。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>证书必须覆盖数据链路泛域名&lt;/strong>（例如 &lt;code>*.example.com&lt;/code>）——只覆盖 &lt;code>api.example.com&lt;/code> 的单域名证书&lt;strong>不满足要求&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>私钥必须是未加密的 RSA PEM 格式。&lt;/strong> 如果你的私钥是 PKCS#8 的 &lt;code>-----BEGIN PRIVATE KEY-----&lt;/code>，要先转换：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openssl rsa -in pkcs8.key -out pkcs1.key
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>不支持中文域名。&lt;/strong>&lt;/li>
&lt;li>同一主账号默认最多绑 &lt;strong>5 个&lt;/strong>云沙箱自定义域名。&lt;/li>
&lt;li>自定义域名需要完成备案或接入备案，以控制台校验结果为准。&lt;/li>
&lt;/ul>
&lt;p>配置要动两个控制台：云沙箱控制台加域名拿 CNAME，再去云解析 DNS 配解析（控制链路主机记录 &lt;code>api&lt;/code>，数据链路主机记录 &lt;code>*&lt;/code>）。配完用 &lt;code>dig +short CNAME api.example.com&lt;/code> 和 &lt;code>dig +short CNAME test.example.com&lt;/code> 验证——&lt;strong>第二条是专门用来验证泛域名解析的&lt;/strong>，这个细节挺贴心。&lt;/p>
&lt;p>还有一个实用提醒：&lt;strong>&lt;code>sandbox.getHost(port)&lt;/code> 返回的是 host，访问时通常要自己拼 &lt;code>https://&lt;/code>。&lt;/strong>&lt;/p>
&lt;h3 id="team-配额管理注意鉴权通道不一样">Team 配额管理：注意鉴权通道不一样
&lt;/h3>&lt;p>这块的&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/quota-management" target="_blank" rel="noopener"
>文档&lt;/a>里有一条我认为最容易被忽略、也最容易配错的信息：&lt;/p>
&lt;blockquote>
&lt;p>Team 配额管理使用 &lt;strong>POP SDK 和阿里云 AK/SK，不使用云沙箱 API Key 鉴权&lt;/strong>。&lt;/p>
&lt;/blockquote>
&lt;p>也就是说，你的系统在这里会同时持有两套凭证：沙箱的 API Key 和阿里云的 AK/SK。&lt;strong>这两套东西的权限模型、轮换策略、泄露影响面都不一样&lt;/strong>，值得在密钥管理上分开对待。文档也重申了「云沙箱 API Key 仍通过函数计算控制台创建和管理」「不要把 AK/SK、API Key 写入代码仓库、镜像、模板、日志、截图、工单或前端页面」。&lt;/p>
&lt;p>前置条件里有一条挺关键：&lt;strong>「已在阿里云控制台联系客服完成加白」&lt;/strong>——所以 Team 配额同样是白名单能力。&lt;/p>
&lt;p>配额模型很简单，以 Team ID 作为 &lt;code>TagValue&lt;/code>，两个配额项：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字段&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>约束&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>TagValue&lt;/code>&lt;/td>
&lt;td>&lt;code>*string&lt;/code>&lt;/td>
&lt;td>匹配 &lt;code>[A-Za-z0-9_-]{1,64}&lt;/code>&lt;/td>
&lt;td>填 Team ID&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CpuCores&lt;/code>&lt;/td>
&lt;td>&lt;code>*int32&lt;/code>&lt;/td>
&lt;td>≥ 0&lt;/td>
&lt;td>CPU 核数配额&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MemoryGB&lt;/code>&lt;/td>
&lt;td>&lt;code>*int32&lt;/code>&lt;/td>
&lt;td>≥ 0&lt;/td>
&lt;td>内存 GB 配额&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>设为 &lt;code>0&lt;/code> 表示禁止该 Team 使用对应资源&lt;/strong>（不是「无限制」，别理解反）。&lt;code>UpdateQuota&lt;/code> 是&lt;strong>覆盖式更新&lt;/strong>，同一 &lt;code>TagValue&lt;/code> 多次调用后一次覆盖前一次。&lt;/p>
&lt;p>四个接口和对应的 RAM Action：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>操作&lt;/th>
&lt;th>Go 方法&lt;/th>
&lt;th>RAM Action&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>创建或更新配额&lt;/td>
&lt;td>&lt;code>UpdateQuota&lt;/code>&lt;/td>
&lt;td>&lt;code>fcsandbox:UpdateQuota&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>查询配额&lt;/td>
&lt;td>&lt;code>DescribeQuota&lt;/code>&lt;/td>
&lt;td>&lt;code>fcsandbox:DescribeQuota&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>列出配额&lt;/td>
&lt;td>&lt;code>ListQuota&lt;/code>&lt;/td>
&lt;td>&lt;code>fcsandbox:ListQuota&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>删除配额&lt;/td>
&lt;td>&lt;code>DeleteQuota&lt;/code>&lt;/td>
&lt;td>&lt;code>fcsandbox:DeleteQuota&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>服务的 RAM 授权码是 &lt;code>fcsandbox&lt;/code>。如果要按地域和账号收敛范围，把 &lt;code>Resource&lt;/code> 从 &lt;code>*&lt;/code> 改成 &lt;code>acs:fcsandbox:&amp;lt;region&amp;gt;:&amp;lt;account-id&amp;gt;:*&lt;/code>。&lt;/p>
&lt;p>这也是我和第一节那段绕了一圈的地方：&lt;strong>配额走的是 RAM 这条路，不是 API Key。&lt;/strong> 第一节里说过 &lt;code>fcsandbox&lt;/code> 不在可视化编辑器里、要用脚本编辑手写——所以真正的现象往往是「沙箱跑得好好的，一到配额接口就 401」，因为两条链路上的凭证压根不是同一套。&lt;/p>
&lt;p>用阿里云 POP Go SDK 的骨架：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">config&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">openapiutil&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Config&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">AccessKeyId&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_ID&amp;#34;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">AccessKeySecret&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_SECRET&amp;#34;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SecurityToken&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ALIBABA_CLOUD_SECURITY_TOKEN&amp;#34;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Endpoint&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;fcsandbox.cn-beijing.aliyuncs.com&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">client&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">fcsandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">resp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">UpdateQuota&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">fcsandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">UpdateQuotaRequest&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Body&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">fcsandbox&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Quota&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">TagValue&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">String&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">teamID&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">CpuCores&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Int32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">MemoryGB&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">tea&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Int32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>错误码表（也可以当排错用）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HTTP&lt;/th>
&lt;th>Code&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>400&lt;/td>
&lt;td>&lt;code>InvalidParameter&lt;/code>&lt;/td>
&lt;td>参数校验失败，缺少必填字段或格式不合法&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>401&lt;/td>
&lt;td>&lt;code>Unauthorized&lt;/code>&lt;/td>
&lt;td>凭证无效或未提供&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>403&lt;/td>
&lt;td>&lt;code>Forbidden&lt;/code>&lt;/td>
&lt;td>无权操作&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>404&lt;/td>
&lt;td>&lt;code>ResourceQuotaNotFound&lt;/code>&lt;/td>
&lt;td>查询的配额不存在&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>429&lt;/td>
&lt;td>&lt;code>LimitExceeded&lt;/code>&lt;/td>
&lt;td>超出配额限制&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>500&lt;/td>
&lt;td>&lt;code>InternalError&lt;/code>&lt;/td>
&lt;td>服务内部错误&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>删除配额后再查会返回 404，&lt;strong>但可能有短暂的最终一致性延迟&lt;/strong>，文档建议用有上限的轮询确认——「有上限」三个字是重点。&lt;/p>
&lt;p>使用建议里我最认同两条：&lt;strong>「不要让测试任务和生产任务共用同一个高配额 Team」&lt;/strong>，以及 &lt;strong>「配额只解决资源上限问题，不能替代业务侧限流、任务队列、超时控制和资源释放」&lt;/strong>。后者是在提醒你：配额是天花板，不是限流器；撞到天花板的失败模式和排队等待完全不一样。&lt;/p>
&lt;h3 id="日志采集要确认的三件事">日志采集要确认的三件事
&lt;/h3>&lt;p>前面坑四里说过，E2B 的 Logs 接口返回空数组是已知行为。正路是走&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/monitoring-and-logging" target="_blank" rel="noopener"
>监控与日志&lt;/a>。那一页有一句我觉得该抄进设计文档的话：&lt;/p>
&lt;blockquote>
&lt;p>应用侧可以记录 sandboxId、任务 ID 和命令结果，便于在云上监控或日志页面中定位问题。&lt;/p>
&lt;/blockquote>
&lt;p>它还把常见观测对象列成了清单，可以直接当埋点需求单用：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Sandbox 生命周期&lt;/strong>：创建、连接、暂停、恢复、终止。&lt;/li>
&lt;li>&lt;strong>命令执行结果&lt;/strong>：退出码、stdout、stderr、耗时。&lt;/li>
&lt;li>&lt;strong>资源使用&lt;/strong>：CPU、内存、磁盘和网络。&lt;/li>
&lt;li>&lt;strong>关联字段&lt;/strong>：&lt;code>sandboxId&lt;/code>、任务 ID、用户或租户标识、模板名称。&lt;/li>
&lt;li>&lt;strong>结构化日志&lt;/strong>：&lt;code>event&lt;/code>、&lt;code>taskId&lt;/code>、&lt;code>sandboxId&lt;/code>、&lt;code>exitCode&lt;/code>、stdout / stderr 摘要。&lt;/li>
&lt;/ul>
&lt;p>注意这里列了「资源使用：CPU、内存、磁盘和网络」——&lt;strong>但指标接口的磁盘字段是占位值&lt;/strong>，所以真实数据得从控制台或云监控拿。这两处放在一起看，能看出「观测对象」和「可获取的数据源」是两件事。&lt;/p>
&lt;h2 id="八迁不动的那部分怎么办">八、迁不动的那部分怎么办
&lt;/h2>&lt;p>几个决策，我按「原来怎么做 → 现在怎么做」列一下（&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-compatibility-and-migration" target="_blank" rel="noopener"
>E2B 兼容与迁移&lt;/a>）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>原来（E2B 原生）&lt;/th>
&lt;th>现在（云沙箱）&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Volume 做持久化&lt;/td>
&lt;td>换 NAS / OSS；跨 Sandbox 的数据本来就不该放本地文件系统&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SDK 管理 API Key&lt;/td>
&lt;td>函数计算控制台创建、查看、编辑、重置、禁用、删除&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Team 管理&lt;/td>
&lt;td>函数计算控制台（创建 Team 见&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-team" target="_blank" rel="noopener"
>创建 Team&lt;/a>，订阅计划见 &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/team-and-subscription-plans" target="_blank" rel="noopener"
>Team 与订阅计划&lt;/a>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Snapshot 做状态分叉&lt;/td>
&lt;td>可以，但要白名单 + 第二代运行时，且注意命名与超时规则与官方不同&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>文件自定义元数据&lt;/td>
&lt;td>不支持；改用控制面 &lt;code>metadata&lt;/code>，或把标签写进业务库 / JSON 清单&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>依赖 E2B 的 Logs 接口&lt;/td>
&lt;td>换成函数计算日志采集 + 业务侧结构化日志&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>依赖 E2B 的网络配置更新接口&lt;/td>
&lt;td>换到云沙箱控制面&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>想要固定域名和自有证书&lt;/td>
&lt;td>用 FC Extensions 的&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-domain-name" target="_blank" rel="noopener"
>自定义域名&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>沙箱要访问内网 RDS / Redis / NAS&lt;/td>
&lt;td>用 FC Extensions 的 &lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/vpc-network-configuration-1" target="_blank" rel="noopener"
>VPC 网络配置&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>E2B 托管 MCP Gateway / BYOC&lt;/td>
&lt;td>不在兼容路径里，需要单独设计&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>API Key 的隔离策略&lt;/strong>值得单独说一句：按应用、环境、团队或租户拆分 Key，不要多人多系统共用一个长期 Key；生产用自定义过期时间并定期轮换；&lt;strong>重置或删除前先确认业务侧已经切到新 Key&lt;/strong>——这条写在文档里，但我猜真出事的时候是「先重置，再发现有三个服务挂了」。&lt;/p>
&lt;p>还有一条和 E2B 一样的老常识，仍然要重复一遍：&lt;strong>Sandbox 的本地文件系统只适合当前任务内的临时文件。&lt;/strong> Sandbox 终止之后，这些文件不应该被当成持久数据依赖。临时输入、生成代码和中间结果建议写 &lt;code>/tmp&lt;/code> 或业务自定义工作目录。&lt;/p>
&lt;p>以及从沙箱选型那篇就一直在强调的点：处理用户上传路径时，要限制文件大小、文件类型和可写路径，别把未校验的路径直接传给 Filesystem API 或命令行。&lt;strong>这一点和用哪家沙箱无关&lt;/strong>——沙箱防的是代码逃逸，不防你的业务逻辑被 prompt injection 牵着走。浏览器模板那一页把这条说得更直接：&lt;strong>网页内容和下载文件都可能携带 prompt injection。&lt;/strong>&lt;/p>
&lt;h2 id="九上生产前必须确认的边界">九、上生产前必须确认的边界
&lt;/h2>&lt;p>这一节是&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/usage-constraints-of-fc-agent-sandbox" target="_blank" rel="noopener"
>使用约束&lt;/a>的整理。官方把话说得很直接：&lt;strong>快速入门只演示最小接入路径，实际业务接入前应先确认本文约束。&lt;/strong>&lt;/p>
&lt;h3 id="配额六类不能假设无限">配额：六类不能假设无限
&lt;/h3>&lt;ul>
&lt;li>单账号或单地域&lt;strong>并发 Sandbox 数&lt;/strong>&lt;/li>
&lt;li>单个 Sandbox 可用的 &lt;strong>CPU、内存和本地磁盘空间&lt;/strong>&lt;/li>
&lt;li>单个 Sandbox 可打开的&lt;strong>进程数、端口数、文件数&lt;/strong>&lt;/li>
&lt;li>单次任务的&lt;strong>输入、输出文件大小&lt;/strong>&lt;/li>
&lt;li>&lt;strong>模板构建&lt;/strong>的并发数、构建资源和构建时长&lt;/li>
&lt;li>&lt;strong>端口访问&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>超出默认配额时，Sandbox 创建、模板构建或任务执行都可能失败。需要更高配额要走控制台或阿里云支持渠道申请。&lt;strong>这条对做 To C 产品的人尤其重要&lt;/strong>——你的并发上限不是你代码写的并发数，是账号配额。&lt;/p>
&lt;h3 id="生命周期">生命周期
&lt;/h3>&lt;p>Sandbox 创建后会持续占用资源，直到被主动终止、超时回收，或进入支持的暂停状态。任务完成后 &lt;code>kill()&lt;/code>。&lt;/p>
&lt;p>三个和生命周期相关的数字放在一起记：&lt;strong>上限 24 小时（86400 秒）、空闲超时下限 60 秒、命令超时默认 60 秒。&lt;/strong> 超时参数单位差异再说一遍：&lt;strong>Python 通常用秒，TypeScript 用毫秒。&lt;/strong>&lt;/p>
&lt;h3 id="文件与存储">文件与存储
&lt;/h3>&lt;ul>
&lt;li>本地文件系统只服务当前 Sandbox 生命周期。&lt;/li>
&lt;li>跨 Sandbox 保留、共享或长期保存的数据写 NAS / OSS。&lt;/li>
&lt;li>处理用户上传时限制文件大小、类型和可写路径。&lt;/li>
&lt;/ul>
&lt;h2 id="十一份可执行的迁移顺序">十、一份可执行的迁移顺序
&lt;/h2>&lt;p>如果你手上有现成的 E2B 应用，我会建议按这个顺序推，每一步都有明确的验收点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>开通、建 Team、建 Key。&lt;/strong> 确认目标地域支持云沙箱、账号已开通云沙箱功能，按「项目 × 环境」建 Team，再在 Team 下创建 API Key，描述写清楚用途。&lt;strong>这一步会同时决定你要不要那段 &lt;code>fcsandbox&lt;/code> 策略&lt;/strong>——只用 SDK 就不需要，要在 OpenAPI 侧管资源才要。&lt;/li>
&lt;li>&lt;strong>定地域、配环境变量、钉版本。&lt;/strong> 选出地域，配好三个环境变量，把 SDK 版本写进 requirements / package.json，别用 &lt;code>latest&lt;/code>。&lt;/li>
&lt;li>&lt;strong>跑最小验证，只验证四件事&lt;/strong>：创建 Sandbox、执行命令、读写文件、释放资源。文档原话是「不要一开始就迁移复杂模板、网络和存储逻辑」——这个顺序是对的，因为它把变量控制在最少。&lt;/li>
&lt;li>&lt;strong>想清楚用哪个模板，别让默认值替你做决定。&lt;/strong> 要 &lt;code>run_code&lt;/code> 就必须装 &lt;code>e2b-code-interpreter&lt;/code> / &lt;code>@e2b/code-interpreter&lt;/code>；用通用 &lt;code>e2b&lt;/code> SDK 时&lt;strong>必须显式传 &lt;code>template&lt;/code>&lt;/strong>，不传会落到 &lt;code>base&lt;/code>。&lt;/li>
&lt;li>&lt;strong>再迁自定义模板。&lt;/strong> 用官方镜像先跑通构建流程，再换自己的 ACR EE 镜像；构建前对着镜像要求表逐条核一遍，并确认镜像仓库、VPC 和沙箱同地域。&lt;/li>
&lt;li>&lt;strong>需要浏览器能力的话，这里才开始&lt;/strong>。browser / All-In-One 都要先构建模板，再注意 &lt;code>/health&lt;/code> 轮询、&lt;code>X-Access-Token&lt;/code>、窗口尺寸烤镜像这几件事。&lt;/li>
&lt;li>&lt;strong>迁移时同步做「能力减法」&lt;/strong>：把 Volume、Access Token、Team 管理、文件自定义元数据、E2B Logs 接口这些从代码里挑出来，换成 NAS / OSS、控制台、控制面 &lt;code>metadata&lt;/code> 和日志采集。&lt;/li>
&lt;li>&lt;strong>最后才是网络和可观测。&lt;/strong> VPC、OSS 挂载、自定义域名、日志采集、Team 配额，都属于 FC Extensions，走 metadata 或控制面配置，不占用迁移改造的窗口。&lt;/li>
&lt;li>&lt;strong>上线前补三样东西&lt;/strong>：显式的 &lt;code>kill()&lt;/code> 释放路径（&lt;code>try/finally&lt;/code>、&lt;code>defer&lt;/code> 或 &lt;code>AutoCloseable&lt;/code> 都行）、业务侧的 &lt;code>taskId ↔ sandboxId&lt;/code> 映射、以及结构化日志。&lt;/li>
&lt;/ol>
&lt;h2 id="十一排错对照表">十一、排错对照表
&lt;/h2>&lt;p>踩过一轮之后，我把「现象 → 先查什么」整理成了一张表，比按文档目录翻快：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>现象&lt;/th>
&lt;th>优先排查&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>认证失败 / 模板不可见&lt;/td>
&lt;td>三个环境变量是否配全、Key 是否被禁用或重置、账号与地域是否一致&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>创建失败、连接失败、模板找不到&lt;/td>
&lt;td>API URL、域名、模板、Sandbox &lt;strong>是否同地域&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>连接失败&lt;/td>
&lt;td>目标 Sandbox 是否已终止、超时回收，或不属于当前账号 / 地域&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sandbox 创建成功但 &lt;code>run_code&lt;/code> 失败&lt;/td>
&lt;td>&lt;strong>SDK 是否装错&lt;/strong>；通用 &lt;code>e2b&lt;/code> SDK 不传模板会落到 &lt;code>base&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>内置模板正常、自定义模板 &lt;code>run_code&lt;/code> 失败&lt;/td>
&lt;td>自定义模板的 Code Interpreter 依赖、启动命令、监听端口、就绪条件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TypeScript 里上下文管理方法不可用&lt;/td>
&lt;td>该能力当前&lt;strong>仅 Python SDK&lt;/strong> 提供&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写入报 &lt;code>File metadata requires envd 0.6.2 or later&lt;/code>&lt;/td>
&lt;td>用了文件自定义元数据，当前不支持；改用控制面 &lt;code>metadata&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>上传 / 下载 URL 403&lt;/td>
&lt;td>创建 Sandbox 时是否显式设了 &lt;code>secure=false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>服务起来 60 秒后被杀&lt;/td>
&lt;td>命令超时默认 60 秒，后台进程要显式设 &lt;code>timeout&lt;/code> / &lt;code>timeoutMs&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PTY 输出解析不了&lt;/td>
&lt;td>PTY 会改变输出格式；批处理应该用 &lt;code>commands.run()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>浏览器端点 403&lt;/td>
&lt;td>请求头缺 &lt;code>X-Access-Token&lt;/code>（&lt;code>sbx._envd_access_token&lt;/code> / &lt;code>sbx.envdAccessToken&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>noVNC 连不上&lt;/td>
&lt;td>浏览器 WebSocket API 不能带自定义 header；改用支持 header 的客户端，或直接截图&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CDP 探测返回退出码 28&lt;/td>
&lt;td>正常现象，看响应里有没有 &lt;code>101 Switching Protocols&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>改了浏览器窗口尺寸没生效&lt;/td>
&lt;td>&lt;code>envs&lt;/code> 不影响浏览器栈，要烤进镜像（&lt;code>RESOLUTION&lt;/code> 等）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>browser / All-In-One 创建模板失败&lt;/td>
&lt;td>跨地域镜像构建会失败，镜像地址里的地域要换成沙箱地域&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>建了带 VPC 的沙箱但内网连不通&lt;/td>
&lt;td>安全组出方向、目标资源白名单里的 &lt;strong>vSwitch 网段&lt;/strong>、服务监听状态&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>挂载 OSS 后没有权限&lt;/td>
&lt;td>是否同时配了 &lt;code>fc.sandbox.auth.role&lt;/code>（RAM Role ARN）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>自定义域名证书校验失败&lt;/td>
&lt;td>证书要覆盖&lt;strong>数据链路泛域名&lt;/strong>（&lt;code>*.example.com&lt;/code>），且私钥是未加密 RSA PEM&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>改了网络策略没生效&lt;/td>
&lt;td>Network Config Update 是受限能力，要去控制面改&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>抓不到沙箱日志&lt;/td>
&lt;td>E2B Logs 接口返回空数组是已知行为，走函数计算日志采集&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>容量 / 费用看着不对&lt;/td>
&lt;td>别用 Metrics 的磁盘字段（占位值），以控制台 / 云监控 / 日志服务为准&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>创建 Snapshot 超时&lt;/td>
&lt;td>&lt;code>request_timeout&lt;/code> 是否 ≥ 300；&lt;strong>先查是否已建成，不要无条件重试&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>沙箱列表里找不到失败的快照&lt;/td>
&lt;td>&lt;code>snapshot_failed&lt;/code> 状态默认不显示，要按状态过滤&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>刚建的 Snapshot 列表里看不到&lt;/td>
&lt;td>索引延迟；直接用创建返回的 &lt;code>snapshotId&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Team 配额调用报 401 / 403&lt;/td>
&lt;td>配额用 &lt;strong>AK/SK + &lt;code>fcsandbox&lt;/code> RAM 授权&lt;/strong>，不用沙箱 API Key&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RAM 控制台搜不到 &lt;code>fcsandbox&lt;/code>&lt;/td>
&lt;td>该服务不在可视化编辑器的服务列表里，改用&lt;strong>脚本编辑&lt;/strong>手写 action&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>建策略后仍建不了模板 / API Key&lt;/td>
&lt;td>Team 级策略要同时给 &lt;code>teams/&amp;lt;id&amp;gt;&lt;/code> 和 &lt;code>teams/&amp;lt;id&amp;gt;/*&lt;/code>，只给前者会「看得见动不了」&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>沙箱能跑，但 OpenAPI 管不了 Team / API Key&lt;/td>
&lt;td>两套鉴权别混：SDK / CLI 用 API Key，控制台 / OpenAPI 用 RAM 策略 + AK/SK&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>模板构建失败&lt;/td>
&lt;td>先查镜像仓库、网络、账号权限，再查 SDK 参数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SDK 可用但 CLI 不可用&lt;/td>
&lt;td>CLI 版本、&lt;code>E2B_API_KEY&lt;/code>、&lt;code>E2B_API_URL&lt;/code>、&lt;code>E2B_DOMAIN&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CLI 行为与文档不一致&lt;/td>
&lt;td>先 &lt;code>e2b --version&lt;/code> 和 &lt;code>&amp;lt;cmd&amp;gt; --help&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="十二文档地图">十二、文档地图
&lt;/h2>&lt;p>上面所有结论都来自官方文档。我把 38 页按分组列在这里，并标了每页在本文的对应位置——&lt;strong>标「—」的是我提了一句但没展开的，需要细节请直接点链接。&lt;/strong>&lt;/p>
&lt;p>&lt;strong>入门与接入&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/product-overview-of-fc-agent-sandbox" target="_blank" rel="noopener"
>产品简介&lt;/a>&lt;/td>
&lt;td>定位、五类场景、八个核心对象、基本使用路径&lt;/td>
&lt;td>第一节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-api-key" target="_blank" rel="noopener"
>创建 API Key&lt;/a>&lt;/td>
&lt;td>控制台创建步骤、过期时间、编辑/重置/删除、安全建议&lt;/td>
&lt;td>第一节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-cloud-sandbox-via-the-sdk" target="_blank" rel="noopener"
>通过 SDK 使用云沙箱&lt;/a>&lt;/td>
&lt;td>Python / TypeScript 快速入门、版本要求&lt;/td>
&lt;td>第一节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-cloud-sandbox-via-the-cli" target="_blank" rel="noopener"
>通过 CLI 使用云沙箱&lt;/a>&lt;/td>
&lt;td>CLI 安装、八步操作流程、各命令示例&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-integration-parameter-description" target="_blank" rel="noopener"
>E2B SDK 接入参数说明&lt;/a>&lt;/td>
&lt;td>三个环境变量与 SDK 参数逐个对应、snake/camel 差异、不作为接入参数的能力&lt;/td>
&lt;td>第一、二节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>兼容与迁移&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-compatibility-explanation" target="_blank" rel="noopener"
>E2B 兼容说明&lt;/a>&lt;/td>
&lt;td>四档兼容状态、各模块完整方法清单、受限与暂不兼容能力&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-compatible-api-list" target="_blank" rel="noopener"
>E2B SDK 兼容 API 清单&lt;/a>&lt;/td>
&lt;td>按对象罗列的兼容方法、使用建议&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-compatibility-and-migration" target="_blank" rel="noopener"
>E2B 兼容与迁移&lt;/a>&lt;/td>
&lt;td>迁移四件事、地域一致性、常见问题&lt;/td>
&lt;td>第一、八节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/e2b-sdk-java-and-go" target="_blank" rel="noopener"
>E2B SDK（Java 与 Go）&lt;/a>&lt;/td>
&lt;td>两个 SDK 的安装与快速入门、Maven / replace 注意事项&lt;/td>
&lt;td>第二节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/usage-constraints-of-fc-agent-sandbox" target="_blank" rel="noopener"
>使用约束&lt;/a>&lt;/td>
&lt;td>地域、版本、鉴权、六类配额、生命周期、模板构建、文件存储&lt;/td>
&lt;td>第九节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Sandbox 功能&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-a-sandbox" target="_blank" rel="noopener"
>创建沙箱&lt;/a>&lt;/td>
&lt;td>&lt;code>Sandbox.create&lt;/code> 参数（&lt;code>timeoutMs&lt;/code> / &lt;code>envs&lt;/code> / &lt;code>metadata&lt;/code>）、Python 与 TS 传参差异&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/lifecycle" target="_blank" rel="noopener"
>生命周期&lt;/a>&lt;/td>
&lt;td>创建、连接、查询、终止、设置超时&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/timeout" target="_blank" rel="noopener"
>超时&lt;/a>&lt;/td>
&lt;td>创建时配置、创建后调整、单位差异&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/pause-and-resume" target="_blank" rel="noopener"
>暂停与恢复&lt;/a>&lt;/td>
&lt;td>白名单、&lt;code>pause()&lt;/code>、连接时自动恢复、使用建议&lt;/td>
&lt;td>第五节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/environment-variable" target="_blank" rel="noopener"
>环境变量&lt;/a>&lt;/td>
&lt;td>沙箱级与命令级 &lt;code>envs&lt;/code>、覆盖规则&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/metadata" target="_blank" rel="noopener"
>元数据&lt;/a>&lt;/td>
&lt;td>控制面 &lt;code>metadata&lt;/code> 写法、与环境变量的区别&lt;/td>
&lt;td>第四节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/snapshots" target="_blank" rel="noopener"
>快照（邀测）&lt;/a>&lt;/td>
&lt;td>API 表、名称规则、保留 7 天、请求超时 300 秒、十种失败条件、参数覆盖清单&lt;/td>
&lt;td>第五节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Commands 与 Filesystem&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/run-the-command" target="_blank" rel="noopener"
>运行命令&lt;/a>&lt;/td>
&lt;td>&lt;code>commands.*&lt;/code> 方法清单、与 PTY 的分工&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/backend-command" target="_blank" rel="noopener"
>后台命令&lt;/a>&lt;/td>
&lt;td>&lt;code>background=True&lt;/code>、&lt;code>getHost&lt;/code>、进程连接与终止、命令超时默认 60 秒&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/pty" target="_blank" rel="noopener"
>PTY&lt;/a>&lt;/td>
&lt;td>&lt;code>pty.create&lt;/code> / &lt;code>send_stdin&lt;/code> / &lt;code>wait&lt;/code> / &lt;code>resize&lt;/code> / &lt;code>connect&lt;/code>、何时该用 PTY&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/read-and-write-files" target="_blank" rel="noopener"
>读写文件&lt;/a>&lt;/td>
&lt;td>方法清单、批量写入、二进制与流、目录监听&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/upload-and-download-files" target="_blank" rel="noopener"
>上传和下载文件&lt;/a>&lt;/td>
&lt;td>&lt;code>uploadUrl&lt;/code> / &lt;code>downloadUrl&lt;/code>、&lt;strong>&lt;code>secure=false&lt;/code> 要求&lt;/strong>、选型建议&lt;/td>
&lt;td>第四节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-metadata" target="_blank" rel="noopener"
>自定义元数据&lt;/a>&lt;/td>
&lt;td>不支持文件自定义元数据、失败行为、三条替代方案&lt;/td>
&lt;td>第四节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Code Interpreter&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/overview-1" target="_blank" rel="noopener"
>Code Interpreter 概览&lt;/a>&lt;/td>
&lt;td>Code Interpreter 能力总览&lt;/td>
&lt;td>第三节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-the-code-interpreter-sandbox" target="_blank" rel="noopener"
>使用 Code Interpreter Sandbox&lt;/a>&lt;/td>
&lt;td>四步推荐流程、固定脚本入口优于 &lt;code>run_code&lt;/code>、六条上线建议&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>模板&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/built-in-templates" target="_blank" rel="noopener"
>内置模板&lt;/a>&lt;/td>
&lt;td>三个内置模板清单、哪些需要自行构建、如何查看&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/base-template" target="_blank" rel="noopener"
>base 模板&lt;/a>&lt;/td>
&lt;td>功能特性、默认配置（2 vCPU / 2048 MB）、&lt;strong>不传 template 时的默认值&lt;/strong>&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/code-interpreter-v1-template" target="_blank" rel="noopener"
>code-interpreter-v1 模板&lt;/a>&lt;/td>
&lt;td>默认端口 5000、24 小时上限、空闲超时下限、&lt;code>run_code&lt;/code> 参数与默认超时、上下文管理仅 Python&lt;/td>
&lt;td>第三、六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/browser-template" target="_blank" rel="noopener"
>browser 模板&lt;/a>&lt;/td>
&lt;td>镜像与规格、CDP / VNC 端点、&lt;code>X-Access-Token&lt;/code>、窗口尺寸烤镜像、构建与验证&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/all-in-one-template" target="_blank" rel="noopener"
>All-In-One 模板&lt;/a>&lt;/td>
&lt;td>与 browser 的差异、3000 + 5000 双端口、跨地域构建会失败&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/use-browser-use-sandbox" target="_blank" rel="noopener"
>使用 Browser Use Sandbox&lt;/a>&lt;/td>
&lt;td>八步推荐流程、Puppeteer 示例、BrowserUse 接入、六条上线建议&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/using-aio-sandbox" target="_blank" rel="noopener"
>使用 AIO Sandbox&lt;/a>&lt;/td>
&lt;td>浏览器 + 代码执行协同流程、固定任务目录、接入建议&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/templates" target="_blank" rel="noopener"
>快速开始（模板）&lt;/a>&lt;/td>
&lt;td>&lt;code>Template.build&lt;/code> 用法、从模板创建沙箱&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/template-name" target="_blank" rel="noopener"
>模板名称与版本&lt;/a>&lt;/td>
&lt;td>命名建议、标签 &lt;code>assignTags&lt;/code> / &lt;code>getTags&lt;/code> / &lt;code>removeTags&lt;/code>、&lt;code>default&lt;/code> 不可删&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/build-a-custom-image-template" target="_blank" rel="noopener"
>构建自定义镜像模板&lt;/a>&lt;/td>
&lt;td>ACR EE 前置条件、镜像要求表、官方镜像地址、构建脚本&lt;/td>
&lt;td>第六节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>其他模板（Desktop / Claude Code / OpenClaw 等）&lt;/td>
&lt;td>需从对应官方镜像自行构建&lt;/td>
&lt;td>第六节（未展开）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>FC Extensions&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>页面&lt;/th>
&lt;th>内容&lt;/th>
&lt;th>本文位置&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/fc-extensions-overview" target="_blank" rel="noopener"
>FC Extensions 概览&lt;/a>&lt;/td>
&lt;td>七项扩展、五步使用方式、注意事项&lt;/td>
&lt;td>第七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/vpc-network-configuration-1" target="_blank" rel="noopener"
>VPC 网络配置&lt;/a>&lt;/td>
&lt;td>&lt;code>fc.sandbox.network.vpc&lt;/code> metadata 格式、前置条件、连通性验证、常见问题&lt;/td>
&lt;td>第七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/mount-oss-dynamically-1" target="_blank" rel="noopener"
>动态挂载 OSS&lt;/a>&lt;/td>
&lt;td>&lt;code>fc.sandbox.storage.oss&lt;/code> + &lt;code>fc.sandbox.auth.role&lt;/code>、挂载字段、RAM Policy 建议&lt;/td>
&lt;td>第七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/custom-domain-name" target="_blank" rel="noopener"
>自定义域名&lt;/a>&lt;/td>
&lt;td>控制链路 / 数据链路域名、证书与私钥要求、DNS 配置、五步验证&lt;/td>
&lt;td>第七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/monitoring-and-logging" target="_blank" rel="noopener"
>监控与日志&lt;/a>&lt;/td>
&lt;td>配置入口与生效范围、结构化日志、常见观测对象&lt;/td>
&lt;td>第四、七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/quota-management" target="_blank" rel="noopener"
>Team 配额管理&lt;/a>&lt;/td>
&lt;td>POP SDK + AK/SK 鉴权、配额模型、四个接口、RAM 授权、错误码&lt;/td>
&lt;td>第七节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/agent-sandbox/getting-started/configure-ram-user-permissions" target="_blank" rel="noopener"
>配置 RAM 用户权限&lt;/a>&lt;/td>
&lt;td>两套鉴权方式的边界、&lt;code>fcsandbox&lt;/code> action 与 ARN 格式、三种授权粒度、常见误区&lt;/td>
&lt;td>第一节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/agent-sandbox/getting-started/create-team" target="_blank" rel="noopener"
>创建 Team&lt;/a>&lt;/td>
&lt;td>创建 Team、获取 Team ID、资源组与订阅计划&lt;/td>
&lt;td>第一节&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/team-and-subscription-plans" target="_blank" rel="noopener"
>Team 与订阅计划&lt;/a>&lt;/td>
&lt;td>Team 与订阅计划的关系&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-oss-volume" target="_blank" rel="noopener"
>创建 OSS Volume&lt;/a>&lt;/td>
&lt;td>为 OSS Bucket 或子目录创建可复用挂载配置&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://help.aliyun.com/zh/functioncompute/create-an-agenticfs-volume" target="_blank" rel="noopener"
>创建 AgenticFS Volume&lt;/a>&lt;/td>
&lt;td>为 AgenticFS Access Point 创建可复用挂载配置&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>几点收获：&lt;/p>
&lt;p>&lt;strong>1. 「协议兼容」比「接口兼容」值钱得多。&lt;/strong> 三个环境变量就能跑通，是因为它兼容的是数据面协议，而不是照着表抄了一遍方法名——这也是 Java / Go SDK 能被第三方写出来的前提。代价是你得把 SDK 版本钉住，因为协议对齐是跟着版本走的，而且 Python 与 TypeScript 的参数名和单位还不一样。&lt;/p>
&lt;p>&lt;strong>2. 兼容清单要当成契约读，重点看「受限」和「暂不兼容」那两档。&lt;/strong> 「能调用」不等于「有效果」。Logs 返回空数组、Network Config Update 返回成功但不生效，这两个如果只看方法名不看文档，最后会以「线上偶发空白」和「改了没反应」的形式还给你。&lt;/p>
&lt;p>&lt;strong>3. 默认值是这个系统里最需要审计的东西。&lt;/strong> 通用 SDK 不传模板落到 &lt;code>base&lt;/code>（没有 Code Interpreter）、命令超时默认 60 秒、Snapshot 请求超时默认 60 秒但它要跑好几分钟、Snapshot 默认保留 7 天、&lt;code>secure&lt;/code> 默认要求带 token——&lt;strong>这五个默认值凑在一起，就是一个「Demo 能跑、生产出事」的配置集合。&lt;/strong> 文档里专门写了「不要无条件重试」，这种提醒通常意味着它真的发生过。&lt;/p>
&lt;p>&lt;strong>4. 静默失败比报错更贵。&lt;/strong> 这次真正花时间的坑，基本都不抛异常。&lt;code>metadata&lt;/code> 那个算厚道的——至少在发请求前把你拦下来了；&lt;code>secure&lt;/code> 和 Network Config Update 连拦都不拦。判断一个兼容层成熟不成熟，可以看它有多少能力是「返回成功但什么都没做」。&lt;/p>
&lt;p>&lt;strong>5. 跨语言抄示例是新的高频错误源。&lt;/strong> &lt;code>timeout&lt;/code> 秒 vs &lt;code>timeoutMs&lt;/code> 毫秒、&lt;code>api_key&lt;/code> vs &lt;code>apiKey&lt;/code>、&lt;code>make_dir&lt;/code> vs &lt;code>makeDir&lt;/code>、&lt;code>get_tags&lt;/code> 要传模板 ID 而 TS 传模板名、TS 里模板是第一个位置参数&lt;strong>但没模板时 options 又跑到第一位&lt;/strong>——同一个功能两套写法，而 SDK 不会因为你传错字段名报错，只会用默认值继续跑。&lt;strong>这类错误的共同特征是不报错，所以只能靠纪律防。&lt;/strong>&lt;/p>
&lt;p>&lt;strong>6. 「兼容」两个字的颗粒度，比你想的细。&lt;/strong> 同一个 Code Interpreter，&lt;code>run_code&lt;/code> 在两种语言里都通，但上下文管理只有 Python 有；同一个 Snapshot，创建和恢复能用，命名和删除路径又和官方不同。&lt;strong>迁移检查表要按「方法 × 语言 × 平台」三维核对，不能按模块粗过。&lt;/strong>&lt;/p>
&lt;p>&lt;strong>7. 白名单能力不要写进架构。&lt;/strong> pause、Snapshot、Team 配额都要加白。能开不代表一直在，当成设计前提就是把可用性挂在别人的后台配置上。&lt;/p>
&lt;p>&lt;strong>8. 云沙箱的差异化在 E2B SDK 之外。&lt;/strong> 真正的增量是 FC Extensions——VPC、OSS、自定义域名、日志、配额。如果你的场景需要这些，那这套东西的价值远不止「国内能连上的 E2B」；如果不需要，它就成了架构里一块只属于这朵云的代码。&lt;/p>
&lt;p>&lt;strong>9. 同一朵云里并存两套鉴权，是新的认知负担。&lt;/strong> API Key 管数据面，RAM 权限策略管控制台和 OpenAPI，管配额还要再叠一层 AK/SK。它们报的错长得几乎一样（401 / 403），但该修的地方完全不同。&lt;strong>排查任何「没权限」之前，先确认自己走的是哪条链路&lt;/strong>——我这次的弯路，就是从「以为只有一套凭证」开始的。还有一个附带结论：&lt;code>fcsandbox&lt;/code> 这种新服务不在 RAM 可视化编辑器里，是正常现象，不是你的账号有问题。&lt;/p>
&lt;p>&lt;strong>10. 顺手再提醒一次地域。&lt;/strong> &lt;code>E2B_API_URL&lt;/code>、&lt;code>E2B_DOMAIN&lt;/code>、模板、Sandbox 必须同地域；镜像仓库、VPC、自定义域名也都要同地域。这条排在最前面，也最容易在第一次接入时踩到，因为它和「Key 填错了」的现象几乎一样——都是认证或创建失败，而人的第一反应通常是去查 Key。&lt;/p></description></item><item><title>Agent 埋点接 ARMS：上报返回 success，控制台却是空的</title><link>https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/</link><pubDate>Fri, 18 Sep 2026 18:05:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/index.svg" alt="Featured image of post Agent 埋点接 ARMS：上报返回 success，控制台却是空的" />&lt;p>上报接口返回 &lt;code>200&lt;/code>，响应体是两个引号包着的 &lt;code>&amp;quot;success&amp;quot;&lt;/code>。控制台刷新了五遍，调用链分析里 &lt;code>Span 数量: 0&lt;/code>。&lt;/p>
&lt;p>第一反应当然是上报没成功。于是回头去查 payload、查鉴权、查代理——查了小半个小时，最后发现&lt;strong>数据一直都在，是我看的地方不对&lt;/strong>。&lt;/p>
&lt;p>这篇文章把这个过程完整记下来。前面是排查（含三个可以复用的判别实验），后面是这次顺带做的一个具体问题：&lt;strong>对话里带附件时，span 应该怎么记&lt;/strong>。&lt;/p>
&lt;blockquote>
&lt;p>前置概念（Trace 与 Span、父子关系、属性与事件的边界、上下文传递）见同系列 &lt;a class="link" href="https://www.zata.cc/p/agent-tracing-%E5%9F%BA%E7%A1%80trace-span-%E4%B8%8E-opentelemetry-%E5%9F%8B%E7%82%B9/" >Agent Tracing 基础：Trace、Span 与 OpenTelemetry 埋点&lt;/a>。本篇不重复讲基础，直接从&amp;quot;接进 ARMS 之后为什么看不到&amp;quot;讲起。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一gen_ai-到底是什么">一、&lt;code>gen_ai.*&lt;/code> 到底是什么
&lt;/h2>&lt;p>动手之前先明确一件事：埋点里那一堆 &lt;code>gen_ai.xxx&lt;/code> 不是我起的名字，是 &lt;strong>OpenTelemetry 的 GenAI 语义约定&lt;/strong>（Generative AI semantic conventions）——社区为&amp;quot;生成式 AI / LLM 应用&amp;quot;单独定义的一套属性规范。&lt;/p>
&lt;p>它解决的问题很朴素。没有规范时，同一个&amp;quot;模型名&amp;quot;会被写成 &lt;code>model&lt;/code>、&lt;code>model_name&lt;/code>、&lt;code>llm.model&lt;/code>；&amp;ldquo;输入 token&amp;quot;叫 &lt;code>prompt_tokens&lt;/code>、&lt;code>input_tokens&lt;/code>、&lt;code>usage.prompt&lt;/code>。结果就是&lt;strong>换个观测后端，埋点得重写一遍&lt;/strong>。&lt;/p>
&lt;p>约定把名字、类型、取值都钉死了，所以一份埋点能同时被 Langfuse、Phoenix、ARMS 正确解析。它大致覆盖五类：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类别&lt;/th>
&lt;th>属性&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>操作身份&lt;/td>
&lt;td>&lt;code>gen_ai.operation.name&lt;/code>&lt;/td>
&lt;td>&lt;code>chat&lt;/code> / &lt;code>embeddings&lt;/code> / &lt;code>invoke_agent&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>厂商与模型&lt;/td>
&lt;td>&lt;code>gen_ai.provider.name&lt;/code>、&lt;code>gen_ai.request.model&lt;/code>、&lt;code>gen_ai.response.model&lt;/code>&lt;/td>
&lt;td>provider 是判别字段&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>对话内容&lt;/td>
&lt;td>&lt;code>gen_ai.input.messages&lt;/code>、&lt;code>gen_ai.output.messages&lt;/code>、&lt;code>gen_ai.system_instructions&lt;/code>&lt;/td>
&lt;td>结构化消息，见下文&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用量与结果&lt;/td>
&lt;td>&lt;code>gen_ai.usage.input_tokens&lt;/code>、&lt;code>output_tokens&lt;/code>、&lt;code>gen_ai.response.finish_reasons&lt;/code>&lt;/td>
&lt;td>成本核算靠这组&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具与会话&lt;/td>
&lt;td>&lt;code>gen_ai.tool.name&lt;/code>、&lt;code>gen_ai.tool.call.id&lt;/code>、&lt;code>gen_ai.conversation.id&lt;/code>&lt;/td>
&lt;td>关联工具调用与多轮会话&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其中 &lt;code>input.messages&lt;/code> / &lt;code>output.messages&lt;/code> 是&lt;strong>结构化&lt;/strong>的，不是一段纯文本。每条消息是 &lt;code>{role, parts[]}&lt;/code>，part 有这些类型：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>part 类型&lt;/th>
&lt;th>必填字段&lt;/th>
&lt;th>用在哪&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>text&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>&lt;/td>
&lt;td>普通文本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>uri&lt;/code>&lt;/td>
&lt;td>&lt;code>uri&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>文件已在对象存储里，span 只记引用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>file&lt;/code>&lt;/td>
&lt;td>&lt;code>file_id&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>用厂商预上传能力（如 OpenAI Files API）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>blob&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>、&lt;code>modality&lt;/code>&lt;/td>
&lt;td>只有必须内联时才用，base64 进 span 有成本也有合规风险&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_call&lt;/code> / &lt;code>tool_call_response&lt;/code>&lt;/td>
&lt;td>&lt;code>name&lt;/code> / &lt;code>response&lt;/code>&lt;/td>
&lt;td>工具调用与结果&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>三个必须知道的坑：&lt;strong>这些属性绝大部分还是 &lt;code>Development&lt;/code> 状态，会改名&lt;/strong>——&lt;code>gen_ai.system&lt;/code> 就被重命名成了 &lt;code>gen_ai.provider.name&lt;/code>，&lt;code>gen_ai.prompt.*&lt;/code> 也被 &lt;code>input.messages&lt;/code> 取代；&lt;strong>结构化属性落不到 span 上&lt;/strong>，OTel 的 Python SDK 只接受基本类型，所以得 &lt;code>json.dumps&lt;/code> 成字符串再塞；&lt;strong>&lt;code>input/output.messages&lt;/code> 是 Opt-In 属性&lt;/strong>，规范明确说不该默认记录，因为可能含用户隐私。&lt;/p>
&lt;/blockquote>
&lt;h2 id="二span-树怎么设计">二、span 树怎么设计
&lt;/h2>&lt;p>目标形态是一次 Agent Run 对应一棵 span 树，业务库的 Run 记录里存 &lt;code>trace_id&lt;/code> 作为关联键：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">agent.run [run.id=run-xxx, user.id=demo-user-1, session.id=demo-session-1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ agent.attach_document file.uri / file.mime_type / file.size_bytes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ gen_ai chat gen_ai.input.messages / usage.* / latency_ms
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ tool get_freight_quote gen_ai.tool.name / gen_ai.tool.call.id / tool.result
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ gen_ai chat 基于工具结果的第二轮作答
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>写属性的部分收敛成几个 helper，别散在业务代码里——改名时只改一处：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">set_llm_request&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">messages&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.operation.name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;chat&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.provider.name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.request.model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.conversation.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">conversation_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># system 消息按约定不进 input messages，单独走 system_instructions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;gen_ai.input.messages&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to_input_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>to_input_messages&lt;/code> 里做两件事：把 system 消息摘出去、把文本统一截断。&lt;strong>截断不是可选项&lt;/strong>——&lt;code>input.messages&lt;/code> 里塞进一个 8000 字的 prompt，span 会大得没法用。&lt;/p>
&lt;h2 id="三一个-200-success-的假象">三、一个 200 success 的假象
&lt;/h2>&lt;p>配置填完，跑起来，日志里看到这一行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">POST http://tracing-analysis-dc-sg.aliyuncs.com/adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/api/otlp/traces HTTP/1.1&amp;#34; 200 9
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>200&lt;/code>、响应体 9 字节（就是 &lt;code>&amp;quot;success&amp;quot;&lt;/code> 加引号）。这种时候人很容易直接跳到&amp;quot;好，通了&amp;rdquo;，然后去控制台发现什么都没有，再回头怀疑人生。&lt;/p>
&lt;p>我做了三个实验来定位问题到底在链路哪一段。这三个实验本身挺通用，值得记：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>实验&lt;/th>
&lt;th>操作&lt;/th>
&lt;th>结果&lt;/th>
&lt;th>排除了什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>绕过本地代理&lt;/td>
&lt;td>&lt;code>--noproxy '*'&lt;/code> 直连重发&lt;/td>
&lt;td>同样 &lt;code>200 &amp;quot;success&amp;quot;&lt;/code>&lt;/td>
&lt;td>不是代理伪造的响应&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>发垃圾 payload&lt;/td>
&lt;td>&lt;code>head -c 300 /dev/urandom&lt;/code> 当 body 发过去&lt;/td>
&lt;td>&lt;code>400 Bad Request&lt;/code>&lt;/td>
&lt;td>网关&lt;strong>真的在解析&lt;/strong> protobuf，不是无脑返回成功&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>伪造 license key&lt;/td>
&lt;td>把 &lt;code>/adapt_.../&lt;/code> 换成随机串&lt;/td>
&lt;td>&lt;code>403 Forbidden&lt;/code>&lt;/td>
&lt;td>key 是&lt;strong>有效&lt;/strong>的，且服务端会校验 workspace&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个实验做完，结论就很硬了：&lt;strong>数据确实被服务端收下了&lt;/strong>，而且收进了一个合法的 workspace。既然服务端没问题，那&amp;quot;看不到&amp;quot;就只剩一种可能——&lt;strong>去看的那个地方，不是数据所在的地方&lt;/strong>。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/diagnosis.svg"
loading="lazy"
alt="诊断切段图：把上报链路切成 Agent 进程、本地代理、ARMS 网关、控制台四段，实验①绕过代理排除代理伪造，实验②发垃圾字节得到 400 排除网关不解析，实验③伪造 license key 得到 403 证明 key 有效，四段全部通过后剩下唯一的失败段是控制台的地域、页签与页面作用域"
>&lt;/p>
&lt;p>事实也确实如此。&lt;/p>
&lt;h2 id="四三个坑">四、三个坑
&lt;/h2>&lt;h3 id="坑一地域">坑一：地域
&lt;/h3>&lt;p>接入点是 &lt;code>tracing-analysis-dc-sg.aliyuncs.com&lt;/code>，&lt;code>dc-sg&lt;/code> 就是&lt;strong>新加坡&lt;/strong>。而控制台默认停在别的地域，调用链分析自然是空的。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>症状&lt;/strong>&lt;/td>
&lt;td>上报 &lt;code>200 success&lt;/code>，控制台调用链分析 &lt;code>Span 数量: 0&lt;/code>，左侧所有维度显示&amp;quot;没有匹配的值&amp;quot;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>控制台地域与接入点地域不一致。数据在 &lt;code>ap-southeast-1&lt;/code>，眼睛在别处&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>控制台切到与接入点同地域；&lt;code>应用列表&lt;/code> 也要在同地域看&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这类问题最坑的地方在于：&lt;strong>它不报错&lt;/strong>。上报端一切正常，服务端一切正常，只有&amp;quot;人对不上&amp;quot;。&lt;/p>
&lt;h3 id="坑二入口">坑二：入口
&lt;/h3>&lt;p>地域切对之后，还有第二层错位。控制台里同一个应用有不止一个入口：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>应用列表&lt;/strong> 分 &lt;code>OpenTelemetry&lt;/code> 和 &lt;code>ARMS&lt;/code> 两个页签。通过 OTel SDK 上报的应用只出现在前者；后者是 ARMS 探针接入的应用列表。看错页签 = 看到空列表。&lt;/li>
&lt;li>&lt;strong>应用维度的调用链分析&lt;/strong> 是带过滤的。我一度搜索框里直接粘 trace_id，仍然是 0 —— 因为页面作用域被钉死在某个应用上，搜索条件没换掉&amp;quot;应用&amp;quot;这个更大的前提。页面里搜出来的搜索框内容长这样：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">serviceName:&amp;#34;agentrun-agent-quick-oUpbu&amp;#34; and resources.acs.arms.service_id : &amp;#34;azmulz8rp8...&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第三段 &lt;code>resources.acs.arms.service_id&lt;/code> 是 ARMS 探针注入的资源属性。&lt;strong>OTel 自己上报的 span 根本没有这个属性&lt;/strong>，所以这个条件永远匹配不上。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>症状&lt;/strong>&lt;/td>
&lt;td>搜 trace_id 也搜不到；应用列表某个页签永远是空的&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>页面作用域（应用 / 页签）与数据的归属不匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>换到不绑定单个应用的入口，或把搜索条件改成 &lt;code>serviceName:&amp;quot;&amp;lt;你的服务名&amp;gt;&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="坑三配置写法">坑三：配置写法
&lt;/h3>&lt;p>第三个坑不致命，但错误信息很难自查，因为&lt;strong>不报错&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 错的：ARMS 的 header 不是 Authorization: Bearer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">Authorization&lt;/span>&lt;span class="o">=&lt;/span>Bearer &amp;lt;token&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 对的：HTTP 接入点鉴权编码在 /adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/ 路径里，headers 留空&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>http://tracing-analysis-dc-sg.aliyuncs.com/adapt_&amp;lt;xx&amp;gt;_&amp;lt;xx&amp;gt;/api/otlp/traces
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只有 gRPC 才需要 header，格式是 Authentication=&amp;lt;token&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span>&lt;span class="o">=&lt;/span>http://tracing-analysis-dc-sg.aliyuncs.com:8090
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">OTEL_EXPORTER_OTLP_HEADERS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">Authentication&lt;/span>&lt;span class="o">=&lt;/span>&amp;lt;token&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>还有一条同类的：HTTP 接入点&lt;strong>自带&lt;/strong> &lt;code>/api/otlp/traces&lt;/code>，不要再拼 &lt;code>/v1/traces&lt;/code>——那是本地 Jaeger 的写法，拼上去就是 404。&lt;/p>
&lt;h2 id="五对话里带附件怎么记">五、对话里带附件怎么记
&lt;/h2>&lt;p>这是这次真正想解决的问题：Agent 的对话里带了文件，trace 上该怎么表达。&lt;/p>
&lt;p>关键决定是&lt;strong>文件不进 span 的字节流&lt;/strong>。做法是三处落点：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>位置&lt;/th>
&lt;th>记什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>agent.attach_document&lt;/code> span&lt;/td>
&lt;td>&lt;code>file.uri&lt;/code> / &lt;code>file.mime_type&lt;/code> / &lt;code>file.size_bytes&lt;/code> / &lt;code>file.doc_id&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>gen_ai chat&lt;/code> 的 &lt;code>gen_ai.input.messages&lt;/code>&lt;/td>
&lt;td>该条 user 消息的 &lt;code>parts&lt;/code> 里一个 &lt;code>uri&lt;/code> part + 一个 &lt;code>text&lt;/code> part&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>业务库&lt;/td>
&lt;td>文件归属业务层（doc_id ↔ Run），trace 里只留引用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;img src="https://www.zata.cc/p/agent-%E5%9F%8B%E7%82%B9%E6%8E%A5-arms%E4%B8%8A%E6%8A%A5%E8%BF%94%E5%9B%9E-success%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%8D%B4%E6%98%AF%E7%A9%BA%E7%9A%84/images/index/attachment.svg"
loading="lazy"
alt="三层落点图：左边对象存储是唯一保存文件字节的地方；中间 trace 用 agent.attach_document 记 file.uri 与规模，用 gen_ai.input.messages 的 text、uri、text 三个 part 记模型看到的内容；右边业务库用 trace_id 关联两层"
>&lt;/p>
&lt;p>实测落进 ARMS 的属性长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[{&lt;/span>&lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;parts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;按附件里的信息报价&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;uri&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;uri&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;oss://bucket/uploads/doc-adcc4d1f/booking-note.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;modality&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;document&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;mime_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text/plain&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;订舱委托书 / BOOKING NOTE\n委托编号：BN-2026-0918-075…&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]}]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>三个设计取舍值得说一下：&lt;/p>
&lt;p>&lt;strong>第一，&lt;code>uri&lt;/code> part 和 &lt;code>text&lt;/code> part 成对出现。&lt;/strong> &lt;code>uri&lt;/code> 是溯源（文件从哪来），&lt;code>text&lt;/code> 是模型真正读到的内容（服务端预抽取的结果）。只记 &lt;code>uri&lt;/code>，排查时无法回答&amp;quot;模型到底看没看到附件&amp;quot;；只记 &lt;code>text&lt;/code>，又丢了文件来源。两个都记才查得动。&lt;/p>
&lt;p>&lt;strong>第二，&lt;code>modality&lt;/code> 是必填。&lt;/strong> &lt;code>uri&lt;/code> 和 &lt;code>file&lt;/code> part 都要求这个字段，取值 &lt;code>image&lt;/code> / &lt;code>video&lt;/code> / &lt;code>audio&lt;/code> / &lt;code>document&lt;/code>。容易漏，漏了后端可能直接忽略这个 part。&lt;/p>
&lt;p>&lt;strong>第三，模型调用侧和 trace 侧要分开构造。&lt;/strong> 应用侧消息保留结构化内容（&lt;code>{type: &amp;quot;text&amp;quot;}&lt;/code> / &lt;code>{type: &amp;quot;document&amp;quot;}&lt;/code>），发给 OpenAI 兼容端点时拍平成纯文本；转成 &lt;code>gen_ai.input.messages&lt;/code> 时才展开成 uri + text。这样两边各取所需，不会为了迁就某一方而变形：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模型侧：附件以抽取文本注入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">to_openai_content&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">isinstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;document&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;[[附件 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;uri&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">]]&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;extracted_text&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>验证方式是让模型复述附件里的独有信息——它在回答里用上了委托书的 &lt;code>ETD 2026-09-25&lt;/code>、截关时间和&amp;quot;14 天免箱期&amp;quot;。这说明附件确实进了模型上下文，不只是进了 span。&lt;/p>
&lt;p>顺手补的一个洞：&lt;code>BatchSpanProcessor&lt;/code> 默认靠 &lt;code>atexit&lt;/code> 冲刷，进程被强杀（Ctrl-C、沙箱超时）时最后一批 span 会丢。Run 结束处显式 &lt;code>force_flush()&lt;/code> 更稳。&lt;/p>
&lt;h2 id="六数据怎么拿出来给-ai-看">六、数据怎么拿出来给 AI 看
&lt;/h2>&lt;p>控制台是给人看的，AI 想自己查得走另外三条路：&lt;/p>
&lt;p>&lt;strong>1. OpenAPI&lt;/strong>（免费，最直接）。&lt;code>xtrace&lt;/code> 产品两个接口就能拼出一个导出脚本：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>接口&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>关键参数&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>SearchTraces&lt;/code>&lt;/td>
&lt;td>按条件列出调用链，返回 TraceID 列表&lt;/td>
&lt;td>&lt;code>RegionId&lt;/code>、&lt;code>StartTime&lt;/code>/&lt;code>EndTime&lt;/code>（毫秒）、&lt;code>ServiceName&lt;/code>、&lt;code>OperationName&lt;/code>、&lt;code>MinDuration&lt;/code>、&lt;code>Tag&lt;/code>、&lt;code>PageNumber&lt;/code>/&lt;code>PageSize&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>GetTrace&lt;/code>&lt;/td>
&lt;td>按 TraceID 取完整 span 明细&lt;/td>
&lt;td>&lt;code>TraceID&lt;/code>、&lt;code>RegionId&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>翻页拿 ID，再逐条取详情，落地成 JSON 就完了。&lt;/p>
&lt;p>&lt;strong>2. 可观测 MCP Server&lt;/strong>（&lt;code>aliyun/alibabacloud-observability-mcp-server&lt;/code>）。Go 单二进制，里面正好有对应的工具：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;alibaba_cloud_observability&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./bin/alibabacloud-observability-mcp-server&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;start&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--stdio&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_ID&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;AK&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_ACCESS_KEY_SECRET&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;SK&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ALIBABA_CLOUD_REGION&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cn-singapore&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>接上之后可以直接问&amp;quot;昨天耗时最长的几条 Run 是什么，&lt;code>gen_ai.input.messages&lt;/code> 里带没带附件&amp;quot;，由 &lt;code>umodel_search_traces&lt;/code> / &lt;code>umodel_get_traces&lt;/code> 去取。两个注意点：用只读 RAM 用户（&lt;code>AliyunARMSReadOnlyAccess&lt;/code>），别用主账号 AK；&lt;code>sls_text_to_sql&lt;/code>、&lt;code>sls_sop&lt;/code> 这类 AI 工具走 STAROps 单独计费，不需要就在 &lt;code>config.yaml&lt;/code> 的 &lt;code>enabled_tools&lt;/code> 里关掉。&lt;/p>
&lt;p>&lt;strong>3. Collector 双写&lt;/strong>。反正上报端在自己手里，OTel Collector 同一份数据同时投 ARMS 和本地存储（ClickHouse / 文件 / 对象存储）。本地那份没有 AK、没有分页、随便 SQL——&lt;strong>AI 分析用它比用控制台 API 舒服得多&lt;/strong>。ARMS 负责看瀑布图和告警，本地那份负责喂给 Agent。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>几个收获，都是这次真金白银换来的：&lt;/p>
&lt;p>&lt;strong>1. 200 不等于数据可用。&lt;/strong> &lt;code>&amp;quot;success&amp;quot;&lt;/code> 只说明网关收了，说明不了数据落到了你正在看的那个地方。判断&amp;quot;到底通没通&amp;quot;要有独立证据——我这次用的是&amp;quot;发垃圾字节看它报不报错&amp;quot;和&amp;quot;伪造 key 看它拦不拦&amp;quot;，两个实验各一次请求，就能把链路切段定位。&lt;/p>
&lt;p>&lt;strong>2. 可观测性接入的坑，多半在&amp;quot;人对不上&amp;quot;而不是&amp;quot;数据不通&amp;quot;。&lt;/strong> 地域、页签、页面作用域、filter 条件，这四样任何一个不对，现象都是同一个：空列表。而且全都不报错。排查时优先怀疑这个，比怀疑 SDK 划算。&lt;/p>
&lt;p>&lt;strong>3. 约定比实现重要。&lt;/strong> 用 &lt;code>gen_ai.*&lt;/code> 的收益在这次很直观：同一份埋点，改个 endpoint 就从本地控制台切到了 ARMS，一行代码没动。代价是要接受它还在 &lt;code>Development&lt;/code>、会改名——所以属性写入必须收敛到 helper 里。&lt;/p>
&lt;p>&lt;strong>4. 文件类数据，&amp;ldquo;引用 + 摘要&amp;quot;比&amp;quot;内容&amp;quot;有用。&lt;/strong> 把 pdf 塞进 span 不会让排查更容易，只会让它更贵更难搜。&lt;code>uri&lt;/code> part 记来源、&lt;code>text&lt;/code> part 记模型看到的内容、业务库存文件本身，三层各司其职。&lt;/p>
&lt;p>&lt;strong>5. 顺手能修的洞就别留着。&lt;/strong> &lt;code>force_flush()&lt;/code> 这种一行的事，不补就是&amp;quot;偶发丢 trace&amp;rdquo;，而偶发丢 trace 是最难查的那类问题——它会让上面所有的排查经验都建立在错误的观察上。&lt;/p></description></item><item><title>Cua 框架详解：给任何 Agent 一台可操控的电脑</title><link>https://www.zata.cc/p/cua-%E6%A1%86%E6%9E%B6%E8%AF%A6%E8%A7%A3%E7%BB%99%E4%BB%BB%E4%BD%95-agent-%E4%B8%80%E5%8F%B0%E5%8F%AF%E6%93%8D%E6%8E%A7%E7%9A%84%E7%94%B5%E8%84%91/</link><pubDate>Mon, 14 Sep 2026 16:00:00 +0800</pubDate><guid>https://www.zata.cc/p/cua-%E6%A1%86%E6%9E%B6%E8%AF%A6%E8%A7%A3%E7%BB%99%E4%BB%BB%E4%BD%95-agent-%E4%B8%80%E5%8F%B0%E5%8F%AF%E6%93%8D%E6%8E%A7%E7%9A%84%E7%94%B5%E8%84%91/</guid><description>&lt;img src="https://www.zata.cc/p/cua-%E6%A1%86%E6%9E%B6%E8%AF%A6%E8%A7%A3%E7%BB%99%E4%BB%BB%E4%BD%95-agent-%E4%B8%80%E5%8F%B0%E5%8F%AF%E6%93%8D%E6%8E%A7%E7%9A%84%E7%94%B5%E8%84%91/images/index/index.svg" alt="Featured image of post Cua 框架详解：给任何 Agent 一台可操控的电脑" />&lt;h1 id="cua-框架详解给任何-agent-一台可操控的电脑">Cua 框架详解：给任何 Agent 一台可操控的电脑
&lt;/h1>&lt;h2 id="前言会写代码的-agent不会用电脑">前言：会写代码的 Agent，不会用电脑
&lt;/h2>&lt;p>过去两年，AI Agent 在&amp;quot;写代码&amp;quot;这件事上已经卷出了新高度——Claude Code、Codex、Cursor 个个都能独立完成复杂工程任务。但一旦任务超出终端的边界：打开计算器验证一个结果、在 LibreOffice 里填一张表、操作一个只有 GUI 的内部系统——大多数 Agent 就只能干瞪眼。&lt;/p>
&lt;p>这就是 &lt;strong>Computer Use（计算机使用）&lt;/strong> 要解决的问题：让 Agent 像人一样看屏幕、点鼠标、敲键盘、操作真实的桌面应用。&lt;/p>
&lt;p>今天要介绍的 &lt;a class="link" href="https://github.com/trycua/cua" target="_blank" rel="noopener"
>trycua/cua&lt;/a> 是这个赛道上热度最高的开源项目之一（截至 2026-09 已超过 22.6k star，MIT 协议，4700+ commits，更新极其活跃）。它的定位一句话就能说清：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>You bring the agent and model. Cua provides the computer and automation tools.&lt;/strong>
你带来 Agent 和模型，Cua 提供电脑和自动化工具。&lt;/p>
&lt;/blockquote>
&lt;p>它不是一个绑定某家模型的产品，而是一套&lt;strong>模型无关、Agent 无关&lt;/strong>的基础设施：任何支持 MCP 的 Agent（Claude Code、Codex、Cursor、OpenClaw……）接上它，立刻获得对真实桌面的控制能力。&lt;/p>
&lt;h2 id="一computer-use-20不只会截图点击">一、Computer-Use 2.0：不只会截图点击
&lt;/h2>&lt;p>Cua 提出了 &amp;ldquo;Computer-Use 2.0&amp;rdquo; 的概念，核心思想是：&lt;strong>Agent 不应该在 API、代码和图形界面之间二选一，而是按任务自由切换&lt;/strong>。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>路线&lt;/th>
&lt;th>典型做法&lt;/th>
&lt;th>问题&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>纯视觉 CUA（1.0）&lt;/td>
&lt;td>截图 → 模型找坐标 → 模拟点击&lt;/td>
&lt;td>慢、贵、易错，每一步都要一次模型往返&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>纯 API/脚本&lt;/td>
&lt;td>直接调系统接口&lt;/td>
&lt;td>覆盖不了没有 API 的 GUI 应用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cua 的 2.0&lt;/strong>&lt;/td>
&lt;td>&lt;strong>语义接口优先，GUI 兜底，混合编排&lt;/strong>&lt;/td>
&lt;td>兼顾速度与覆盖面&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>具体来说，Cua Driver 操作一个应用时，会优先走&lt;strong>无障碍树（Accessibility Tree）、快捷键、直接赋值&lt;/strong>这类语义通道；只有在没有语义通道可用时，才退化到 OCR 和像素坐标。这带来两个直接好处：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>低延迟&lt;/strong>：少截图、少模型往返，一步到位；&lt;/li>
&lt;li>&lt;strong>后台投递（Background Delivery）&lt;/strong>：向应用发送指令时&lt;strong>不移动鼠标指针、不抢焦点&lt;/strong>——Agent 在后台操作 LibreOffice，你可以继续在前台干自己的事，两个 Cua 会话还能同时操作不同的应用。&lt;/li>
&lt;/ul>
&lt;p>这是它和&amp;quot;截图派&amp;quot;方案拉开差距最大的地方。&lt;/p>
&lt;h2 id="二四大核心组件">二、四大核心组件
&lt;/h2>&lt;p>Cua 仓库由四个产品组件构成，各自解决 computer use 链路中的一段：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Cua Driver │ │ Lume │ │ Cua Fleets │ │ Cua Bench │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 桌面驱动/MCP │ │ 本地虚拟化 │ │ 云桌面舰队 │ │ 评测基准 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制真实桌面 Apple Silicon 隔离的云端 训练/评估/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> macOS/Linux VM Linux 桌面池 数据生成
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="1-cua-driver--核心驱动">1. Cua Driver —— 核心驱动
&lt;/h3>&lt;p>整个项目的心脏，值得单独展开：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Rust 实现&lt;/strong>（&lt;code>cua-driver-rs&lt;/code>），通过 UniFFI 同时提供 Python 和 TypeScript SDK 绑定，性能和内存占用都远好于 Python 方案；&lt;/li>
&lt;li>&lt;strong>三种接入方式&lt;/strong>：CLI（&lt;code>cua-driver call ...&lt;/code>）、MCP Server（&lt;code>cua-driver mcp&lt;/code>）、类型化 SDK；&lt;/li>
&lt;li>&lt;strong>MCP 协议支持&lt;/strong>：支持 MCP &lt;code>2026-07-28&lt;/code> 新版协议（stdio 通道），同时向后兼容老客户端；还内嵌了 &lt;code>skill://cua-driver/&lt;/code> 技能资源，Agent 可以按需读取使用指南；&lt;/li>
&lt;li>&lt;strong>跨平台&lt;/strong>：macOS、Windows、Linux（X11、Wayland/Hyprland、AT-SPI 都有适配）。&lt;/li>
&lt;/ul>
&lt;h3 id="2-lume--本地虚拟化">2. Lume —— 本地虚拟化
&lt;/h3>&lt;p>在 Apple Silicon Mac 上基于 &lt;strong>Apple Virtualization.Framework&lt;/strong> 创建和管理本地 macOS / Linux 虚拟机（Swift 实现）。想让 Agent 在隔离环境里折腾而&lt;strong>不污染宿主机&lt;/strong>，这是最顺手的选择——几条命令就能从 Apple 官方恢复镜像拉起一台干净的 macOS VM。&lt;/p>
&lt;h3 id="3-cua-fleets--云桌面舰队">3. Cua Fleets —— 云桌面舰队
&lt;/h3>&lt;p>不想本地跑 VM？&lt;a class="link" href="https://run.cua.ai" target="_blank" rel="noopener"
>run.cua.ai&lt;/a> 提供按需供应的隔离云桌面：Fleet 维护一个 sandbox 容量池，代码从池里&amp;quot;认领&amp;quot;桌面，通过 &lt;strong>Sandbox SDK&lt;/strong> 执行命令、截图、与应用交互，用完即毁。底层是 Kubernetes 式的集群配置和 Image API（CRD 契约），适合批量任务和数据生成。&lt;/p>
&lt;h3 id="4-cua-bench--评测与训练">4. Cua Bench —— 评测与训练
&lt;/h3>&lt;p>构建 computer-use 任务、评估 Agent 表现、&lt;strong>导出操作轨迹用于训练和数据生成&lt;/strong>（站点 &lt;a class="link" href="https://cuabench.ai" target="_blank" rel="noopener"
>cuabench.ai&lt;/a>）。支持两种模式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>模拟任务&lt;/strong>：不需要 VM、Docker 甚至模型 API key 就能跑；&lt;/li>
&lt;li>&lt;strong>浏览器任务&lt;/strong>：基于 Playwright Chromium。&lt;/li>
&lt;/ul>
&lt;p>对做 Agent 研究和微调数据的人来说，这部分是宝藏。&lt;/p>
&lt;h2 id="三安装与权限">三、安装与权限
&lt;/h2>&lt;h3 id="安装-driver">安装 Driver
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># macOS / Linux&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://cua.ai/driver/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Windows (PowerShell)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">irm https://cua.ai/driver/install.ps1 &lt;span class="p">|&lt;/span> iex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装后先验证驱动能看到桌面：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cua-driver --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver call list_apps
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="macos-权限">macOS 权限
&lt;/h3>&lt;p>macOS 上必须授予**辅助功能（Accessibility）&lt;strong>和&lt;/strong>屏幕录制（Screen Recording）**权限：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cua-driver permissions status
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="权限模式standard-vs-bounded">权限模式：standard vs bounded
&lt;/h3>&lt;p>这是 Cua 安全模型里很关键的设计。注册 MCP server 时&lt;strong>并不决定权限&lt;/strong>，权限由持有 driver 运行时的进程在启动时决定：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>standard 模式&lt;/strong>：Agent 可以对桌面上&lt;strong>所有应用&lt;/strong>执行输入操作；&lt;/li>
&lt;li>&lt;strong>bounded 模式&lt;/strong>：通过能力清单（capability manifest）限定 Agent 只能触碰&lt;strong>审查过的应用、来源和目录&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Windows/Linux 可用环境变量指定模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CUA_DRIVER_PERMISSION_MODE&lt;/span>&lt;span class="o">=&lt;/span>bounded &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>&lt;span class="nv">CUA_DRIVER_CAPABILITY_MANIFEST_FILE&lt;/span>&lt;span class="o">=&lt;/span>/path/to/manifest &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>&lt;span class="nv">CUA_DRIVER_CAPABILITY_MANIFEST_APPROVED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> cua-driver mcp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Agent 自己&lt;strong>无法&lt;/strong>通过工具调用扩大权限，这个边界必须由用户在启动时画好。&lt;/p>
&lt;h3 id="本地虚拟化可选">本地虚拟化（可选）
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://cua.ai/lume/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="四接入你的-agent">四、接入你的 Agent
&lt;/h2>&lt;p>Cua Driver 内置 MCP server，所有支持 MCP 的客户端都能接。官方甚至提供了配置生成器：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cua-driver mcp-config --client &amp;lt;client&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="claude-code">Claude Code
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">claude mcp add --transport stdio cua-driver -- cua-driver mcp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">claude mcp list &lt;span class="c1"># 确认已连接&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="cursor">Cursor
&lt;/h3>&lt;p>把生成的 JSON 粘到 &lt;code>~/.cursor/mcp.json&lt;/code>（或项目级 &lt;code>.cursor/mcp.json&lt;/code>）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;cua-driver&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cua-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mcp&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="codex">Codex
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cua-driver mcp-config --client codex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 会输出使用绝对路径的注册命令，避免 PATH 问题：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">codex mcp add cua-driver -- /Users/you/.local/bin/cua-driver mcp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="任意-mcp-客户端通用-json">任意 MCP 客户端（通用 JSON）
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;cua-driver&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cua-driver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mcp&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此外还有 OpenCode、Qwen Code、Prime Agent、OpenClaw 等一长串官方适配，完整的客户端清单见 &lt;a class="link" href="https://cua.ai/docs/how-to-guides/driver/connect-your-agent" target="_blank" rel="noopener"
>官方文档&lt;/a>。&lt;/p>
&lt;h2 id="五第一个任务让-agent-算-6--7">五、第一个任务：让 Agent 算 6 × 7
&lt;/h2>&lt;p>官方入门教程的任务很朴素：让 Agent 打开计算器，算出 &lt;strong>6 × 7&lt;/strong>，并&lt;strong>验证界面显示 42&lt;/strong>。&lt;/p>
&lt;p>装好 Driver 并接入 Claude Code 后，直接说人话就行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">用 cua-driver 打开计算器，计算 6 × 7，
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">截图确认结果显示 42。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Agent 会自主完成：定位应用 → 激活窗口 → 语义/快捷键输入 → 截图验证。全过程你只负责最后看一眼结果。&lt;/p>
&lt;h3 id="实测记录从安装到-42">实测记录：从安装到 42
&lt;/h3>&lt;p>笔者在 Apple Silicon Mac（macOS）上完整跑了一遍这条链路（不接 MCP、直接走官方 CLI &lt;code>cua-driver call&lt;/code>，与 MCP 是同一个驱动内核），最终截图确认计算器显示 &lt;strong>6×7 = 42&lt;/strong>。实际执行序列：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 安装（装到 /Applications/CuaDriver.app，symlink 到 ~/.local/bin）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://cua.ai/driver/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 授权：一条命令搞定三项权限（弹出系统对话框，点击确认即可）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver permissions grant
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver permissions status &lt;span class="c1"># Accessibility / Screen Recording / Direct Capture 全 ✅&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 执行任务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver call launch_app --args &lt;span class="s1">&amp;#39;{&amp;#34;bundle_id&amp;#34;: &amp;#34;com.apple.calculator&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver call bring_to_front --args &lt;span class="s1">&amp;#39;{&amp;#34;pid&amp;#34;: &amp;lt;PID&amp;gt;, &amp;#34;window_id&amp;#34;: &amp;lt;WID&amp;gt;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 通过 AX 树点击按钮：&amp;#39;6&amp;#39; → &amp;#39;Multiply&amp;#39; → &amp;#39;7&amp;#39; → &amp;#39;Equals&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cua-driver call get_desktop_state --args &lt;span class="s1">&amp;#39;{}&amp;#39;&lt;/span> &lt;span class="c1"># 截屏验证：6×7 = 42 ✅&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>看似简单，实际踩了三个坑，每一个都值得写进 Agent 的操作手册：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>坑&lt;/th>
&lt;th>现象&lt;/th>
&lt;th>原因与解法&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ambiguous_window_target&lt;/code>&lt;/td>
&lt;td>所有 click / press_key / bring_to_front 被&lt;strong>拒绝执行&lt;/strong>&lt;/td>
&lt;td>Calculator 有 5 个窗口（含离屏辅助窗口），driver 拒绝猜测目标。所有操作必须显式传 &lt;code>window_id&lt;/code>——这是刻意的安全设计&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>element_token&lt;/code> 快照绑定&lt;/td>
&lt;td>第二次用同一 token 点击，&lt;strong>静默失效&lt;/strong>&lt;/td>
&lt;td>&lt;code>get_window_state&lt;/code> 每次返回新 snapshot，token 与快照绑定。必须&lt;strong>每次点击前重新拉取&lt;/strong> AX 树取新 token&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>zoom&lt;/code> 返回陈旧帧&lt;/td>
&lt;td>明明点击成功了，截图里显示屏却&amp;quot;是空的&amp;quot;&lt;/td>
&lt;td>&lt;code>zoom&lt;/code> 可能命中缓存帧；用 &lt;code>get_desktop_state&lt;/code> 全屏新鲜捕获再裁剪，才反映真实状态&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>还有一个反直觉的发现：&lt;code>press_key&lt;/code> 走合成键盘事件（synthetic events）对 Calculator &lt;strong>不生效&lt;/strong>（返回 &lt;code>unverifiable&lt;/code>，实际未注册输入），而 &lt;strong>AX 元素点击（accessibility 路线）一次就中&lt;/strong>。这正好印证了 Cua &amp;ldquo;语义优先于像素坐标&amp;quot;的设计——能用无障碍树就别模拟键盘鼠标，又快又稳。&lt;/p>
&lt;p>另外两个值得知道的细节：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>遥测默认开启&lt;/strong>：安装器提示默认收集匿名 ID 和无内容用法统计，介意就 &lt;code>cua-driver telemetry disable&lt;/code>；&lt;/li>
&lt;li>&lt;strong>验证要靠&amp;quot;新鲜眼&amp;rdquo;&lt;/strong>：driver 的 &lt;code>verify_state&lt;/code> 支持对精确窗口做确定性断言（谓词 AND 组合、连续采样防抖），Agent 验证结果应优先用它，而不是自己截图目测。&lt;/li>
&lt;/ul>
&lt;p>其他官方教程场景：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>组件&lt;/th>
&lt;th>场景&lt;/th>
&lt;th>教程&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Fleets&lt;/td>
&lt;td>供应云桌面 → 跑 &lt;code>uname -a&lt;/code> → 截图 → 释放资源&lt;/td>
&lt;td>&lt;a class="link" href="https://cua.ai/docs/tutorials/your-first-cloud-fleet" target="_blank" rel="noopener"
>Your first Cloud Fleet&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Driver&lt;/td>
&lt;td>计算器算 6×7 并验证&lt;/td>
&lt;td>&lt;a class="link" href="https://cua.ai/docs/tutorials/drive-your-first-app" target="_blank" rel="noopener"
>Drive your first app&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lume&lt;/td>
&lt;td>从 Apple 恢复镜像创建 macOS VM → SSH 连接&lt;/td>
&lt;td>&lt;a class="link" href="https://cua.ai/docs/tutorials/create-your-first-lume-vm" target="_blank" rel="noopener"
>Create your first Lume VM&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Bench&lt;/td>
&lt;td>创建模拟任务 → 跑参考解法 → 验证得分 1.0&lt;/td>
&lt;td>&lt;a class="link" href="https://cua.ai/docs/tutorials/your-first-cua-bench-task" target="_blank" rel="noopener"
>Your first Cua Bench task&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="六横向对比该选哪个">六、横向对比：该选哪个？
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项目&lt;/th>
&lt;th>Stars&lt;/th>
&lt;th>形态&lt;/th>
&lt;th>强项&lt;/th>
&lt;th>适合&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>trycua/cua&lt;/strong>&lt;/td>
&lt;td>22.6k&lt;/td>
&lt;td>驱动 + MCP + VM + 云 + 评测&lt;/td>
&lt;td>桌面级控制、低延迟后台操作、Mac 支持最好&lt;/td>
&lt;td>给 Claude Code/Codex 等编码 Agent 加&amp;quot;手&amp;quot;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://github.com/browser-use/browser-use" target="_blank" rel="noopener"
>browser-use&lt;/a>&lt;/td>
&lt;td>114k&lt;/td>
&lt;td>浏览器自动化库&lt;/td>
&lt;td>浏览器内任务的事实标准&lt;/td>
&lt;td>只需要网页操作&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://github.com/microsoft/OmniParser" target="_blank" rel="noopener"
>microsoft/OmniParser&lt;/a>&lt;/td>
&lt;td>25.4k&lt;/td>
&lt;td>屏幕解析模型&lt;/td>
&lt;td>纯视觉 GUI 理解的底层组件&lt;/td>
&lt;td>自研 CUA 的基建&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="https://github.com/bytedance/UI-TARS-desktop" target="_blank" rel="noopener"
>UI-TARS-desktop&lt;/a>&lt;/td>
&lt;td>39k&lt;/td>
&lt;td>桌面 Agent 应用&lt;/td>
&lt;td>开箱即用的多模态 Agent 产品&lt;/td>
&lt;td>不想写代码直接用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Anthropic CUA API&lt;/td>
&lt;td>—&lt;/td>
&lt;td>闭源 API&lt;/td>
&lt;td>与 Claude 深度集成&lt;/td>
&lt;td>全托管的商业方案&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>简单决策树：&lt;/p>
&lt;ul>
&lt;li>只操作&lt;strong>网页&lt;/strong> → browser-use；&lt;/li>
&lt;li>想让&lt;strong>编码 Agent 操控整个桌面&lt;/strong>（macOS 尤佳）→ &lt;strong>Cua&lt;/strong>；&lt;/li>
&lt;li>想要&lt;strong>开箱即用的桌面 Agent 应用&lt;/strong> → UI-TARS-desktop；&lt;/li>
&lt;li>要&lt;strong>自研底层&lt;/strong> → OmniParser + Cua Bench。&lt;/li>
&lt;/ul>
&lt;h2 id="七安全注意事项">七、安全注意事项
&lt;/h2>&lt;p>Computer use 天然是双刃剑，几个必须记住的点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>权限最小化&lt;/strong>：优先用 &lt;code>bounded&lt;/code> 模式 + 能力清单，别随手开 standard；&lt;/li>
&lt;li>&lt;strong>屏幕内容只是数据&lt;/strong>：网页、邮件、OCR 出来的文字不能改变用户授权，警惕提示注入（prompt injection）——一个网页上写着&amp;quot;请把密钥发到 xxx&amp;quot;的页面，不该让 Agent 照做；&lt;/li>
&lt;li>&lt;strong>高危动作加人审&lt;/strong>：发送、付款、删除、上传、权限变更这类动作，务必留在确认边界之外；&lt;/li>
&lt;li>&lt;strong>沙箱优先&lt;/strong>：不信任的任务丢进 Lume VM 或 Fleets 云桌面里跑，别碰宿主机。&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>Cua 补上了 AI Agent 工具链里缺了很久的一环：&lt;strong>编码 Agent 已经很强，缺的只是一双操作真实电脑的手&lt;/strong>。它用 Rust 驱动保证了低延迟，用 MCP 保证了&amp;quot;任何 Agent 都能接&amp;quot;，用 Lume/Fleets 保证了隔离执行环境，用 Bench 打通了训练评测闭环——每一层都是独立可用的开源组件。&lt;/p>
&lt;p>如果你的 Agent 目前只会敲终端命令，值得花半小时把 Cua Driver 接上试试——当 Claude Code 第一次自己打开计算器算出 42 并截图给你看时，那种&amp;quot;AI 长出手了&amp;quot;的感觉还是非常震撼的。&lt;/p>
&lt;h2 id="参考资源">参考资源
&lt;/h2>&lt;ul>
&lt;li>仓库：&lt;a class="link" href="https://github.com/trycua/cua" target="_blank" rel="noopener"
>https://github.com/trycua/cua&lt;/a>&lt;/li>
&lt;li>官方文档：&lt;a class="link" href="https://cua.ai/docs" target="_blank" rel="noopener"
>https://cua.ai/docs&lt;/a>&lt;/li>
&lt;li>Agent 接入指南：&lt;a class="link" href="https://cua.ai/docs/how-to-guides/driver/connect-your-agent" target="_blank" rel="noopener"
>https://cua.ai/docs/how-to-guides/driver/connect-your-agent&lt;/a>&lt;/li>
&lt;li>云桌面：&lt;a class="link" href="https://run.cua.ai" target="_blank" rel="noopener"
>https://run.cua.ai&lt;/a>&lt;/li>
&lt;li>评测基准：&lt;a class="link" href="https://cuabench.ai" target="_blank" rel="noopener"
>https://cuabench.ai&lt;/a>&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>数据说明：文中 star 数截至 2026-09-14。Cua 主项目为 MIT 协议；可选的 &lt;code>cua-agent[omni]&lt;/code> 依赖 ultralytics（AGPL-3.0），商用集成时注意区分。&lt;/p>
&lt;/blockquote></description></item><item><title>AG-UI：当 Agent 学会了和前端说话</title><link>https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/</link><pubDate>Thu, 10 Sep 2026 14:30:00 +0800</pubDate><guid>https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/</guid><description>&lt;img src="https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/images/index/index.svg" alt="Featured image of post AG-UI：当 Agent 学会了和前端说话" />&lt;p>最近在给自己的 Agent Runtime 设计输入输出契约时，我在 README 里画了一张三方对照表：我定义的事件类型一列、Runtime 原生事件一列，第三列空着——那是留给 &lt;a class="link" href="https://github.com/ag-ui-protocol/ag-ui" target="_blank" rel="noopener"
>AG-UI&lt;/a> 的。填完这张表之后我发现，AG-UI 值得不只是一列，它值得被完整讲一遍。为了写这篇文章，我把官方仓库、Python SDK 源码和 PyPI/npm 的发布记录都翻了一遍，下面带着版本号讲。&lt;/p>
&lt;h2 id="先看仓库现状2026-09-10">先看仓库现状（2026-09-10）
&lt;/h2>&lt;p>先把事实钉住，本文所有描述都对应这个版本：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>值&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>仓库&lt;/td>
&lt;td>&lt;a class="link" href="https://github.com/ag-ui-protocol/ag-ui" target="_blank" rel="noopener"
>&lt;code>github.com/ag-ui-protocol/ag-ui&lt;/code>&lt;/a>，创建于 2025-05-07&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>文档&lt;/td>
&lt;td>&lt;a class="link" href="https://docs.ag-ui.com/" target="_blank" rel="noopener"
>docs.ag-ui.com&lt;/a>（draft spec + SDK 参考）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>热度&lt;/td>
&lt;td>&lt;strong>15.8k stars / 1.4k forks&lt;/strong>，MIT License&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>本文对应 commit&lt;/td>
&lt;td>&lt;code>main@5f32a64ee999&lt;/code>（2026-09-09）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Python SDK&lt;/td>
&lt;td>&lt;a class="link" href="https://pypi.org/project/ag-ui-protocol/" target="_blank" rel="noopener"
>&lt;code>ag-ui-protocol&lt;/code>&lt;/a> &lt;strong>0.1.22&lt;/strong>（PyPI，2026-08-31 发布，首版 0.1.4 于 2025-04-30）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TypeScript SDK&lt;/td>
&lt;td>&lt;a class="link" href="https://www.npmjs.com/package/@ag-ui/core" target="_blank" rel="noopener"
>&lt;code>@ag-ui/core&lt;/code>&lt;/a> 及 client/encoder/proto 系列 &lt;strong>0.0.59&lt;/strong>（npm latest，另有 canary/alpha 通道）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>发布节奏&lt;/td>
&lt;td>几乎&lt;strong>每日&lt;/strong>切一次 release（2026-08-20 到 09-09 切了 5 个），协议仍处 draft 阶段&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Python 包只依赖 &lt;code>pydantic&amp;gt;=2.11.2&lt;/code>，一个依赖，克制得不像一个协议实现。&lt;/p>
&lt;h2 id="三层协议的最后一块">三层协议的最后一块
&lt;/h2>&lt;p>把 2024-2026 这两年 agent 协议的版图摊开，会发现一个很规整的分层：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>MCP&lt;/strong>（Anthropic）：agent ↔ 工具。让模型能标准地&amp;quot;伸手拿东西&amp;quot;。&lt;/li>
&lt;li>&lt;strong>A2A&lt;/strong>（Google）：agent ↔ agent。让多个 agent 标准地互相委派任务。&lt;/li>
&lt;li>&lt;strong>AG-UI&lt;/strong>（CopilotKit）：agent ↔ 前端。让 agent 的运行过程标准地&amp;quot;演给用户看&amp;quot;。&lt;/li>
&lt;/ul>
&lt;p>前两层这几年讨论得足够多了，第三层却长期处于&amp;quot;每家自己造&amp;quot;的状态：LangGraph 自己定义 stream mode，OpenAI Assistants 自己定义 run steps，各家 ChatUI 各自解析各家的事件格式。你写一个 agent 后端，想换个前端，事件层的适配就得重写一遍；你写一个聊天前端，想接不同的 agent，每种 agent 的 SSE 格式都得单独处理。&lt;/p>
&lt;p>AG-UI 的野心就是把这层标准化掉。它不是委员会里设计出来的协议——它是从 CopilotKit 这个产品里长出来的：CopilotKit 做了几年&amp;quot;把 agent 嵌进 React 应用&amp;quot;这件事，攒够了事件类型的实战样本，回头把内部格式提炼成了开放协议，初始合作伙伴是 LangChain（LangGraph）和 CrewAI。这一点从仓库结构里看得见：&lt;code>integrations/&lt;/code> 目录下有 21 个条目——18 个框架适配层、2 个 server starter 模板加一个 community 目录；&lt;code>apps/&lt;/code> 里挂着 dojo（官方演示站）和一个 CLI 示例应用。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/images/three-protocol-layers.svg"
loading="lazy"
alt="三层 Agent 协议:各管一个边界"
>&lt;/p>
&lt;h2 id="仓库解剖一个协议-monorepo-长什么样">仓库解剖：一个协议 monorepo 长什么样
&lt;/h2>&lt;p>官方仓库是一个 pnpm + nx 的 monorepo，顶层结构本身就是协议生态的切片：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ag-ui/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── sdks/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── python/ # ag-ui-protocol 0.1.22 (core + encoder)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── typescript/ # @ag-ui/core|client|encoder|proto 0.0.59 + cli
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── dotnet/ # 官方 .NET SDK
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── community/ # dart / rust / ruby / c++ / kotlin / go / java ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── integrations/ # 20 个框架适配层(见后文生态一节)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── middlewares/ # a2a / a2ui / mcp / mcp-apps / event-throttle
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── apps/ # dojo(官方演示) + client-cli-example
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── docs/ # 文档源
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>两个细节值得注意：&lt;strong>middlewares 是独立一层&lt;/strong>——A2A 桥接、MCP 桥接、MCP Apps、A2UI、事件节流各占一个包，说明协议把&amp;quot;和别的协议对接&amp;quot;也当成了标准件而不是用户自理；&lt;strong>protocol 有 TypeScript 和 Python 两套官方实现&lt;/strong>，且明确以 TypeScript 为参考实现（源码注释里直接写着 TS 的行为是其他 SDK 对齐的基准）。&lt;/p>
&lt;h2 id="事件流是唯一事实源">事件流是唯一事实源
&lt;/h2>&lt;p>AG-UI 的核心设计只有一句话：&lt;strong>agent 的一次运行 = 一条有序事件流，前端状态完全由事件序列归约得到&lt;/strong>。&lt;/p>
&lt;p>没有独立的 REST 查询接口，没有&amp;quot;拉取当前状态&amp;quot;的端点。前端想知道 agent 在干什么？订阅事件流，逐条归约。想在断线后恢复？重新拿一遍事件（或快照），重放。这个思想和事件溯源（Event Sourcing）一脉相承，也和我自己的 Canonical Run 契约不谋而合——事件即事实，投影即状态。&lt;/p>
&lt;p>协议在 0.1.22 的定义里共有 &lt;strong>36 个事件枚举成员&lt;/strong>——31 个在用，加上 5 个已废弃的 &lt;code>THINKING_*&lt;/code>（1.0.0 移除）。全部继承自一个四字段信封：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;TEXT_MESSAGE_CONTENT&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1730000000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rawEvent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;metadata&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>按功能分组看一眼全集，就能感受到协议想覆盖的交互光谱：&lt;/p>
&lt;p>&lt;strong>生命周期（5 种）&lt;/strong>——&lt;code>RUN_STARTED&lt;/code>、&lt;code>RUN_FINISHED&lt;/code>、&lt;code>RUN_ERROR&lt;/code> 管起止，&lt;code>STEP_STARTED&lt;/code>/&lt;code>STEP_FINISHED&lt;/code> 标记过程里的小步。注意 run 的两个标识 &lt;code>threadId&lt;/code> 和 &lt;code>runId&lt;/code> 都是客户端生成的，服务端在 &lt;code>RUN_STARTED&lt;/code> 里回显——这和 OpenAI 式&amp;quot;服务端生成 ID 返回给你&amp;quot;正好相反。&lt;/p>
&lt;p>&lt;strong>文本消息（4 种）&lt;/strong>——经典三段式 &lt;code>TEXT_MESSAGE_START&lt;/code> → &lt;code>TEXT_MESSAGE_CONTENT&lt;/code>（带 &lt;code>delta&lt;/code>）→ &lt;code>TEXT_MESSAGE_END&lt;/code>，外加一个 &lt;code>TEXT_MESSAGE_CHUNK&lt;/code> 快捷形式：省掉配对开销，客户端自己展开。消息靠 &lt;code>messageId&lt;/code> 关联。&lt;/p>
&lt;p>&lt;strong>工具调用（5 种）&lt;/strong>——&lt;code>TOOL_CALL_START&lt;/code>（带 &lt;code>toolCallId&lt;/code> 和 &lt;code>toolCallName&lt;/code>）→ &lt;code>TOOL_CALL_ARGS&lt;/code>（参数 JSON 增量下发，支持流式渲染参数）→ &lt;code>TOOL_CALL_END&lt;/code>，加上 &lt;code>TOOL_CALL_RESULT&lt;/code> 和 &lt;code>TOOL_CALL_CHUNK&lt;/code>。&lt;/p>
&lt;p>&lt;strong>共享状态（3 种）&lt;/strong>——这是 AG-UI 最有辨识度的部分。&lt;code>STATE_SNAPSHOT&lt;/code> 全量下发一份 typed state，之后 &lt;code>STATE_DELTA&lt;/code> 用 &lt;strong>RFC 6902 JSON Patch&lt;/strong> 增量同步。这意味着前端不只是&amp;quot;看&amp;quot;agent 生成文本，而是和 agent 共享一份可编辑的应用状态——agent 改了表单、改了画布、改了文档大纲，前端实时跟着变。CopilotKit 的生成式 UI 就建立在这上面。&lt;/p>
&lt;p>&lt;strong>活动消息（2 种）&lt;/strong>——&lt;code>ACTIVITY_SNAPSHOT&lt;/code>/&lt;code>ACTIVITY_DELTA&lt;/code>，聊天消息之间的结构化进度材料，为 generative UI 准备的通道。&lt;/p>
&lt;p>&lt;strong>推理（7 种）&lt;/strong>——&lt;code>REASONING_*&lt;/code> 一族把思维链流式透出（&lt;code>REASONING_MESSAGE_CONTENT&lt;/code> 的 &lt;code>delta&lt;/code>），甚至有 &lt;code>REASONING_ENCRYPTED_VALUE&lt;/code> 用于加密透传私有推理。另有 5 个旧的 &lt;code>THINKING_*&lt;/code> 事件已废弃、1.0.0 移除。&lt;/p>
&lt;p>&lt;strong>子代理（3 种）&lt;/strong>——&lt;code>SUBAGENT_STARTED&lt;/code>/&lt;code>FINISHED&lt;/code>/&lt;code>ERROR&lt;/code>，用 &lt;code>subagentRunId&lt;/code> + &lt;code>parentSubagentRunId&lt;/code> 构成树。有个字段很说明问题：&lt;code>SUBAGENT_STARTED&lt;/code> 带可选的 &lt;code>parentToolCallId&lt;/code>，直接支持&amp;quot;agent-as-tool&amp;quot;模式——子代理是父代理调用的一个工具（deep agents 的 &lt;code>task&lt;/code> 就是这个模式），事件树和工具调用树是同一棵。&lt;/p>
&lt;p>&lt;strong>特殊（2 种）&lt;/strong>——&lt;code>RAW&lt;/code>（底层框架事件透传）和 &lt;code>CUSTOM&lt;/code>（自定义事件通道）。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/images/event-stream-model.svg"
loading="lazy"
alt="事件流是唯一事实源:BaseEvent 信封与 36 个枚举的九个功能组"
>&lt;/p>
&lt;h2 id="能力声明协议里藏着一份agent-名片">能力声明：协议里藏着一份&amp;quot;agent 名片&amp;quot;
&lt;/h2>&lt;p>读 Python SDK 源码时的一个意外发现：除了事件和输入类型，&lt;code>ag_ui/core/&lt;/code> 下还有一个 &lt;code>capabilities.py&lt;/code>（415 行），定义了一整套 &lt;strong>&lt;code>AgentCapabilities&lt;/code> 能力声明体系&lt;/strong>，十个分类：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>分类&lt;/th>
&lt;th>回答的问题&lt;/th>
&lt;th>典型字段&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>identity&lt;/code>&lt;/td>
&lt;td>你是谁&lt;/td>
&lt;td>name / type(框架标识) / version / provider / documentation_url&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>transport&lt;/code>&lt;/td>
&lt;td>怎么连你&lt;/td>
&lt;td>streaming / websocket / http_binary / push_notifications / &lt;strong>resumable&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tools&lt;/code>&lt;/td>
&lt;td>你能调什么&lt;/td>
&lt;td>supported / 自带工具清单(完整 JSON Schema) / parallel_calls / &lt;strong>client_provided&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>output&lt;/code>&lt;/td>
&lt;td>你产出什么&lt;/td>
&lt;td>structured_output / supported_mime_types&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>state&lt;/code>&lt;/td>
&lt;td>你的状态怎么管&lt;/td>
&lt;td>snapshots / deltas / &lt;strong>memory&lt;/strong>(跨会话记忆) / persistent_state&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>multi_agent&lt;/code>&lt;/td>
&lt;td>你和谁协作&lt;/td>
&lt;td>delegation / handoffs / sub_agents 清单&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>reasoning&lt;/code>&lt;/td>
&lt;td>你的思考可见吗&lt;/td>
&lt;td>supported / streaming / &lt;strong>encrypted&lt;/strong>(零数据保留模式)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>multimodal&lt;/code>&lt;/td>
&lt;td>什么模态进、什么出&lt;/td>
&lt;td>input: image/audio/video/pdf/file; output: image/audio&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>execution&lt;/code>&lt;/td>
&lt;td>你的执行边界&lt;/td>
&lt;td>code_execution / sandboxed / max_iterations / max_execution_time&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>human_in_the_loop&lt;/code>&lt;/td>
&lt;td>人在哪介入&lt;/td>
&lt;td>approvals / interventions / feedback / &lt;strong>interrupts&lt;/strong> / approve_with_edits&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>关键语义写在 docstring 里：&lt;strong>&amp;ldquo;所有字段可选，缺省意味着未声明（unknown），不等于不支持&amp;rdquo;&lt;/strong>——典型的开放协议渐进声明风格；外加 &lt;code>custom&lt;/code> 逃生舱给集成特有能力。这份能力体系和我之前给自己协议设计 discovery 契约（&lt;code>RuntimeDescriptor&lt;/code>）时想解决的问题一模一样：让调用方在运行前就知道对端有什么。AG-UI 的答案粒度更细——它同时服务于&amp;quot;agent 市场、发现 UI、调试&amp;quot;三类消费场景，甚至 &lt;code>sub_agents&lt;/code> 清单的注释都写着&amp;quot;帮助客户端构建 agent 选择界面&amp;quot;。&lt;/p>
&lt;h2 id="输入全量历史的无状态哲学">输入：全量历史的无状态哲学
&lt;/h2>&lt;p>事件流往回走，输入往前送。AG-UI 的请求体 &lt;code>RunAgentInput&lt;/code> 长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">RunAgentInput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">thread_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="c1"># 会话标识&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">run_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="c1"># 本次运行,客户端生成&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent_run_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="c1"># agent-started-agent 场景的父 run&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span> &lt;span class="c1"># 起始共享状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 全量对话历史,按序&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Tool&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 前端工具定义&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Context&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 注入上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">forwarded_props&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span> &lt;span class="c1"># 透传段,中间层不得改动&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">resume&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ResumeEntry&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="c1"># 中断应答&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最有态度的是 &lt;code>messages&lt;/code>：&lt;strong>全量历史回传，按序，无旁路通道&lt;/strong>。服务器不存对话记忆，每一轮前端把完整历史发过来。这是刻意的——无状态服务器好扩展、好重放、好调试，代价是每轮请求变大、长对话依赖客户端裁剪。&lt;/p>
&lt;p>消息定义了七种角色：&lt;code>developer&lt;/code>、&lt;code>system&lt;/code>、&lt;code>assistant&lt;/code>（可带 OpenAI 风格的 &lt;code>tool_calls&lt;/code>）、&lt;code>user&lt;/code>（支持多模态：text/image/audio/video/document，URL 或 base64 inline）、&lt;code>tool&lt;/code>（工具结果）、&lt;code>activity&lt;/code>（渲染材料，发送前必须剥离，不作为恢复历史）、&lt;code>reasoning&lt;/code>。&lt;/p>
&lt;p>人在回路是一个完整闭环：&lt;code>RUN_FINISHED&lt;/code> 的 &lt;code>outcome&lt;/code> 可以是 &lt;code>{type: &amp;quot;interrupt&amp;quot;, interrupts: [...]}&lt;/code>——agent 停下来等输入，且源码里有校验：&lt;strong>interrupt outcome 至少携带一个 interrupt&lt;/strong>，空的直接构造失败。每个 interrupt 带 &lt;code>id&lt;/code>、&lt;code>reason&lt;/code>、可选的 &lt;code>response_schema&lt;/code> 和 &lt;code>expires_at&lt;/code>；客户端下一轮在 &lt;code>resume&lt;/code> 数组里逐条应答（&lt;code>resolved&lt;/code> 或 &lt;code>cancelled&lt;/code>，可带 &lt;code>payload&lt;/code>）——审批、确认、澄清问题，都是这个机制的产品化。能力声明里的 &lt;code>approve_with_edits&lt;/code> 说明连&amp;quot;批准前修改参数&amp;quot;都在协议考虑内。&lt;/p>
&lt;h2 id="从-sdk-源码里读出的工程细节">从 SDK 源码里读出的工程细节
&lt;/h2>&lt;p>我读了 Python SDK（&lt;code>ag-ui-protocol&lt;/code> 0.1.22）的几个核心模块，这几处注释值得单独说：&lt;/p>
&lt;p>&lt;strong>序列化的血泪史&lt;/strong>。基类 &lt;code>ConfiguredBaseModel&lt;/code> 自定义了 serializer：可选字段为 None 时，整个键从线上 JSON 省略，而不是写 &lt;code>null&lt;/code>。注释里明说了缘由——Python SDK 曾是唯一把 null 写上线的 producer，为此协议吃了三个兼容补丁（&lt;code>TOOL_CALL_START.parentMessageId&lt;/code>、&lt;code>TOOL_CALL_CHUNK.parentMessageId&lt;/code>、&lt;code>RUN_FINISHED.outcome&lt;/code>）。类型注释里那句&amp;quot;在基类上统一省略，才能保证每一条序列化路径都生效&amp;quot;是一位工程师被 null 坑过之后的防御性姿势。字段 camelCase 上线（&lt;code>alias_generator=to_camel&lt;/code>），&lt;code>extra=&amp;quot;allow&amp;quot;&lt;/code> 放行未知字段——协议演进不锁死旧 producer。&lt;/p>
&lt;p>&lt;strong>token 计数的跨语言红线&lt;/strong>。&lt;code>token_usage.py&lt;/code> 里有一个细节：所有计数上限被钳在 &lt;strong>2⁵³−1&lt;/strong>——不是 int64 的上限，而是因为&amp;quot;TypeScript 的 protobuf 解码器停在 &lt;code>Number.MAX_SAFE_INTEGER&lt;/code>，这才是各语言绑定之间的真实天花板&amp;quot;。而且注释明确写了这个结构会喂给匿名遥测，&lt;strong>不允许携带任何内容字段&lt;/strong>（无 prompt、无补全、无 thread/run/user ID），只有 provider/model 标签和纯数字。跨语言兼容和隐私边界都焊死在类型定义里。&lt;/p>
&lt;p>&lt;strong>metadata 的克制&lt;/strong>。每个事件都带可选 &lt;code>metadata&lt;/code>，开放键值空间，但保留 &lt;code>&amp;quot;ag-ui&amp;quot;&lt;/code> 键给协议自己用——而且是纯约定，不做运行时强制。注释的原话：验证它的 shape 会&amp;quot;contradict that&amp;quot;（与开放性矛盾）。&lt;/p>
&lt;p>&lt;strong>双编码传输&lt;/strong>。&lt;code>encoder.py&lt;/code> 不只是 SSE——协议定义了二进制编码，媒体类型 &lt;code>application/vnd.ag-ui.event+proto&lt;/code>（protobuf over HTTP），TypeScript 侧有独立的 &lt;code>@ag-ui/proto&lt;/code> 包维护 &lt;code>.proto&lt;/code> 定义。文本 SSE 用于开发调试，二进制用于生产，同一套事件类型两种编码。&lt;/p>
&lt;h2 id="运行时管线一次-run-的完整解剖">运行时管线：一次 run 的完整解剖
&lt;/h2>&lt;p>前面讲的是协议的&amp;quot;静态&amp;quot;部分（类型和字段）。协议真正的工程含量在客户端 SDK 的&lt;strong>运行时管线&lt;/strong>里——一次 &lt;code>runAgent()&lt;/code> 调用背后是一条精心编排的 RxJS 管道。以 TypeScript &lt;code>@ag-ui/client&lt;/code> 0.0.59 的 &lt;code>AbstractAgent.runAgent()&lt;/code>（&lt;code>src/agent/agent.ts&lt;/code>）为例，事件从网络到你的回调要穿过这条流水线：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">run()（子类实现,发 HTTP/SSE 请求）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓ pipe 依次串接:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">① middleware 链 ← reduceRight 组装成洋葱模型
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">② transformChunks ← CHUNK 快捷事件展开成三段式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">③ verifyEvents ← 事件文法状态机校验
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">④ takeUntil(detach$) ← 单次运行的中止信号
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⑤ apply ← 把事件归约进 messages/state
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⑥ processApplyEvents ← 分发到 subscriber 回调
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⑦ catchError / finalize ← 错误与收尾
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>每一层都可以单独讲。这正是&amp;quot;看哪些文件&amp;quot;的答案：读懂这七个环节，你就读懂了 AG-UI 客户端的全部。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/images/runtime-pipeline.svg"
loading="lazy"
alt="一次 run 的运行管线:事件从网络到回调穿过七个环节"
>&lt;/p>
&lt;h3 id="-middleware洋葱模型的拦截器">① Middleware：洋葱模型的拦截器
&lt;/h3>&lt;p>中间件机制定义在 &lt;code>src/middleware/middleware.ts&lt;/code>。核心接口只有一个方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">abstract&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">Middleware&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">abstract&lt;/span> &lt;span class="nx">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>: &lt;span class="kt">RunAgentInput&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">next&lt;/span>: &lt;span class="kt">AbstractAgent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">BaseEvent&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 调下一个 agent,附带 chunk 展开
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">runNext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">transformChunks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 调下一个,且把每一步之后的 messages/state 一起带出来
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">runNextWithState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">EventWithState&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>组装方式是 &lt;code>agent.ts&lt;/code> 里的 &lt;code>reduceRight&lt;/code>——数组里&lt;strong>后注册的中间件先执行&lt;/strong>，&lt;code>next&lt;/code> 指向链上更内层的 agent（最内层是真正 agent 的 &lt;code>run()&lt;/code>）。这就是经典的洋葱模型，和 Koa/Express 中间件一个思想。&lt;/p>
&lt;p>有意思的是 &lt;code>runNextWithState&lt;/code> 的实现：它用一个 &lt;code>ReplaySubject&lt;/code> 把事件喂给 &lt;code>defaultApplyEvents&lt;/code>（事件归约器），让中间件在&lt;strong>每个事件后&lt;/strong>都能拿到&amp;quot;应用了这个事件之后&amp;quot;的 messages 和 state——中间件因此能基于语义状态做决策，而不只是看原始事件。实现里那句 &lt;code>await new Promise(resolve =&amp;gt; setTimeout(resolve, 0))&lt;/code> 是给归约器留一个微任务窗口同步状态，朴素但有效。&lt;/p>
&lt;p>官方仓库 &lt;code>middlewares/&lt;/code> 目录下有 6 个可参考的成品：&lt;code>event-throttle&lt;/code>（按帧率节流 + 合并 delta）、&lt;code>mcp&lt;/code>（把 MCP 工具桥接进事件流）、&lt;code>a2a&lt;/code>（A2A 协议互转）、&lt;code>a2ui&lt;/code>、&lt;code>mcp-apps&lt;/code>，外加一个 &lt;code>middleware-starter&lt;/code> 脚手架。&lt;/p>
&lt;h3 id="-三个值得抄的中间件设计">② 三个值得抄的中间件设计
&lt;/h3>&lt;p>&lt;strong>EventThrottleMiddleware&lt;/strong>（&lt;code>middlewares/event-throttle-middleware/src/index.ts&lt;/code>）——把高频 delta 按时间窗（默认 16ms ≈ 60fps）和最小字符数节流并合并，防止前端被打爆。它的三个设计决策都写满了&amp;quot;为什么&amp;quot;：&lt;/p>
&lt;p>其一，&lt;strong>只对白名单事件做缓冲&lt;/strong>。&lt;code>BUFFERABLE_EVENT_TYPES&lt;/code> 是显式白名单（各种 &lt;code>*_CHUNK&lt;/code>、&lt;code>*_CONTENT&lt;/code>、&lt;code>STATE_*&lt;/code>、&lt;code>ACTIVITY_*&lt;/code>），&lt;strong>不在表内的事件一律立即透传&lt;/strong>。注释原话：&amp;ldquo;这是白名单而非黑名单，这样协议将来新增的事件类型默认走立即透传——对生命周期/边界事件来说，这是更安全的失败模式。&amp;ldquo;新事件宁可多一次渲染，也不能被错误地延迟。&lt;/p>
&lt;p>其二，&lt;strong>合并只在同一 subagent lane 内进行&lt;/strong>。chunk 的合并键是 &lt;code>JSON.stringify([kind, owner, entityId])&lt;/code>——用 JSON 编码而不是分隔符拼接，因为 owner/id 是任意字符串，&amp;ldquo;任何分隔符都可能出现在某个分量里，把两个不同的 (owner, id) 别名到同一个键上&amp;rdquo;。这个注释值得所有写缓存键的人读一遍。&lt;/p>
&lt;p>其三，&lt;strong>metadata 反向合并&lt;/strong>。合并两个 chunk 时，&lt;code>role&lt;/code>/&lt;code>name&lt;/code> 取&lt;strong>第一个&lt;/strong>（它们只出现在首 chunk），但 &lt;code>metadata&lt;/code> 取&lt;strong>最后一个&lt;/strong>并按 key 逐项合并——因为 metadata 是设计成&amp;quot;最后到达&amp;quot;的，携带 usage 和 finish reason。&amp;ldquo;在这里丢掉它会吞掉一个纯 usage 的尾 chunk。&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>FilterToolCallsMiddleware&lt;/strong>（&lt;code>src/middleware/filter-tool-calls.ts&lt;/code>）——按工具名拦截工具调用事件。看起来平平无奇，但注释记了一个真实并发 bug：中间件实例是跨 run 复用的，如果被拦截的 toolCallId 集合放在&lt;strong>实例&lt;/strong>上，&amp;ldquo;一个卡住的 run 的订阅还开着时，下一个 run 启动会抹掉那些还在过滤这个卡住的 run 的 id，于是它被禁的工具的 &lt;code>TOOL_CALL_ARGS&lt;/code>/&lt;code>END&lt;/code>/&lt;code>RESULT&lt;/code> 就开始漏出来了&amp;rdquo;。解法是用 &lt;code>defer&lt;/code> 给&lt;strong>每个订阅&lt;/strong>一份独立集合，并在 &lt;code>RUN_STARTED&lt;/code> 时重置。并发场景下&amp;quot;状态放实例还是放订阅&amp;quot;这个坑，这是教科书级的案例。&lt;/p>
&lt;p>&lt;strong>四个 BackwardCompatibility 中间件&lt;/strong>（&lt;code>src/middleware/backward-compatibility-0-0-*.ts&lt;/code>）——这是我见过最优雅的协议演进手法。agent 构造时读对端声明的 &lt;code>maxVersion&lt;/code>，按版本号&lt;strong>自动插到中间件链最前面&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">compareVersions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">maxVersion&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;0.0.39&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">middlewares&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unshift&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">BackwardCompatibility_0_0_39&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">compareVersions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">maxVersion&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;0.0.57&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 0.0.57 之前的 agent 不认识 subagent:剥掉 subagentRunId、丢弃 SUBAGENT_* 事件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">middlewares&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unshift&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">BackwardCompatibility_0_0_57&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>0.0.45&lt;/code> 中间件把废弃的 &lt;code>THINKING_*&lt;/code> 事件翻译成新的 &lt;code>REASONING_*&lt;/code>，&lt;code>0.0.47&lt;/code> 把老的 &lt;code>BinaryInputContent&lt;/code> 映射成新的分模态输入类型，&lt;code>0.0.57&lt;/code> 给不认识 subagent 的老客户端剥掉归属字段。&lt;strong>每个协议破坏性变更都固化为一个可测试、可移除的适配中间件&lt;/strong>，而不是散落在业务代码里的 if-else。协议处于日更的 draft 阶段还能保持向后兼容，靠的就是这套机制。&lt;/p>
&lt;h3 id="-verifyevents1052-行的事件文法状态机">③ verifyEvents：1052 行的事件文法状态机
&lt;/h3>&lt;p>&lt;code>src/verify/verify.ts&lt;/code> 是整个客户端最硬核的文件——它把&amp;quot;合法的 AG-UI 事件流&amp;quot;编码成了一个状态机，逐事件校验。维护四组&amp;quot;哪些实体还开着&amp;quot;的集合（文本消息、工具调用、推理、活动），外加每个实体类型的&lt;strong>归属映射&lt;/strong>（哪个 subagent 开的）。&lt;/p>
&lt;p>这个文件的注释本身就是一部协议演进史，每条规则背后都是真实 bug：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>为什么 owners 按实体类别分桶&lt;/strong>：一个 message 和一个 tool call 可以都叫 &amp;ldquo;x&amp;rdquo; 而不冲突。早期单个桶时，&amp;ldquo;tool call 的写入会覆盖 message 的 owner，导致后续一个加密值拿了错误的 owner 做校验而被接受&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>为什么 owners 关闭后不清除&lt;/strong>：&lt;code>REASONING_ENCRYPTED_VALUE(subtype=&amp;quot;tool-call&amp;quot;)&lt;/code> 合法地要在 &lt;code>TOOL_CALL_END&lt;/code> &lt;strong>之后&lt;/strong>到达，&amp;ldquo;清掉 owner 会让这个不匹配变得无法匹配从而接受一个错误的&amp;rdquo;。&lt;/li>
&lt;li>&lt;strong>为什么 step 要按 (owner, name) 嵌套 Map 而不是拼字符串&lt;/strong>：曾经用分隔符拼接 owner 和 name，结果&amp;quot;没有 owner 的父级会和 subagent id 是空字符串的实体撞键&amp;rdquo;——而空字符串是合法的 opaque id。注释直言：&amp;ldquo;有个设计伙伴从真实的 deepagents run 里报回了这个 bug：合法的嵌套 step 被拒，而非法的跨 owner 关闭却被接受。&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>为什么 &lt;code>STEP_STARTED&lt;/code> 判重按 owner 分&lt;/strong>：父 agent 和子 agent 常常跑同一个图，两边&lt;strong>同时&lt;/strong>有一个叫 &amp;ldquo;tools&amp;rdquo; 的 step 是合法的（父的包着委派，子的是自己的内部工作）——按名字单独判重会把合法嵌套当成错误拒绝。&lt;/li>
&lt;/ul>
&lt;p>这些不是设计文档里能写出来的东西，全是生产流量打磨出来的。想理解 AG-UI 的边界条件，&lt;code>verify.ts&lt;/code> 比 spec 更有信息量。&lt;/p>
&lt;h3 id="-transformchunks快捷事件的双向门">④ transformChunks：快捷事件的双向门
&lt;/h3>&lt;p>&lt;code>src/chunks/transform.ts&lt;/code>（564 行）负责 &lt;code>*_CHUNK&lt;/code> 快捷事件和 &lt;code>*_START/CONTENT/END&lt;/code> 三段式之间的转换。核心概念是 &lt;strong>lane（通道）&lt;/strong>：每个 subagent 一条，&amp;ldquo;一条 lane 上最多有一路正在组装的流，因为 chunk 的简写只靠&amp;rsquo;和之前一样&amp;rsquo;来标识延续&amp;rdquo;。这里也有个精妙的元数据规则——chunk 的 metadata 会扩散到由它合成的每个事件上，&lt;strong>但绝不施加于关闭前一条消息的合成 &lt;code>*_END&lt;/code>&lt;/strong>，&amp;ldquo;这就是防止某个 chunk 的 metadata 泄漏到它正在关闭的那条消息上的原因&amp;rdquo;。&lt;/p>
&lt;h3 id="-apply-与-subscribers归约器和观察者">⑤ apply 与 subscribers：归约器和观察者
&lt;/h3>&lt;p>&lt;code>src/apply/default.ts&lt;/code>（约 1500 行）是事件归约器——一个巨型 &lt;code>switch(event.type)&lt;/code>，把每一种事件映射成 &lt;code>messages&lt;/code>/&lt;code>state&lt;/code> 的变更（&lt;code>AgentStateMutation&lt;/code>）。这是&amp;quot;事件即事实、状态即投影&amp;quot;落地的地方。它对 &lt;code>*_CHUNK&lt;/code> 事件直接抛错（&lt;code>TEXT_MESSAGE_CHUNK must be transformed before being applied&lt;/code>）——这是管线的纪律：chunk 必须先过 &lt;code>transformChunks&lt;/code> 这道门，归约器只认展开后的三段式。&lt;/p>
&lt;p>&lt;code>src/agent/subscriber.ts&lt;/code>（408 行）则是应用层的观察者接口，钩子分三类：生命周期（&lt;code>onRunInitialized&lt;/code>/&lt;code>onRunFailed&lt;/code>/&lt;code>onRunFinalized&lt;/code>）、按事件类型（&lt;code>onTextMessageContentEvent&lt;/code> 连 &lt;code>textMessageBuffer&lt;/code> 都给你拼好了、&lt;code>onToolCallArgsEvent&lt;/code> 带 &lt;code>toolCallBuffer&lt;/code> 和 &lt;code>toolCallName&lt;/code>）、变更通知（&lt;code>onMessagesChanged&lt;/code>/&lt;code>onStateChanged&lt;/code>）。&lt;/p>
&lt;p>这里有个订阅者协议的关键设计：&lt;strong>订阅者不直接改状态，而是返回一个 &lt;code>AgentStateMutation&lt;/code>&lt;/strong>，由 &lt;code>runSubscribersWithMutation&lt;/code> 统一应用。多个订阅者的 mutation 可以 &lt;code>stopPropagation&lt;/code> 拦截后续订阅者——订阅者之间也是中间件式的。实现里还有一段性能注释：dev 环境会对输入做 &lt;code>structuredClone&lt;/code> + &lt;code>deepFreeze&lt;/code> 来抓&amp;quot;原地修改&amp;quot;的 bug，但这个守卫是&amp;quot;每个事件最大的一笔分配&amp;rdquo;，流式大参数时会把 V8 堆打爆，所以它在生产环境关闭、dev 环境下 payload 过大时也跳过——&amp;ldquo;常见的不改状态的事件cost 零次克隆&amp;rdquo;。&lt;/p>
&lt;h2 id="怎么读-ag-ui-的源码一份导航">怎么读 AG-UI 的源码：一份导航
&lt;/h2>&lt;p>如果你要接 AG-UI 或者只是想学它的设计，按这个顺序读最省时间：&lt;/p>
&lt;p>&lt;strong>第一梯队——协议本身（先读，两小时）&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>文件&lt;/th>
&lt;th>读什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>sdks/python/ag_ui/core/events.py&lt;/code>&lt;/td>
&lt;td>全部事件类型（555 行，每个类型的字段和约束都在）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sdks/python/ag_ui/core/types.py&lt;/code>&lt;/td>
&lt;td>&lt;code>RunAgentInput&lt;/code>、七种消息、多模态、中断类型（416 行）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sdks/python/ag_ui/core/capabilities.py&lt;/code>&lt;/td>
&lt;td>能力声明体系（415 行，看注释里的&amp;quot;为什么这样设计&amp;quot;）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>sdks/python/ag_ui/encoder/encoder.py&lt;/code>&lt;/td>
&lt;td>SSE 与二进制编码（很短，看序列化约定）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>第二梯队——客户端运行时（TypeScript &lt;code>packages/client/src/&lt;/code>，深读）&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>文件&lt;/th>
&lt;th>读什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>agent/agent.ts&lt;/code>&lt;/td>
&lt;td>运行管线七环节 + middleware 链组装 + 生命周期（看 &lt;code>runAgent&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>middleware/middleware.ts&lt;/code>&lt;/td>
&lt;td>洋葱模型基类 + &lt;code>runNextWithState&lt;/code> 的状态追踪&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>middleware/backward-compatibility-0-0-*.ts&lt;/code>&lt;/td>
&lt;td>协议演进如何固化成中间件（四份文件一起看）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>verify/verify.ts&lt;/code>&lt;/td>
&lt;td>事件文法状态机（注释里的 bug 案例最值钱）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>apply/default.ts&lt;/code>&lt;/td>
&lt;td>事件归约器（看状态怎么从事件投影出来）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>chunks/transform.ts&lt;/code> + &lt;code>agent/subscriber.ts&lt;/code>&lt;/td>
&lt;td>chunk 双向门 + 订阅者协议&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>第三梯队——生态接线&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>目录&lt;/th>
&lt;th>读什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>integrations/langgraph/{python,typescript}/&lt;/code>&lt;/td>
&lt;td>一个真实框架适配层的全貌（含中断处理、SSE 断线恢复测试）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>middlewares/event-throttle-middleware/&lt;/code>&lt;/td>
&lt;td>生产级中间件的最完整示例（含性能和不变量注释）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>apps/dojo/&lt;/code>&lt;/td>
&lt;td>官方演示站，交互式体验所有事件类型&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一个实用建议：读 TS 客户端时&lt;strong>先读注释再读代码&lt;/strong>——这个仓库的注释密度和坦诚度罕见，&amp;ldquo;我们当初这么写、后来发现 X 有问题、现在的写法是 Y、原因是 Z&amp;quot;的叙事随处可见。很多边界条件 spec 里没有，但注释里有。&lt;/p>
&lt;h2 id="生态谁在用它">生态：谁在用它
&lt;/h2>&lt;p>AG-UI 的知名产品就是创造它的 &lt;a class="link" href="https://www.copilotkit.ai/" target="_blank" rel="noopener"
>CopilotKit&lt;/a>（React 前端 agent 框架，&lt;code>useCopilotAction&lt;/code> 的 human-in-the-loop 模式就是协议里前端工具机制的产品化），加上官方 demo 站 &lt;strong>Dojo&lt;/strong>（&lt;code>apps/dojo&lt;/code>，仓库内）。&lt;/p>
&lt;p>但真正的覆盖面在 &lt;code>integrations/&lt;/code> 目录里，18 个框架适配层的原文清单：LangGraph、LangChain、CrewAI、Microsoft Agent Framework、Google ADK、AWS Strands、Agno、Mastra、Pydantic AI、LlamaIndex、AG2、Vercel AI SDK、watsonx、Langroid、&lt;strong>Claude Agent SDK&lt;/strong>、&lt;strong>Claude Managed Agents&lt;/strong>、A2A、Agent Spec，另有两个 server starter 模板。社区 SDK 覆盖 Dart、Rust、Ruby、C++、Kotlin、Go、Java。客户端不止浏览器——终端、React Native、Slack、Teams 都能当 AG-UI client。&lt;/p>
&lt;p>不过要校准一下预期：AG-UI 的&amp;quot;知名&amp;quot;集中在&lt;strong>框架生态&lt;/strong>，不是终端产品。和 MCP 一样，它是管道协议，终端用户永远看不到它——人们用的是接了它的 CopilotKit 应用和 LangGraph agent。&lt;/p>
&lt;h2 id="和有状态-runtime-的张力">和有状态 Runtime 的张力
&lt;/h2>&lt;p>最后说一个我实际设计契约时撞上的问题。AG-UI 的无状态模型很优雅，但我的 Runtime 是&lt;strong>有状态&lt;/strong>的：对话记忆按 &lt;code>session_id&lt;/code> 存在服务端（SQLite checkpointer），每次 Run 只需要送最新的问题。&lt;/p>
&lt;p>两套范式接在一起时的正确姿势是：AG-UI 前端发全量 &lt;code>messages&lt;/code>，adapter 只取&lt;strong>最后一条 user 消息&lt;/strong>作为 Runtime 的输入，历史由服务端记忆提供；事件流方向则反过来，把 Runtime 的事件投影成 AG-UI 事件喂给前端。适配层的本质是两种会话范式之间的翻译。&lt;/p>
&lt;p>还有几块 AG-UI 有、多数自研契约没有的东西，值得列为后续演进的清单：interrupt/resume 的人在回路（自研契约通常只有&amp;quot;取消&amp;rdquo;，没有&amp;quot;挂起等输入&amp;quot;）、共享状态的 StateSnapshot/StateDelta、子代理事件树、前端工具的反向执行回路，以及那份克制的能力声明体系。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ag-ui%E5%BD%93agent%E5%AD%A6%E4%BC%9A%E4%BA%86%E5%92%8C%E5%89%8D%E7%AB%AF%E8%AF%B4%E8%AF%9D/images/stateful-vs-stateless.svg"
loading="lazy"
alt="两种会话范式对比:AG-UI 的无状态服务器 vs Canonical Runtime 的服务端记忆"
>&lt;/p>
&lt;p>三层协议凑齐之后的图景其实很清晰：MCP 让 agent 拿到工具，A2A 让 agent 找到同伴，AG-UI 让用户看见过程。你的 agent 用哪套实现无所谓——只要它在这三个边界上说标准语言，就同时获得了被所有前端渲染、被所有 agent 编排、被所有工具增强的资格。&lt;/p>
&lt;hr>
&lt;p>&lt;em>本文基于 &lt;code>ag-ui-protocol/ag-ui&lt;/code> main@5f32a64ee999（2026-09-09）、Python SDK ag-ui-protocol 0.1.22、TypeScript SDK @ag-ui/client 0.0.59 写成，源码研读覆盖 &lt;code>sdks/python/ag_ui/core/&lt;/code>（events / types / capabilities / token_usage / encoder）与 &lt;code>sdks/typescript/packages/client/src/&lt;/code>（agent / middleware / verify / apply / chunks / interrupts）、&lt;code>middlewares/event-throttle-middleware/&lt;/code>。协议处于 draft 阶段、日更演进，引用前请核对最新版本。&lt;/em>&lt;/p></description></item><item><title>阿里云百炼联网搜索：三种入口，三种结果，我全都踩了一遍</title><link>https://www.zata.cc/p/%E9%98%BF%E9%87%8C%E4%BA%91%E7%99%BE%E7%82%BC%E8%81%94%E7%BD%91%E6%90%9C%E7%B4%A2%E4%B8%89%E7%A7%8D%E5%85%A5%E5%8F%A3%E4%B8%89%E7%A7%8D%E7%BB%93%E6%9E%9C%E6%88%91%E5%85%A8%E9%83%BD%E8%B8%A9%E4%BA%86%E4%B8%80%E9%81%8D/</link><pubDate>Mon, 07 Sep 2026 19:40:00 +0800</pubDate><guid>https://www.zata.cc/p/%E9%98%BF%E9%87%8C%E4%BA%91%E7%99%BE%E7%82%BC%E8%81%94%E7%BD%91%E6%90%9C%E7%B4%A2%E4%B8%89%E7%A7%8D%E5%85%A5%E5%8F%A3%E4%B8%89%E7%A7%8D%E7%BB%93%E6%9E%9C%E6%88%91%E5%85%A8%E9%83%BD%E8%B8%A9%E4%BA%86%E4%B8%80%E9%81%8D/</guid><description>&lt;img src="https://www.zata.cc/p/%E9%98%BF%E9%87%8C%E4%BA%91%E7%99%BE%E7%82%BC%E8%81%94%E7%BD%91%E6%90%9C%E7%B4%A2%E4%B8%89%E7%A7%8D%E5%85%A5%E5%8F%A3%E4%B8%89%E7%A7%8D%E7%BB%93%E6%9E%9C%E6%88%91%E5%85%A8%E9%83%BD%E8%B8%A9%E4%BA%86%E4%B8%80%E9%81%8D/images/index/index.svg" alt="Featured image of post 阿里云百炼联网搜索：三种入口，三种结果，我全都踩了一遍" />&lt;p>给 Agent 接搜索的时候，我以为阿里云百炼的联网搜索就是「一个开关」。实测下来发现完全不是：同一个模型，至少有三条完全不同的搜索入口——Chat Completions 的 &lt;code>enable_search&lt;/code> 参数、Responses API 的 &lt;code>web_search&lt;/code> 工具、以及 MCP 广场里的联网搜索 MCP。三者的触发方式、来源返回、计费方式都不同，混着看文档很容易得出「阿里云搜索不行」的错误结论，而实际上只是走错了入口。&lt;/p>
&lt;p>这篇文章按我踩坑的顺序写：先说我怎么在错误的入口上浪费了一轮调试，再写正确入口的实测行为，最后把三条路的差异列成表，给一个选型判断。&lt;/p>
&lt;h2 id="一chat-completions--enable_search会假装成功的入口">一、Chat Completions + enable_search：会「假装成功」的入口
&lt;/h2>&lt;p>我的第一版代码走的是 OpenAI-compatible Chat Completions，请求里加了 &lt;code>extra_body={&amp;quot;enable_search&amp;quot;: True}&lt;/code>。请求返回 200，模型照样生成了一段关于 vLLM 的详细回答——看起来一切正常。&lt;/p>
&lt;p>直到我想找引用来源才发现：响应里没有任何搜索相关字段。没有 &lt;code>search_results&lt;/code>，没有 &lt;code>web_search_info&lt;/code>，没有任何元数据。我又加了一堆参数组合（&lt;code>search_options.forced_search&lt;/code>、&lt;code>enable_source&lt;/code> 等文档里能搜到的字段）反复试，结论都一样：&lt;strong>这个私有部署的百炼端点在 Chat Completions 协议下，搜索扩展参数被静默忽略了&lt;/strong>。&lt;/p>
&lt;p>这类问题最麻烦的地方在于它不报错。请求成功、回答流畅，模型甚至会用「根据最新资料」开头——但那全是参数化记忆在演戏。如果你不检查来源字段，很容易把这种回答当成「搜索生效了但质量一般」，然后得出完全错误的结论。&lt;/p>
&lt;p>后来在文档里翻到关键一句：Responses API 暂不支持 &lt;code>enable_source&lt;/code> 等参数，来源要用专门的方式取——这句话反过来提醒我，这些参数本来就属于不同的协议层。Chat Completions 的搜索行为在不同部署形态下表现不一致，而 Responses API 是明确支持联网搜索的入口。&lt;/p>
&lt;h2 id="二responses-api--web_search能跑通的正路">二、Responses API + web_search：能跑通的正路
&lt;/h2>&lt;p>换成 Responses API 之后，接法反而更简单了。不需要任何 &lt;code>enable_search&lt;/code> 之类的开关，直接在 &lt;code>tools&lt;/code> 数组里声明工具：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://&lt;/span>&lt;span class="si">{WorkspaceId}&lt;/span>&lt;span class="s2">.cn-beijing.maas.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen3.7-flash&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;今天的 GitHub trending&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="c1"># 搜索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_extractor&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="c1"># 抓取网页正文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stream&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>两个工具的分工和我在 Agent 里自己包搜索工具时的经验一致：&lt;code>web_search&lt;/code> 负责查，&lt;code>web_extractor&lt;/code> 负责读正文。文档建议再配上 &lt;code>code_interpreter&lt;/code>，但多数搜索场景用不上。&lt;/p>
&lt;p>实测下来有几个值得记录的行为细节。&lt;/p>
&lt;p>**来源不在正文里，在 output 数组里。**提取方式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">item&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;web_search_call&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">source&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sources&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Responses API 目前不支持 &lt;code>enable_source&lt;/code>、&lt;code>enable_citation&lt;/code>、&lt;code>citation_format&lt;/code>，不会在正文里自动插 &lt;code>[1]&lt;/code> 角标。要角标标注得走 DashScope 原生调用——这是三条入口差异最直观的一处：同样的搜索，Responses 给你原始链接列表，DashScope 原生给你排版好的引用。&lt;/p>
&lt;p>&lt;strong>流式事件是真实可用的。&lt;/strong>&lt;code>response.web_search_call.searching&lt;/code> / &lt;code>completed&lt;/code> 标记搜索阶段，&lt;code>response.output_text.delta&lt;/code> 是正文增量。但有一个体验坑：SSE 事件存在批量下发——我实测中 &lt;code>in_progress&lt;/code>、&lt;code>searching&lt;/code>、&lt;code>completed&lt;/code> 三个事件几乎同一时刻到达（23.9s），之前模型明显已经在搜了。如果你做前端 UI，别指望靠这些事件展示即时的「搜索中」状态，最好在请求发出时本地先打一行提示。&lt;/p>
&lt;p>**耗时差距很大。**同一问题「今天的 GitHub trending」（qwen3.7-flash）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>工具组合&lt;/th>
&lt;th>实测耗时&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>只开 &lt;code>web_search&lt;/code>&lt;/td>
&lt;td>约 14 秒&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>web_search + web_extractor&lt;/code>&lt;/td>
&lt;td>约 46–67 秒&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>web_extractor&lt;/code> 会触发多轮网页抓取，模型要读正文再综合，时间翻三四倍。概览类问题用 &lt;code>web_search&lt;/code> 就够；需要精确总结文档内容时再开 extractor。&lt;/p>
&lt;p>**模型的搜索结果也有时效性陷阱。**即便搜索成功，拿它答「今天 GitHub trending」时，返回的项目列表在不同请求间差异很大，星标数明显不可靠。厂商侧搜索解决的是「能拿到网页」，不解决「网页本身可信」——这部分判断仍然要靠你的 prompt 约束。&lt;/p>
&lt;h2 id="三计费别把它当成模型调用的一部分">三、计费：别把它当成模型调用的一部分
&lt;/h2>&lt;p>这是容易被忽略但必须算清的部分。Responses API 的联网搜索费用分两块：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>模型调用费&lt;/strong>：搜索到的网页内容拼进提示词，输入 token 按模型标准价格计费。&lt;/li>
&lt;li>&lt;strong>搜索策略费&lt;/strong>：按调用次数收费。turbo 策略 3 元/千次、max 策略 4 元/千次（2026 年 2 月起正式计费，华北 2 地域）；agent 策略 4 元/千次。&lt;/li>
&lt;/ol>
&lt;p>也就是说一次带搜索的调用，实际成本 = 模型 token 费 + 搜索次数费，而搜索会把大量网页内容塞进上下文，token 那头的增量往往比搜索费本身更贵。&lt;/p>
&lt;p>MCP 广场的「联网搜索 MCP」是另一套独立服务，计费也独立：所有用户前 2000 次调用免费，之后 29 元/千次。它和 Responses API 的内置搜索互不相通——这是很多人（包括我一开始）搞混的地方。&lt;/p>
&lt;h2 id="四能力矩阵三条协议到底差在哪">四、能力矩阵：三条协议到底差在哪
&lt;/h2>&lt;p>把官方的能力表和我的实测合在一起看，差异就清楚了。核心结论只有一句：&lt;strong>DashScope 原生协议支持全部进阶功能，OpenAI 兼容协议（无论 Chat Completions 还是 Responses）都只是阉割版&lt;/strong>。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>功能特性&lt;/th>
&lt;th>DashScope&lt;/th>
&lt;th>OpenAI 兼容 Chat Completions&lt;/th>
&lt;th>OpenAI 兼容 Responses&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>基础联网搜索&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>强制联网搜索&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>搜索量级策略（turbo / max / agent）&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>垂域搜索&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>搜索时效性设置&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>限定搜索来源站点&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>自然语言干预检索范围&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>返回搜索来源&lt;/strong>&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>角标引用标注&lt;/strong>&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>提前返回搜索来源&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>图文混合输出&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这张表解释了我前面踩的所有坑：Chat Completions 能搜但拿不到来源；Responses API 虽然也不支持「来源返回参数」，但通过 output 数组里的 &lt;code>web_search_call.action.sources&lt;/code> 可以把链接提取出来；强制搜索、策略设置、时效性、站点限定这些进阶控制，Responses 一律不支持。&lt;strong>如果你的场景需要来源验收、时间范围约束或站点限定，DashScope 原生协议是唯一完整解。&lt;/strong>&lt;/p>
&lt;h2 id="五search_strategy四个档位一次讲清">五、search_strategy：四个档位，一次讲清
&lt;/h2>&lt;p>搜索策略是比协议选择更影响结果质量的参数。四个档位：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>策略&lt;/th>
&lt;th>行为&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>turbo&lt;/code>（默认）&lt;/td>
&lt;td>兼顾速度与效果&lt;/td>
&lt;td>日常查询&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>max&lt;/code>&lt;/td>
&lt;td>调用多源搜索引擎，结果更全&lt;/td>
&lt;td>高精度、多源交叉验证&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>agent&lt;/code>&lt;/td>
&lt;td>多轮调用搜索工具与大模型，多轮检索与内容整合&lt;/td>
&lt;td>研究报告；英文场景推荐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>agent_max&lt;/code>&lt;/td>
&lt;td>agent 基础上加网页抓取&lt;/td>
&lt;td>仅限 qwen3-max 思考模式&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>几个关键限制值得记下：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>agent&lt;/code> 和 &lt;code>agent_max&lt;/code> 启用时，只支持 &lt;code>enable_source: true&lt;/code>，其他联网搜索功能全部不可用&lt;/strong>——开了多轮检索就牺牲参数控制，这是硬约束。&lt;/li>
&lt;li>&lt;code>agent&lt;/code> 策略对模型有要求：Qwen3-Max 系列、Qwen3.5/3.6/3.7 系列可用；Qwen3.8、MiniMax-M2.1、Moonshot-Kimi-K2 与角色扮演模型不支持。走 text 端点时&lt;strong>必须用流式调用&lt;/strong>。&lt;/li>
&lt;li>不同模型处理时效数据的能力不同：qwen3-max 具备日期推理能力（能识别非交易日并提示无数据），qwen-max 会直接返回搜索到的过期数据。股价等强时效查询选 qwen3-max 或更新版本。&lt;/li>
&lt;/ul>
&lt;p>我的实测数据可以直接对应到档位：只开 &lt;code>web_search&lt;/code>（近似 turbo）约 14 秒；加 &lt;code>web_extractor&lt;/code>（行为上接近 agent_max 的抓取）约 46–67 秒。文档说 agent 策略「响应时间可能更长」不是客气话。&lt;/p>
&lt;h2 id="六联网搜索-mcp第四条路">六、联网搜索 MCP：第四条路
&lt;/h2>&lt;p>MCP 广场的「联网搜索 MCP」和上面三条协议层能力完全独立——独立实现、独立计费，也独立于你选哪条 API 协议。所有用户前 2000 次调用免费，之后 29 元/千次。它适合的场景是：多个 Agent / 多个模型要共享同一个搜索出口，不想每条协议各接一遍。&lt;/p>
&lt;h2 id="七我的判断">七、我的判断
&lt;/h2>&lt;p>如果你在百炼上做 Agent 搜索，我的建议很短：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>先想清楚要什么再选协议&lt;/strong>。需要来源验收、时效控制、站点限定 → DashScope 原生；只要基础搜索且想用 OpenAI SDK → Responses API；Chat Completions 只适合「搜到了就行」、不关心来源的场景。&lt;/li>
&lt;li>&lt;strong>每次搜索调用都验收来源列表&lt;/strong>。响应里没有来源字段或 &lt;code>web_search_call&lt;/code>，就当搜索没发生——不管正文看起来多可信。&lt;/li>
&lt;li>&lt;strong>搜索策略按场景选&lt;/strong>。日常 turbo；研究报告或英文场景上 agent（记得流式）；开了 agent 就别指望还能设时效和站点——它只留了 &lt;code>enable_source&lt;/code>。&lt;/li>
&lt;li>&lt;strong>强时效场景先挑模型&lt;/strong>。qwen3-max 有日期推理，qwen-max 没有；模型选错，策略再对也会拿旧数据当新数据。&lt;/li>
&lt;li>&lt;strong>成本核算要把 token 增量算进去&lt;/strong>。搜索策略费是明码标价的小头，搜索内容撑大的上下文才是大头。&lt;/li>
&lt;/ol>
&lt;p>至于「同一朵云为什么功能入口这么分裂」——Responses 是新一代 Agent 协议，搜索工具按它的 Item 模型设计；Chat Completions 是兼容层，进阶参数在协议上就带不回来。功能不是不存在，是协议层放不下。但文档把三条协议的差异藏在能力表里、又把 MCP 和内置搜索放在同一个页面，踩坑几乎不可避免。希望这篇实测记录帮你少绕一段路。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>百炼的联网搜索不是一个功能，而是四条入口：DashScope 原生、Chat Completions、Responses API、搜索 MCP。能力逐级递减，计费各自独立。&lt;/li>
&lt;li>能力表的关键行是「返回搜索来源」和「角标引用标注」：只有 DashScope 支持。OpenAI 兼容协议能搜但拿不到结构化引用（Responses 靠 output 数组曲线救国）。&lt;/li>
&lt;li>&lt;code>search_strategy&lt;/code> 四档：turbo 快、max 全、agent 多轮整合但锁掉其他参数、agent_max 加抓取且只限 qwen3-max 思考模式。&lt;/li>
&lt;li>强时效场景，模型选择和策略同样重要：qwen3-max 有日期推理，qwen-max 没有。&lt;/li>
&lt;li>搜索类回答的最低验收标准是来源列表；没有来源，再流畅的回答也不能采信。&lt;/li>
&lt;/ul></description></item><item><title>OpenAI Responses API与Chat Completions API区别详解</title><link>https://www.zata.cc/p/openai-responses-api%E4%B8%8Echat-completions-api%E5%8C%BA%E5%88%AB%E8%AF%A6%E8%A7%A3/</link><pubDate>Mon, 07 Sep 2026 18:05:39 +0800</pubDate><guid>https://www.zata.cc/p/openai-responses-api%E4%B8%8Echat-completions-api%E5%8C%BA%E5%88%AB%E8%AF%A6%E8%A7%A3/</guid><description>&lt;img src="https://www.zata.cc/p/openai-responses-api%E4%B8%8Echat-completions-api%E5%8C%BA%E5%88%AB%E8%AF%A6%E8%A7%A3/images/index/index.svg" alt="Featured image of post OpenAI Responses API与Chat Completions API区别详解" />&lt;h2 id="一句话结论">一句话结论
&lt;/h2>&lt;p>Chat Completions 是传统的“聊天消息补全”接口：调用方自己维护完整的消息历史，模型按输入生成一条 assistant 回复。Responses 是 OpenAI 推出的新一代统一接口：不仅支持生成回复，还内置工具调用、Agent 循环、会话状态管理和结构化输出 Item，为构建 Agent 应用而生。&lt;/p>
&lt;h2 id="核心区别总览">核心区别总览
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>Chat Completions&lt;/th>
&lt;th>Responses&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>端点&lt;/td>
&lt;td>&lt;code>POST /v1/chat/completions&lt;/code>&lt;/td>
&lt;td>&lt;code>POST /v1/responses&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>输入格式&lt;/td>
&lt;td>&lt;code>messages[]&lt;/code> 数组（客户端维护完整对话历史）&lt;/td>
&lt;td>&lt;code>input&lt;/code>（可以是字符串或消息数组），支持通过 &lt;code>previous_response_id&lt;/code> 让服务端管理历史&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>系统提示&lt;/td>
&lt;td>放在 &lt;code>messages&lt;/code> 中的 &lt;code>role: &amp;quot;system&amp;quot;&lt;/code>&lt;/td>
&lt;td>独立的顶层 &lt;code>instructions&lt;/code> 字段&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>输出格式&lt;/td>
&lt;td>&lt;code>choices[]&lt;/code>，每项包含 &lt;code>message&lt;/code>&lt;/td>
&lt;td>&lt;code>output[]&lt;/code>，一组类型化 Item（&lt;code>message&lt;/code>、&lt;code>function_call&lt;/code>、&lt;code>reasoning&lt;/code> 等）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>内置工具&lt;/td>
&lt;td>仅自定义函数调用&lt;/td>
&lt;td>原生支持 &lt;code>web_search&lt;/code>、&lt;code>file_search&lt;/code>、&lt;code>code_interpreter&lt;/code>、&lt;code>computer_use&lt;/code>、MCP、图像生成等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Agent 循环&lt;/td>
&lt;td>单次请求通常只完成一轮工具调用&lt;/td>
&lt;td>一次请求内模型可多次调用工具，形成 Agent 循环&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>状态管理&lt;/td>
&lt;td>无状态，客户端手动维护历史&lt;/td>
&lt;td>&lt;code>store: true&lt;/code> 默认开启，服务端保存上下文；也支持加密推理 Item 实现无状态&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>推理模型&lt;/td>
&lt;td>推理 Token 暴露有限&lt;/td>
&lt;td>提供更完整的推理摘要与工具联动&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>流式输出&lt;/td>
&lt;td>&lt;code>delta&lt;/code> 增量块&lt;/td>
&lt;td>类型化的 SSE 事件（按 &lt;code>type&lt;/code> 分支处理）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>多候选生成&lt;/td>
&lt;td>支持 &lt;code>n &amp;gt; 1&lt;/code> 返回多个 &lt;code>choices&lt;/code>&lt;/td>
&lt;td>已移除，只返回一个结果&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Structured Output&lt;/td>
&lt;td>&lt;code>response_format&lt;/code>&lt;/td>
&lt;td>&lt;code>text.format&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="协议结构差异">协议结构差异
&lt;/h2>&lt;h3 id="chat-completions消息数组驱动">Chat Completions：消息数组驱动
&lt;/h3>&lt;p>客户端负责拼装完整对话上下文：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful assistant.&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;什么是 Responses API？&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>响应结构围绕 &lt;code>choices&lt;/code> 展开：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;choices&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Responses API 是...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="responses类型化-item-驱动">Responses：类型化 Item 驱动
&lt;/h3>&lt;p>输入更灵活，最简单可以直接传字符串：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;什么是 Chat Completions API？&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以传入结构化消息并指定 &lt;code>instructions&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;You are a helpful assistant.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;帮我比较两个 API 的区别。&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>响应由一组类型化 Item 组成：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;resp_xxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;reasoning&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;summary&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;output_text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span> &lt;span class="p">}]&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="工具调用与-agent-能力">工具调用与 Agent 能力
&lt;/h2>&lt;h3 id="chat-completions-中的工具调用">Chat Completions 中的工具调用
&lt;/h3>&lt;p>Chat Completions 支持 &lt;code>tools&lt;/code> 与 &lt;code>tool_calls&lt;/code>，但逻辑是“一问一答”模式：模型返回 &lt;code>tool_calls&lt;/code> 后，客户端需要执行工具、手动把结果追加回 &lt;code>messages&lt;/code>，再发起下一次请求才能让模型继续。&lt;/p>
&lt;p>适合场景：&lt;/p>
&lt;ul>
&lt;li>单次函数调用（查数据库、调用一个 API）&lt;/li>
&lt;li>简单的多轮对话机器人&lt;/li>
&lt;li>兼容现有生态的主流方案（大量第三方框架和推理服务以此为准）&lt;/li>
&lt;/ul>
&lt;h3 id="responses-中的-agent-循环">Responses 中的 Agent 循环
&lt;/h3>&lt;p>Responses API 一次请求内可以完成多轮工具调用。模型可以搜索、读文件、执行代码、再推理，最终在一个响应中返回结果，客户端无需手动维护中间的工具调用状态。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tools&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;code_interpreter&amp;#34;&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;搜索今天的AI新闻，然后写一段 100 字的摘要&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>适合场景：&lt;/p>
&lt;ul>
&lt;li>需要“搜索 + 总结”或“读文件 + 分析”的多步骤任务&lt;/li>
&lt;li>Agent 工作流、自动化研究助手&lt;/li>
&lt;li>希望服务端托管上下文与工具执行的应用&lt;/li>
&lt;/ul>
&lt;h2 id="会话状态管理">会话状态管理
&lt;/h2>&lt;p>这是两者最实际的工程差异之一。&lt;/p>
&lt;p>&lt;strong>Chat Completions：&lt;/strong> 完全无状态。每一轮对话客户端都要把之前所有消息重新发送一遍。对话越长，每次请求的 Token 成本越高，客户端需要自己实现历史裁剪、摘要、缓存等逻辑。&lt;/p>
&lt;p>&lt;strong>Responses：&lt;/strong> 默认开启 &lt;code>store: true&lt;/code>，服务端会保存对话上下文。后续请求只需传 &lt;code>previous_response_id&lt;/code> 指向上一次的响应，服务端自动拼接历史：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;previous_response_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;resp_xxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;继续刚才的话题&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果担心数据隐私，可以使用 &lt;code>store: false&lt;/code> 并利用加密推理 Item（Encrypted Reasoning Items）实现“无状态但保留推理上下文”的模式，适合 ZDR（Zero Data Retention）场景。&lt;/p>
&lt;h2 id="流式事件差异">流式事件差异
&lt;/h2>&lt;h3 id="chat-completions-流式">Chat Completions 流式
&lt;/h3>&lt;p>返回一系列 &lt;code>delta&lt;/code> 块，客户端拼接 &lt;code>delta.content&lt;/code> 即可：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">data: {&amp;#34;choices&amp;#34;:[{&amp;#34;delta&amp;#34;:{&amp;#34;content&amp;#34;:&amp;#34;你&amp;#34;}}]}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: {&amp;#34;choices&amp;#34;:[{&amp;#34;delta&amp;#34;:{&amp;#34;content&amp;#34;:&amp;#34;好&amp;#34;}}]}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: [DONE]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="responses-流式">Responses 流式
&lt;/h3>&lt;p>返回一组按 &lt;code>type&lt;/code> 区分的 SSE 事件，每个事件都有明确的生命周期：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">event: response.output_text.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: {&amp;#34;type&amp;#34;:&amp;#34;response.output_text.delta&amp;#34;,&amp;#34;delta&amp;#34;:&amp;#34;你&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">event: response.function_call_arguments.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: {&amp;#34;type&amp;#34;:&amp;#34;response.function_call_arguments.delta&amp;#34;,&amp;#34;delta&amp;#34;:&amp;#34;...&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">event: response.completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: {&amp;#34;type&amp;#34;:&amp;#34;response.completed&amp;#34;}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Responses 的事件粒度更细，能区分“文本开始”“工具参数流”“推理摘要”“响应完成”等阶段，适合构建更精确的 UI 反馈。&lt;/p>
&lt;h2 id="兼容性与迁移成本">兼容性与迁移成本
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>方面&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>向后兼容&lt;/td>
&lt;td>Chat Completions 没有废弃计划，现有代码可继续使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>第三方兼容&lt;/td>
&lt;td>大量开源推理服务（vLLM、Ollama、各种国内模型）只实现了 Chat Completions 兼容协议；Responses 兼容目前仅限 OpenAI 官方和少数服务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>迁移路径&lt;/td>
&lt;td>OpenAI 提供 migration guide，主要改动是把 &lt;code>messages&lt;/code> 换成 &lt;code>input&lt;/code>，系统提示提取到 &lt;code>instructions&lt;/code>，&lt;code>choices&lt;/code> 改为 &lt;code>output&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SDK 支持&lt;/td>
&lt;td>OpenAI 官方 SDK 两条路径都支持；社区 SDK 大多以 Chat Completions 为主&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>判断标准：&lt;/strong> 如果你的应用需要部署到非 OpenAI 的模型服务（本地模型、第三方推理引擎），Chat Completions 是事实标准。如果只在 OpenAI 生态内构建，Responses 功能更完整。&lt;/p>
&lt;h2 id="选型建议">选型建议
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>推荐&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>新项目、Agent 应用、需要内置工具&lt;/td>
&lt;td>Responses&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>兼容已有代码 / 只做简单文本补全&lt;/td>
&lt;td>Chat Completions&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要服务端托管对话状态、减少 Token 重复发送&lt;/td>
&lt;td>Responses&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要部署到第三方或本地推理服务&lt;/td>
&lt;td>Chat Completions&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要多候选生成（&lt;code>n &amp;gt; 1&lt;/code>）&lt;/td>
&lt;td>Chat Completions&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要完整推理摘要与工具联动&lt;/td>
&lt;td>Responses&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>Chat Completions 和 Responses API 目前都在维护，没有废弃关系。核心区别不是“新旧替代”，而是定位不同：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Chat Completions&lt;/strong> 是通用的事实标准协议，兼容性最好，适合任何模型服务和简单对话场景。&lt;/li>
&lt;li>&lt;strong>Responses&lt;/strong> 是 OpenAI 为 Agent 时代设计的新一代接口，提供内置工具、Agent 循环、会话状态管理和更丰富的输出结构，适合在 OpenAI 生态内构建复杂智能体应用。&lt;/li>
&lt;/ul>
&lt;p>如果你在构建多步骤、需要工具协作的 Agent 应用，Responses API 能显著降低工程复杂度；如果你的核心需求是“调一个模型拿结果”，Chat Completions 依然是最简单、最兼容的选择。&lt;/p></description></item><item><title>Agent Run 流式协议：事件溯源、SSE 投影与断线恢复</title><link>https://www.zata.cc/p/agent-run-%E6%B5%81%E5%BC%8F%E5%8D%8F%E8%AE%AE%E4%BA%8B%E4%BB%B6%E6%BA%AF%E6%BA%90sse-%E6%8A%95%E5%BD%B1%E4%B8%8E%E6%96%AD%E7%BA%BF%E6%81%A2%E5%A4%8D/</link><pubDate>Mon, 07 Sep 2026 10:30:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-run-%E6%B5%81%E5%BC%8F%E5%8D%8F%E8%AE%AE%E4%BA%8B%E4%BB%B6%E6%BA%AF%E6%BA%90sse-%E6%8A%95%E5%BD%B1%E4%B8%8E%E6%96%AD%E7%BA%BF%E6%81%A2%E5%A4%8D/</guid><description>&lt;img src="https://www.zata.cc/p/agent-run-%E6%B5%81%E5%BC%8F%E5%8D%8F%E8%AE%AE%E4%BA%8B%E4%BB%B6%E6%BA%AF%E6%BA%90sse-%E6%8A%95%E5%BD%B1%E4%B8%8E%E6%96%AD%E7%BA%BF%E6%81%A2%E5%A4%8D/images/index/index.svg" alt="Featured image of post Agent Run 流式协议：事件溯源、SSE 投影与断线恢复" />&lt;p>很多 Agent 系统的流式输出，本质上是把模型服务的 chunk 直接转发给浏览器。这样做上线很快，但一旦遇到刷新页面、断线重连、审计和取消，就会立刻发现：&lt;strong>前端消费的不是一个可恢复的事件流，而是一条一次性管道。&lt;/strong>&lt;/p>
&lt;p>我现在的做法是把协议拆成三层：Runtime 先产出领域事件，Adapter 统一映射成 canonical 事件并落库，最后由 SSE 端点从事件库里做投影。这篇文章记录这套协议的实际形态，以及它为什么这样设计。&lt;/p>
&lt;h2 id="一三层结构">一、三层结构
&lt;/h2>&lt;p>整条链路可以这样理解：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Runtime / Runner
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ AgentEvent 执行器内部事件
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Adapter 映射
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ AgentRunEvent canonical 事实，落库并分配 seq
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ SSE 端点 committed event 的投影
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ 前端
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="runner-端口层">Runner 端口层
&lt;/h3>&lt;p>&lt;code>AgentEvent&lt;/code> 是 Runtime 对外的最小事件模型，目前有 7 种类型：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>专用字段&lt;/th>
&lt;th>含义&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>state_change&lt;/code>&lt;/td>
&lt;td>&lt;code>state&lt;/code>&lt;/td>
&lt;td>Runtime 内部状态迁移&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>delta&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>&lt;/td>
&lt;td>模型文本增量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_call&lt;/code>&lt;/td>
&lt;td>&lt;code>tool_name&lt;/code> / &lt;code>tool_call_id&lt;/code> / &lt;code>tool_args&lt;/code>&lt;/td>
&lt;td>工具调用开始&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool_result&lt;/code>&lt;/td>
&lt;td>&lt;code>tool_name&lt;/code> / &lt;code>tool_call_id&lt;/code> / &lt;code>tool_result&lt;/code>&lt;/td>
&lt;td>工具结果摘要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifact&lt;/code>&lt;/td>
&lt;td>&lt;code>artifact&lt;/code>&lt;/td>
&lt;td>结构化产物元数据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>done&lt;/code>&lt;/td>
&lt;td>&lt;code>answer&lt;/code>&lt;/td>
&lt;td>最终答案全文&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>error&lt;/code>&lt;/td>
&lt;td>&lt;code>error_code&lt;/code> / &lt;code>error_message&lt;/code>&lt;/td>
&lt;td>失败&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这一层有意做薄。不管 Runtime 是 LangGraph、DeepAgents，还是新接的沙箱执行器，只要转换成这 7 类事件，后端后面的所有逻辑都不用重复实现。&lt;/p>
&lt;h3 id="canonical-层">Canonical 层
&lt;/h3>&lt;p>Adapter 把 Runtime 事件映射成 canonical 事件。canonical 层不是照抄 Runtime 词汇，而是补齐运行生命周期：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Canonical 事件&lt;/th>
&lt;th>来源&lt;/th>
&lt;th>payload 要点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>run.started&lt;/code>&lt;/td>
&lt;td>Adapter 生成&lt;/td>
&lt;td>&lt;code>started_at&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>message.started&lt;/code>&lt;/td>
&lt;td>Adapter 生成&lt;/td>
&lt;td>&lt;code>message_id&lt;/code>、&lt;code>role&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>message.delta&lt;/code>&lt;/td>
&lt;td>&lt;code>delta&lt;/code>&lt;/td>
&lt;td>&lt;code>message_id&lt;/code>、&lt;code>content_index&lt;/code>、&lt;code>delta&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool.call.started&lt;/code>&lt;/td>
&lt;td>&lt;code>tool_call&lt;/code>&lt;/td>
&lt;td>&lt;code>tool_call_id&lt;/code>、&lt;code>tool_name&lt;/code>、&lt;code>arguments&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tool.call.completed&lt;/code>&lt;/td>
&lt;td>&lt;code>tool_result&lt;/code>&lt;/td>
&lt;td>&lt;code>result&lt;/code>、&lt;code>result_checksum&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifact.created&lt;/code>&lt;/td>
&lt;td>&lt;code>artifact&lt;/code>&lt;/td>
&lt;td>产物 metadata&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>message.completed&lt;/code>&lt;/td>
&lt;td>&lt;code>done&lt;/code>&lt;/td>
&lt;td>&lt;code>content&lt;/code>、&lt;code>content_checksum&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>run.completed&lt;/code>&lt;/td>
&lt;td>&lt;code>done&lt;/code> 后置&lt;/td>
&lt;td>&lt;code>message_id&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>run.failed&lt;/code>&lt;/td>
&lt;td>&lt;code>error&lt;/code>&lt;/td>
&lt;td>&lt;code>error.code&lt;/code>、&lt;code>error.message&lt;/code>、&lt;code>error.retryable&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>run.cancelled&lt;/code>&lt;/td>
&lt;td>取消路径&lt;/td>
&lt;td>&lt;code>external_stop_confirmed&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>落库后的 envelope 长这样：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;run_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;run_xxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;seq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;event_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;message.delta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;occurred_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2026-09-07T10:30:00.123456&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;recorded_at&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2026-09-07T10:30:00.126000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;agent_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;freight_agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;agent_snapshot_checksum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;message_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;message_xxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;delta&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_checksum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;schema_version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;trace_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;span_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里最重要的是 &lt;code>seq&lt;/code>。Runtime 事件本身不携带权威序号；Adapter 只提交 candidate，repository 落库时按 &lt;code>last_event_seq + 1&lt;/code> 分配。&lt;strong>事件一旦提交，顺序就是系统事实。&lt;/strong>&lt;/p>
&lt;h2 id="二sse-wire-format">二、SSE wire format
&lt;/h2>&lt;p>对外入口是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-http" data-lang="http">&lt;span class="line">&lt;span class="cl">&lt;span class="err">GET /api/agent-runs/{run_id}/events
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>SSE 使用命名帧：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">id: 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">event: message.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">data: {&amp;#34;run_id&amp;#34;:&amp;#34;run_xxx&amp;#34;,&amp;#34;seq&amp;#34;:4,&amp;#34;event_type&amp;#34;:&amp;#34;message.delta&amp;#34;,...}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>几个关键语义：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>重放优先。&lt;/strong> 客户端可以用 &lt;code>?after_seq=&lt;/code> 查询参数，也可以用标准 SSE 的 &lt;code>Last-Event-ID&lt;/code> 请求头；服务端取较大值，然后从事件库里回放。&lt;/li>
&lt;li>&lt;strong>游标安全。&lt;/strong> 游标为负，或大于当前 &lt;code>last_event_seq&lt;/code>，返回 422，避免客户端拿着错误游标等一个永远不会来的事件。&lt;/li>
&lt;li>&lt;strong>实时推送。&lt;/strong> 端点每 250ms 从 committed repository 拉取新事件；发现序号 gap 就断流，不静默跳过。&lt;/li>
&lt;li>&lt;strong>keep-alive。&lt;/strong> 空闲 15 秒发送 SSE 注释帧 &lt;code>: keep-alive&lt;/code>。&lt;/li>
&lt;li>&lt;strong>自然终止。&lt;/strong> Run 进入终态且事件全部送完，连接主动关闭。&lt;/li>
&lt;/ol>
&lt;p>一次成功 Run 的典型序列：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">id: 1 event: run.started
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 2 event: message.started
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 3 event: message.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 4 event: tool.call.started
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 5 event: tool.call.completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 6 event: message.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 7 event: artifact.created
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 8 event: message.completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">id: 9 event: run.completed
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>失败或取消时，终态分别是 &lt;code>run.failed&lt;/code> 或 &lt;code>run.cancelled&lt;/code>。Adapter 用一个终态闸保证三者最多出现一个。&lt;/p>
&lt;h2 id="三为什么先落库再投影">三、为什么先落库，再投影
&lt;/h2>&lt;p>这是整套协议的核心取舍。&lt;/p>
&lt;h3 id="断线恢复变成查询问题">断线恢复变成查询问题
&lt;/h3>&lt;p>客户端刷新页面后，不需要 Runtime 重跑一遍，也不需要服务端缓存整个连接状态。只要带上 &lt;code>Last-Event-ID: 5&lt;/code>，服务端就查询 &lt;code>seq &amp;gt; 5&lt;/code> 的事件继续发。断线恢复从连接层问题退化成数据库查询问题。&lt;/p>
&lt;h3 id="runtime-崩溃不丢执行事实">Runtime 崩溃不丢执行事实
&lt;/h3>&lt;p>事件写入 committed store 之后，Runtime 进程是否还活着，不影响前端重放历史。审计、排障和 UI 恢复消费的是同一份数据。&lt;/p>
&lt;h3 id="多-runtime-共享同一套前端协议">多 Runtime 共享同一套前端协议
&lt;/h3>&lt;p>不同执行器的事件风格可能差异很大。Adapter 负责把差异吸收在入口处，前端只认识 canonical 词汇。新增 Runtime 时，可以避免出现“沙箱一套流、业务 Agent 一套流”的分裂。&lt;/p>
&lt;h3 id="工具调用能稳定配对">工具调用能稳定配对
&lt;/h3>&lt;p>&lt;code>tool_call_id&lt;/code> 优先使用底层模型下发的 id。如果 Runtime 丢了开始事件，Adapter 会补发一个 &lt;code>tool.call.started&lt;/code>，让 UI 仍然成对渲染。这个“配对自愈”很小心地只影响展示完整性，不把 Run 打成失败。&lt;/p>
&lt;h2 id="四当前边界和改进方向">四、当前边界和改进方向
&lt;/h2>&lt;p>这套协议也有几个明显的边界。&lt;/p>
&lt;p>&lt;strong>SSE 出口是客户端胖投影。&lt;/strong> 目前 &lt;code>data&lt;/code> 帧直接序列化完整 envelope，&lt;code>agent_snapshot_checksum&lt;/code>、&lt;code>provenance&lt;/code>、&lt;code>payload_checksum&lt;/code> 等审计字段也被发给前端。短期省事，长期更适合收敛成只包含 &lt;code>seq&lt;/code>、&lt;code>event_type&lt;/code>、&lt;code>occurred_at&lt;/code>、&lt;code>payload&lt;/code> 的客户端投影。&lt;/p>
&lt;p>&lt;strong>实时性靠 250ms 轮询。&lt;/strong> 对文本流足够，但并发观看的连接多了，数据库查询会线性放大。合理演进是加一个进程内或跨实例 pub/sub，只负责“唤醒”轮询，不负责权威事件投递；事件仍然以数据库为准。&lt;/p>
&lt;p>&lt;strong>内容模型只有文本增量。&lt;/strong> &lt;code>message.delta&lt;/code> 目前只有 &lt;code>content_index&lt;/code> 和文本，没有结构化 content block。未来要支持图片、图表卡片或分块富文本，应增量引入 block 类型，而不是扩出新的顶层事件。&lt;/p>
&lt;p>&lt;strong>&lt;code>state_change&lt;/code> 是死词汇。&lt;/strong> Runtime 端口定义了它，但 canonical Adapter 不会映射。后续应该删除或明确语义，避免接入方误以为它会被消费。&lt;/p>
&lt;p>&lt;strong>协议演进规则需要写死。&lt;/strong> 客户端对未知事件类型必须忽略，而不是报错。&lt;code>schema_version&lt;/code> 目前存在，但配套演进策略还要补文档。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>这套协议不是行业标准，传输层用的也不是什么新东西，但它把几件事做对了：&lt;strong>事件先成为带序号的事实，SSE 只是投影；Runtime 词汇保持最小，canonical 词汇保持稳定；断线恢复用标准 SSE 游标；工具调用、产物、失败、取消全部事件化。&lt;/strong>&lt;/p>
&lt;p>如果要从这套设计里抽象一条通用经验，那就是：Agent 的远程流式接口不应该让客户端订阅“模型的输出过程”，而应该让客户端订阅“系统已经发生并且可恢复的执行事实”。管道可以丢，事实不应该丢。&lt;/p></description></item><item><title>给 Agent 接入 Web Search：四种做法，和一条我试过之后放弃的路</title><link>https://www.zata.cc/p/%E7%BB%99-agent-%E6%8E%A5%E5%85%A5-web-search%E5%9B%9B%E7%A7%8D%E5%81%9A%E6%B3%95%E5%92%8C%E4%B8%80%E6%9D%A1%E6%88%91%E8%AF%95%E8%BF%87%E4%B9%8B%E5%90%8E%E6%94%BE%E5%BC%83%E7%9A%84%E8%B7%AF/</link><pubDate>Mon, 07 Sep 2026 10:00:00 +0800</pubDate><guid>https://www.zata.cc/p/%E7%BB%99-agent-%E6%8E%A5%E5%85%A5-web-search%E5%9B%9B%E7%A7%8D%E5%81%9A%E6%B3%95%E5%92%8C%E4%B8%80%E6%9D%A1%E6%88%91%E8%AF%95%E8%BF%87%E4%B9%8B%E5%90%8E%E6%94%BE%E5%BC%83%E7%9A%84%E8%B7%AF/</guid><description>&lt;img src="https://www.zata.cc/p/%E7%BB%99-agent-%E6%8E%A5%E5%85%A5-web-search%E5%9B%9B%E7%A7%8D%E5%81%9A%E6%B3%95%E5%92%8C%E4%B8%80%E6%9D%A1%E6%88%91%E8%AF%95%E8%BF%87%E4%B9%8B%E5%90%8E%E6%94%BE%E5%BC%83%E7%9A%84%E8%B7%AF/images/index/index.svg" alt="Featured image of post 给 Agent 接入 Web Search：四种做法，和一条我试过之后放弃的路" />&lt;p>Agent 的知识止于训练数据。一旦问题涉及「最近」——新发布的版本、上周的新闻、当前的价格——就需要 web search。这件事看起来简单，做起来第一道坎是选型：给 Agent 接入搜索的方式五花八门，方案之间的差别比看起来大。&lt;/p>
&lt;p>把主流做法按「搜索能力由谁提供」归类，其实只有五种：模型厂商内置的搜索工具、搜索 API 包成 function tool、MCP server、CLI+Skill，以及自托管元搜索引擎（SearXNG）。&lt;/p>
&lt;p>我的判断：前四种都走得通，选哪种主要取决于你的模型入口（官方 API 还是自建端点）和工程形态（代码内工具、协议层还是命令行）；第五种 SearXNG 听起来是「免费、自主、无配额」的最优解，我搭过一套给 Agent 用，结论是不推荐——聚合并没有解决上游不稳定的问题，反而把不稳定的方差叠加进了每次搜索。下面逐条展开。&lt;/p>
&lt;h2 id="一先想清楚搜索在谁的进程里执行">一、先想清楚：搜索在谁的进程里执行
&lt;/h2>&lt;p>五种方式的名义差别是「用什么产品」，本质差别是搜索这个动作发生在哪里：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>方式&lt;/th>
&lt;th>搜索在哪执行&lt;/th>
&lt;th>你要维护什么&lt;/th>
&lt;th>对模型入口的要求&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>厂商内置搜索工具&lt;/td>
&lt;td>模型厂商侧&lt;/td>
&lt;td>几乎为零&lt;/td>
&lt;td>绑定官方 API&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>搜索 API + function tool&lt;/td>
&lt;td>你的进程&lt;/td>
&lt;td>一个 tool 和一个 API key&lt;/td>
&lt;td>任何支持 function calling 的模型&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MCP server&lt;/td>
&lt;td>你的进程（协议层）&lt;/td>
&lt;td>server 配置与进程&lt;/td>
&lt;td>任何支持 MCP 的框架&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CLI + Skill&lt;/td>
&lt;td>Agent 的 shell 里&lt;/td>
&lt;td>脚本与 SKILL.md&lt;/td>
&lt;td>有 execute/shell 工具的 Agent&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>自托管 SearXNG&lt;/td>
&lt;td>你自己的服务器&lt;/td>
&lt;td>一整套搜索引擎运维&lt;/td>
&lt;td>包成 tool 后同方式二&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这个视角直接决定第一道筛选：如果你的模型跑在自建 vLLM 或其他 OpenAI 兼容端点上，第一种方式直接出局——厂商内置工具只在官方端点生效，vLLM 不提供搜索后端。这也是很多人（包括我）从官方 API 切到自建端点后，被迫重新做选型的原因。&lt;/p>
&lt;p>另一个前置认知：&lt;strong>搜索工具几乎必须配一个抓取（fetch）工具&lt;/strong>。搜索返回的是链接和摘要，Agent 要真正引用就得读全文。下面每种方式的讨论里，搜索和抓取都是成对出现的。&lt;/p>
&lt;h2 id="二方式一模型厂商内置的搜索工具">二、方式一：模型厂商内置的搜索工具
&lt;/h2>&lt;p>OpenAI Responses API 的 &lt;code>web_search&lt;/code>、Claude API 的 &lt;code>web_search&lt;/code> 工具、Gemini 的 &lt;code>google_search&lt;/code> grounding 都属于这一类：搜索在厂商侧执行，返回结果自带引用，客户端零实现。&lt;/p>
&lt;p>以 Claude 为例，全部接入代码就是声明一个工具：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Anthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Anthropic&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;claude-opus-4-8&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">4096&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Opus 4.8/4.7/4.6 与 Sonnet 4.6 用 web_search_20260209，&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更早的模型用基础版 web_search_20250305&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search_20260209&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;vLLM 最新版本支持哪些 OpenAI 兼容接口？&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>阿里云百炼也是这个形态，但接口协议有个容易踩的坑：联网搜索要走 &lt;strong>OpenAI-compatible Responses API&lt;/strong>，而不是 Chat Completions。在 Chat Completions 里传 &lt;code>enable_search&lt;/code> 这类参数，请求不会报错，但厂商侧搜索可能根本没触发——我就先在这里卡了一次：模型照样回答，正文看起来很专业，响应里却没有任何搜索来源。正确的接法是 &lt;code>tools&lt;/code> 声明：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;https://&lt;/span>&lt;span class="si">{WorkspaceId}&lt;/span>&lt;span class="s2">.cn-beijing.maas.aliyuncs.com/compatible-mode/v1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;qwen3.7-flash&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;今天的 GitHub trending&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="c1"># 搜索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_extractor&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="c1"># 抓取正文，效果更完整但更慢&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stream&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>来源也不混在正文里，而在 &lt;code>response.output&lt;/code> 里 &lt;code>type == &amp;quot;web_search_call&amp;quot;&lt;/code> 的元素中，&lt;code>action.sources&lt;/code> 就是链接列表。流式事件同样能拿到：&lt;code>response.web_search_call.searching&lt;/code> / &lt;code>completed&lt;/code> 表示搜索阶段，&lt;code>response.output_text.delta&lt;/code> 才是正文增量。&lt;/p>
&lt;p>两个实测数字（同一问题「今天的 GitHub trending」，qwen3.7-flash）：只开 &lt;code>web_search&lt;/code> 约 14 秒；&lt;code>web_search + web_extractor&lt;/code> 约 46–67 秒。&lt;code>web_extractor&lt;/code> 的定位就是搜索工具里的 fetch——它让模型读正文，代价是会触发多轮抓取。还有一个体验细节：阿里云的 SSE 事件有批量下发的情况，&lt;code>in_progress&lt;/code> / &lt;code>searching&lt;/code> 可能到搜索快结束时才一起到达，客户端如果要展示「正在搜索」，最好在请求发出时先本地打印一行。&lt;/p>
&lt;p>OpenAI 和 Gemini 形态类似：&lt;code>tools&lt;/code> 里声明一个类型，其余交给平台。没有客户端实现、没有 key 管理、引用自动带回，模型对这类原生工具的使用意愿也最强——不需要你教它什么时候搜。要注意的是这类参数是厂商扩展：换厂商就得换参数名和协议，自建 vLLM 不认识它们。&lt;/p>
&lt;p>代价有三个：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>绑定官方 API&lt;/strong>。换模型入口这条路就断了，前面说过。&lt;/li>
&lt;li>&lt;strong>按次计费&lt;/strong>。两大厂商目前都是 $10/千次搜索的量级（以官网为准），量大的场景要算清这笔钱和 token 账的关系。&lt;/li>
&lt;li>&lt;strong>可控性有限&lt;/strong>。&lt;code>allowed_domains&lt;/code>/&lt;code>blocked_domains&lt;/code> 这类过滤参数有，但缓存、代理、结果后处理这些深度定制没有。&lt;/li>
&lt;/ul>
&lt;p>适合的场景很明确：用官方 API、想零维护快速上线、搜索量不大。这是「能用」的天花板最低、起步最快的一条路。&lt;/p>
&lt;h2 id="三方式二搜索-api-包成-function-tool通用性最好">三、方式二：搜索 API 包成 function tool（通用性最好）
&lt;/h2>&lt;p>找一个搜索 API，用 Agent 框架的工具机制包一层。这是模型无关的默认解，也是自建端点场景的主路。先选后端：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>后端&lt;/th>
&lt;th>定位&lt;/th>
&lt;th>免费额度&lt;/th>
&lt;th>备注&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://www.tavily.com/" target="_blank" rel="noopener"
>Tavily&lt;/a>&lt;/td>
&lt;td>专为 Agent 设计，直接返回清洗后的正文摘录&lt;/td>
&lt;td>有月度免费额度&lt;/td>
&lt;td>LangChain 有现成集成 &lt;code>langchain-tavily&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Exa&lt;/td>
&lt;td>语义/神经搜索&lt;/td>
&lt;td>有&lt;/td>
&lt;td>适合「找相似资料」而非关键词匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Jina（s.jina.ai）&lt;/td>
&lt;td>搜索 + grounding&lt;/td>
&lt;td>有&lt;/td>
&lt;td>与 r.jina.ai 抓取配套&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Serper / SerpAPI&lt;/td>
&lt;td>Google 结果页的结构化代理&lt;/td>
&lt;td>注册赠送&lt;/td>
&lt;td>返回 SERP 结构，正文要自己抓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Brave Search API&lt;/td>
&lt;td>传统搜索 API&lt;/td>
&lt;td>有月度免费额度&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>博查（Bocha）、智谱 web-search-pro&lt;/td>
&lt;td>国内可直连&lt;/td>
&lt;td>少量&lt;/td>
&lt;td>不想给 Agent 配代理出口时用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DuckDuckGo（&lt;code>ddgs&lt;/code> 包）&lt;/td>
&lt;td>免费无 key&lt;/td>
&lt;td>无限（被限流）&lt;/td>
&lt;td>质量与稳定性一般，适合原型验证&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>两个选型提醒：Bing Search API 已于 2025 年 8 月退役，按旧教程选型会踩空；各家额度与价格随时调整，动手前以官网为准。&lt;/p>
&lt;p>接入的最短路径是现成集成：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># uv pip install langchain-tavily&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_tavily&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TavilySearch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">search&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TavilySearch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_results&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_deep_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">system_prompt&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要控制输出格式或随时换后端，就自己包一层。以 LangChain 为例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.tools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">tavily&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TavilyClient&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TavilyClient&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1"># 读 TAVILY_API_KEY 环境变量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TODAY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">astimezone&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">web_search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;搜索互联网，返回最相关的网页标题、链接与内容摘录。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">当问题涉及训练数据之后的信息（新版本、新闻、当前价格）时调用。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> (current date: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">TODAY&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">%Y-%m-%d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_results&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time_range&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;week&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;[&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;title&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">](&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;url&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">)&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;content&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">published: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;published_date&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="s1">&amp;#39;unknown&amp;#39;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;results&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意工具的 docstring 不是注释，是模型判断「何时调用」的依据——触发条件必须写进描述里，这一点后面还会反复出现。&lt;/p>
&lt;p>配套的 fetch 工具，最省事的版本是走 r.jina.ai 把网页转成 Markdown：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">httpx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.tools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_url&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;抓取网页并返回正文。拿到 web_search 的结果链接后，引用前先读全文。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">resp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">httpx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;https://r.jina.ai/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">resp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">20000&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 截断保护，防止单页吃满 context&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>（需要更强的抓取能力再上 Firecrawl。）&lt;/p>
&lt;p>三条工程经验：&lt;/p>
&lt;ul>
&lt;li>**搜索和抓取分成两个工具。**合并成一个「搜了顺便读」的工具，模型的调用意愿和结果质量都会下降，排查问题也更难。&lt;/li>
&lt;li>**时效性是检索约束，不是模型自觉。**我踩过一个很典型的坑：用 Tavily 搜「今天的 GitHub trending」，Tavily 返回的混合了旧博客、旧榜单和缓存页，模型把结果里的「2025」当成了当前年份，最后一本正经地回答「今天是 2025 年」。修法是把时间边界从模型手里拿走：system prompt 注入本机当前日期，搜索查询附带 &lt;code>current date&lt;/code>，检索强制 &lt;code>time_range=&amp;quot;week&amp;quot;&lt;/code>，结果逐条带 &lt;code>published&lt;/code> 字段。不要把 &lt;code>time_range&lt;/code> 作为可选参数交给模型决定——它连今天几号都不确定。&lt;/li>
&lt;li>**弱模型要强指令。**qwen 级别的开源模型对「何时该搜」并不敏感，光给工具不够，还要在 system prompt 里写明「问题涉及训练截止后的信息时，必须先调用 web_search，再回答」这类硬规则。&lt;/li>
&lt;li>**网络环境要提前想好。**国内网络下 Tavily 等域名走代理即可；要求直连就选博查或智谱。&lt;/li>
&lt;/ul>
&lt;h2 id="四方式三mcp-server">四、方式三：MCP server
&lt;/h2>&lt;p>搜索能力不写在 Agent 代码里，而是挂在 MCP server 上，Agent 通过标准协议发现并调用。现成的 server 很多：&lt;a class="link" href="https://github.com/tavily-ai/tavily-mcp" target="_blank" rel="noopener"
>tavily-mcp&lt;/a>、Brave 的官方 MCP、官方参考实现里的 &lt;a class="link" href="https://github.com/modelcontextprotocol/servers" target="_blank" rel="noopener"
>fetch server&lt;/a>，以及若干 SearXNG 的 MCP 封装。&lt;/p>
&lt;p>LangChain 生态用 &lt;code>langchain-mcp-adapters&lt;/code> 接入：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># uv pip install langchain-mcp-adapters&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_mcp_adapters.client&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">MultiServerMCPClient&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">MultiServerMCPClient&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tavily&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;transport&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;-y&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;tavily-mcp&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TAVILY_API_KEY&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fetch&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;transport&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;uvx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mcp-server-fetch&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_tools&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># tools 是标准 LangChain 工具，直接进 create_deep_agent / LangGraph&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>什么时候它比方式二好：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>换后端不改代码&lt;/strong>。从 Tavily 换到 Brave，改一段 server 配置就行，Agent 侧零改动。&lt;/li>
&lt;li>&lt;strong>多 Agent 共享工具栈&lt;/strong>。CLI Agent、IDE 插件、Web 服务共用同一份 MCP 配置，工具升级一处生效。&lt;/li>
&lt;li>&lt;strong>生态即插即用&lt;/strong>。搜索之外，抓取、爬虫、浏览器自动化都有现成 server，不写胶水代码。&lt;/li>
&lt;/ul>
&lt;p>代价是多一层进程管理和协议开销，调试链路变长——工具不工作时，得先分清是 server 挂了、配置错了还是适配层的问题。另外 MCP 不是唯一答案：2026 年的 Agent 工具生态里，MCP 和 CLI+Skill 是并列的两条路线，而不是替代关系，这正好引出下一种方式。&lt;/p>
&lt;h2 id="五方式四cli--skill">五、方式四：CLI + Skill
&lt;/h2>&lt;p>不给 Agent 专门的搜索工具，而是给它 shell 执行能力加一个搜索 CLI，再用 SKILL.md 教它什么时候用、怎么解析输出。飞书给 Agent 生态提供的 lark-cli 就是这条路线的实例——CLI 工具 + Skill 文档，不经过 MCP。&lt;/p>
&lt;p>形态上，search.py 就是方式二里那个 API 调用，只是入口从 function tool 变成命令行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">skills/web-search/SKILL.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">name: web-search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">description: 需要训练数据之后的最新信息时，用 CLI 搜索并阅读网页
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 网络搜索
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">需要最新信息时：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. 执行 `python search.py &amp;#34;查询词&amp;#34;`，获取结果列表（JSON）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 挑选最相关的 2~3 个链接，执行 `python fetch.py &amp;lt;url&amp;gt;` 阅读正文
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 回答时附上来源链接
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现成的搜索 CLI（比如 ddgr）也可以用，但输出解析比结构化的 tool result 脆，自己包一层脚本输出 JSON 更稳。&lt;/p>
&lt;p>这条路线的取舍：&lt;/p>
&lt;ul>
&lt;li>优点是&lt;strong>透明和复用&lt;/strong>。每一步都能手工复现，调试时直接跑同一条命令；已有 CLI 工具栈零成本接入；不挑 Agent 框架，版本管理走 git。&lt;/li>
&lt;li>缺点是&lt;strong>解析脆、权限面大&lt;/strong>。给了 shell 就是给了世界，生产环境必须配沙箱；整个流程依赖模型认真读 SKILL.md，弱模型更容易跳过文档直接编。&lt;/li>
&lt;/ul>
&lt;p>适合已经有 execute/shell 工具且沙箱化的 Agent 运行时（deepagents 类框架都是这个形态），以及团队偏好可审计的命令行工作流的情况。&lt;/p>
&lt;h2 id="六方式五自托管-searxng我试过放弃">六、方式五：自托管 SearXNG——我试过，放弃
&lt;/h2>&lt;p>SearXNG 是开源的元搜索引擎：自己不索引网页，把查询转发给 Google、Bing、DuckDuckGo 等上游引擎再聚合结果，自带 &lt;a class="link" href="https://docs.searxng.org/" target="_blank" rel="noopener"
>JSON API&lt;/a>，无 key 无配额。&lt;/p>
&lt;p>它的卖点写在项目首页上：免费、隐私可控、不依赖单一搜索巨头。「聚合多个上游，一个挂了还有别的」——这句话对 Agent 场景特别有说服力，我当时也是这么被说服的。搭完给 Agent 用了一段时间之后，我的结论是：&lt;strong>这套卖点里的容错是「结果集层面」的，而 Agent 需要的是「结果质量层面」的稳定，聚合解决不了后者。&lt;/strong>&lt;/p>
&lt;p>展开说三点。&lt;/p>
&lt;p>**第一，聚合没有解决上游不稳定，反而放大了它。**元搜索的容错逻辑是：某个上游挂了，其他上游还能出结果，所以「总有东西返回」。但 Agent 对搜索的要求不是「多少有点结果」，而是每次都拿到质量稳定的相关结果——搜索结果的抖动会直接传导成 Agent 行为的抖动，而且极难归因：答案变差了，你分不清是模型的问题还是搜索的问题。实际表现就是这种抖动：上游今天 CAPTCHA、明天限流，结果时好时坏，且坏的时机无法预期。&lt;/p>
&lt;p>**第二，自托管处在上游风控最不利的位置。**SearXNG 的出口是你的服务器：数据中心 IP、没有浏览器指纹、没有登录态。Google 和 Bing 对这类无头流量的限流和 CAPTCHA 是最狠的，而 Tavily、Brave 这类搜索 API 收的钱，本质上就是「由服务商维护合规的抓取出口」的成本。你省下的是 API 费，付出的是和一个风控体系长期对抗。&lt;/p>
&lt;p>**第三，运维对象从「一个 API key」变成「一套搜索引擎」。**引擎启停配置、上游可用性监控、CAPTCHA 处理、结果字段映射，国内部署还要给上游引擎配代理出口。这些全是隐性成本，而且没有 SLA——坏了就是你自己修。&lt;/p>
&lt;p>一句话总结：它解决的问题（隐私、成本）在 Agent 搜索场景里不是主要矛盾，它引入的问题（不稳定性）恰恰是 Agent 最不能接受的。&lt;/p>
&lt;p>边界也说清楚：如果是强隐私合规、内网部署的硬约束，并且有专人维护，SearXNG 值得重新评估——它在那个场景里解决的问题才是真问题。但「给 Agent 加个搜索」这个需求，别从这里开始。&lt;/p>
&lt;h2 id="七怎么选">七、怎么选
&lt;/h2>&lt;p>决策表：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>你的情况&lt;/th>
&lt;th>推荐起点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>用官方 API，要最快上线&lt;/td>
&lt;td>方式一&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>自建/兼容端点（vLLM 等），要模型无关&lt;/td>
&lt;td>方式二（Tavily 起步）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>多 Agent 共享工具栈，要解耦&lt;/td>
&lt;td>方式三&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>已有沙箱化的 execute 工具和 CLI 栈&lt;/td>
&lt;td>方式四&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>想省钱/隐私，考虑自托管 SearXNG&lt;/td>
&lt;td>先算隐性运维成本，默认不推荐&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>三个与具体方式无关的工程结论：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>搜索和抓取永远是两个工具&lt;/strong>，一个查链接，一个读全文。&lt;/li>
&lt;li>&lt;strong>触发条件写进工具描述和 system prompt&lt;/strong>，弱模型尤其需要硬规则，否则工具躺在那里没人调。&lt;/li>
&lt;li>&lt;strong>搜索结果对 context 的消耗很可观&lt;/strong>：一次搜索五条结果就是一到两千 token，深度调研任务给独立的 researcher 子 Agent，让它搜完、读完、综合完再带回结论，别让原始搜索结果塞满主循环。&lt;/li>
&lt;li>**永远区分「厂商侧搜索生效了没有」。**请求不报错不代表搜索被触发：响应里没有 &lt;code>web_search_call&lt;/code> / 来源字段，模型就是在拿参数化记忆演戏。把来源列表当作搜索类回答的最低验收标准，没有来源就不要采信。&lt;/li>
&lt;/ol>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ul>
&lt;li>五种方式按「搜索在谁的进程里执行」分层，前四种都能落地，选型看模型入口和工程形态。&lt;/li>
&lt;li>自建 OpenAI 兼容端点是分水岭：厂商内置工具直接排除，搜索 API 包 function tool 成为默认解。&lt;/li>
&lt;li>SearXNG 的聚合容错是结果集层面的，不解决 Agent 需要的结果质量稳定性，且自托管在上游风控里处于最不利的位置——实测后放弃，不推荐。&lt;/li>
&lt;li>无论选哪种：search 和 fetch 成对出现，触发条件写进描述，重调研用子 Agent 隔离。&lt;/li>
&lt;/ul></description></item><item><title>Agent 内容输出规范：本地给路径，远程给协议</title><link>https://www.zata.cc/p/agent-%E5%86%85%E5%AE%B9%E8%BE%93%E5%87%BA%E8%A7%84%E8%8C%83%E6%9C%AC%E5%9C%B0%E7%BB%99%E8%B7%AF%E5%BE%84%E8%BF%9C%E7%A8%8B%E7%BB%99%E5%8D%8F%E8%AE%AE/</link><pubDate>Thu, 03 Sep 2026 10:00:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E5%86%85%E5%AE%B9%E8%BE%93%E5%87%BA%E8%A7%84%E8%8C%83%E6%9C%AC%E5%9C%B0%E7%BB%99%E8%B7%AF%E5%BE%84%E8%BF%9C%E7%A8%8B%E7%BB%99%E5%8D%8F%E8%AE%AE/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E5%86%85%E5%AE%B9%E8%BE%93%E5%87%BA%E8%A7%84%E8%8C%83%E6%9C%AC%E5%9C%B0%E7%BB%99%E8%B7%AF%E5%BE%84%E8%BF%9C%E7%A8%8B%E7%BB%99%E5%8D%8F%E8%AE%AE/images/index/index.svg" alt="Featured image of post Agent 内容输出规范：本地给路径，远程给协议" />&lt;p>把 Agent 从&amp;quot;本地脚本&amp;quot;变成&amp;quot;线上服务&amp;quot;时,第一个卡住你的往往不是模型,而是输出。&lt;/p>
&lt;p>本地运行时,Agent 写完报告把路径 &lt;code>print&lt;/code> 出来就行,人类看得懂;一旦换到远程,调用方是另一个程序,它拿到的必须是可以解析的结构化信息——Agent 调用工具了没有?调用了哪个?产物文件怎么下载?这些信息在本地靠肉眼,在远程必须靠协议。&lt;/p>
&lt;p>这篇文章回答三个问题:&lt;strong>Agent 到底输出什么、工具调用在消息协议里长什么样、远程接口该用什么方式把这些输出送出去&lt;/strong>。&lt;/p>
&lt;h2 id="一一次运行的三类输出">一、一次运行的三类输出
&lt;/h2>&lt;p>一个 Agent 跑完一轮,输出永远是三类东西的组合:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">① 自然语言流: 给用户看的解释、报告正文、流式增量文本
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">② 工具调用事件: 调用了哪个工具、参数是什么、结果是什么
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">③ 产物 (Artifact): 生成的文件——报告、音频、图片、数据库记录
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>本地和远程的差异,本质上是对这三类输出的&lt;strong>投递方式&lt;/strong>不同:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>输出&lt;/th>
&lt;th>本地&lt;/th>
&lt;th>远程&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>自然语言&lt;/td>
&lt;td>&lt;code>print&lt;/code> / 终端流式 / Streamlit&lt;/td>
&lt;td>SSE 事件里的文本增量块&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具调用&lt;/td>
&lt;td>肉眼观察日志&lt;/td>
&lt;td>结构化事件(工具名、参数、结果)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>产物&lt;/td>
&lt;td>文件路径,直接可访问&lt;/td>
&lt;td>独立下载端点 + 访问 URL&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其中第②类最难标准化,它夹在&amp;quot;模型输出&amp;quot;和&amp;quot;程序输出&amp;quot;之间,是 Agent 独有的形态。先从它说起。&lt;/p>
&lt;h2 id="二消息协议工具调用在线上长什么样">二、消息协议：工具调用在&amp;quot;线&amp;quot;上长什么样
&lt;/h2>&lt;p>主流 Agent 框架(OpenAI Agents SDK、LangChain、各种自研循环)底层都遵循 OpenAI 兼容的聊天消息格式。工具调用不是一段文字,而是一条结构化消息:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我来搜索一下这个问题的答案。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tool_calls&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;call_abc123&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;arguments&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{\&amp;#34;query\&amp;#34;: \&amp;#34;AG-UI protocol\&amp;#34;, \&amp;#34;limit\&amp;#34;: 5}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>模型说&amp;quot;我要调用 &lt;code>web_search&lt;/code>&amp;quot;,程序执行后,把结果以 &lt;code>tool&lt;/code> 角色的消息回灌给模型:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tool&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tool_call_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;call_abc123&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;搜到 3 条结果：...（或被截断的文本）&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>协议里两个必须知道的细节:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>arguments&lt;/code> 是 JSON 字符串,不是对象&lt;/strong>——这是协议约定,传输时不会被二次转义/改键序,接收方拿到后自行 &lt;code>json.loads&lt;/code>。&lt;/li>
&lt;li>&lt;strong>&lt;code>tool&lt;/code> 消息必须带 &lt;code>tool_call_id&lt;/code> 回指&lt;/strong>——否则模型不知道自己拿到的是哪次调用的结果。&lt;/li>
&lt;/ul>
&lt;p>Anthropic 的 content-blocks 格式是另一套等价表达:&lt;code>tool_use&lt;/code> / &lt;code>tool_result&lt;/code> 作为内容块混合在 &lt;code>content&lt;/code> 数组里,概念一致。&lt;/p>
&lt;blockquote>
&lt;p>这一层是&lt;strong>协议事实&lt;/strong>,不依赖某个框架。无论你手写循环、用 SDK、还是用 MCP 桥接工具,最终在&amp;quot;线&amp;quot;上走的都是这个形状。&lt;/p>
&lt;/blockquote>
&lt;h2 id="三两类-agent-框架事件流仓库实证">三、两类 Agent 框架事件流（仓库实证）
&lt;/h2>&lt;p>上面是&amp;quot;一次调用&amp;quot;的静态格式;实际编程里,你可能直接在事件流里消费这些消息。有两种主流的流式事件体系,拿真实代码对比:&lt;/p>
&lt;p>&lt;strong>1. OpenAI Agents SDK 的事件类型&lt;/strong> —— 在 awesome-llm-apps 的流式教程 &lt;code>openai_sdk_crash_course/4_running_agents/4_4_streaming_events/agent.py&lt;/code> 中,事件按类型分发:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># OpenAI Agents SDK (agents 0.1.x) 流式事件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">event&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">Runner&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run_streamed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;解释机器学习原理&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">hasattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;response_start&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[EVENT] Response started&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;tool_call_start&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="c1"># ← 工具调用开始&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[EVENT] Tool call started&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;tool_call_complete&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="c1"># ← 工具调用完成&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[EVENT] Tool call completed&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">type&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;response_complete&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;[EVENT] Response completed&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>事件名是 &lt;code>response_start&lt;/code> / &lt;code>tool_call_start&lt;/code> / &lt;code>tool_call_complete&lt;/code> / &lt;code>response_complete&lt;/code>,SDK 在内部维护运行状态,把模型输出和工具执行切成事件抛给你。&lt;/p>
&lt;p>&lt;strong>2. Anthropic Messages API 的 content-block 流&lt;/strong> —— 事件按内容块细分:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">message_start → content_block_start → text_delta × N
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ content_block_stop → content_block_start(tool_use) → tool_use(...)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ message_stop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>模型吐一个字推一个字(&lt;code>text_delta&lt;/code>),工具参数一段一段到达;结果是前一个事件驱动下一个:你先把参数收完整,再执行工具,再把结果作为 &lt;code>tool_result&lt;/code> 发回。&lt;/p>
&lt;p>两者的差别是&lt;strong>粒度&lt;/strong>:OpenAI SDK 把你当&amp;quot;运行期观察者&amp;quot;,Anthropic 把你当&amp;quot;消息流水线上的协作者&amp;quot;。用到自己的远程接口上,设计可以取其中任意一套——但命名要统一。&lt;/p>
&lt;h2 id="四远程接口三种投递方案">四、远程接口：三种投递方案
&lt;/h2>&lt;h3 id="方案-a非流式-rest返回完整-transcript">方案 A：非流式 REST——返回完整 transcript
&lt;/h3>&lt;p>&lt;code>POST /chat&lt;/code> 传入 messages,Agent 全程跑完后把&lt;strong>完整消息数组&lt;/strong>(含 tool_calls)一次性返回。简单、可缓存,但调用方是黑盒:埋头等十几秒,看不到过程,长任务容易超时。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// POST /chat → 200 OK
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我来搜索一下。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;tool_calls&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="err">...&lt;/span>&lt;span class="p">]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tool&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;tool_call_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;call_abc123&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;结论是……&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>适用:离线批处理、对延迟不敏感、调用方只需要最终答案的场景。&lt;/p>
&lt;h3 id="方案-bsse-流式事件驱动的推荐方案">方案 B：SSE 流式——事件驱动的推荐方案
&lt;/h3>&lt;p>&lt;code>POST /chat&lt;/code> 返回 &lt;code>text/event-stream&lt;/code>,Agent 每做一个动作 yield 一个类型化事件。&lt;strong>这是把 Agent 挂到线上最主流的姿势&lt;/strong>:死等变成边跑边看,工具调用可渲染、可中断、可审计。&lt;/p>
&lt;p>事件类型直接复用上面第三节的命名体系。最小实现:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 依赖: fastapi&amp;gt;=0.115, uvicorn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">StreamingResponse&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">agent_pump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Agent 主循环：把模型输出和工具调用压成事件&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;message_start&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[]}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text_delta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;delta&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我先搜索一下。&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tool_use&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">call_tool&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="c1"># 你的工具执行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tool_result&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;tool_use_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;call_1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text_delta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;delta&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;找到 3 条结果，第一条是……&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;message_stop&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/chat&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">chat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">gen&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">ev&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">agent_pump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;user_input&amp;#34;&lt;/span>&lt;span class="p">]):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;data: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ev&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">StreamingResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">media_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text/event-stream&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>调用方按 &lt;code>type&lt;/code> 分发渲染:&lt;code>text_delta&lt;/code> 追加文本,&lt;code>tool_use&lt;/code> 显示&amp;quot;🔧 正在搜索……&amp;quot;,&lt;code>tool_result&lt;/code> 渲染结果。事件命名建议直接抄 Anthropic 体系(&lt;code>text_delta&lt;/code> / &lt;code>tool_use&lt;/code> / &lt;code>tool_result&lt;/code>),生态里随处可以找到对照实现;用 OpenAI SDK 的话就抄 &lt;code>tool_call_start&lt;/code> / &lt;code>tool_call_complete&lt;/code>,跟你的运行框架一致比追求通用更重要。&lt;/p>
&lt;h3 id="方案-cag-ui--a2ui-标准协议对接现成前端的正解">方案 C：AG-UI / A2UI 标准协议——对接现成前端的正解
&lt;/h3>&lt;p>AG-UI 是 CopilotKit 发起的开源标准,Anthropic 也在推(A2UI),把事件命名&lt;strong>固定下来&lt;/strong>并配上一套前端 SDK。事件类型是一份固定清单:&lt;code>agent_message_chunk&lt;/code>、&lt;code>tool_call_start&lt;/code>、&lt;code>tool_call_detail&lt;/code>、&lt;code>tool_call_end&lt;/code>、&lt;code>artifact&lt;/code>、&lt;code>error&lt;/code>、&lt;code>end&lt;/code>……&lt;/p>
&lt;p>awesome-llm-apps 的 &lt;code>ai-deep-research-agent&lt;/code> 就是一个 FastAPI + AG-UI 的完整部署:把 LangGraph agent 注册成标准端点(代码截图):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># awesome-llm-apps/generative_ui_agents/ai-deep-research-agent/agent/main.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agui_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">copilotkit_customize_config&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emit_tool_calls&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;research&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;write_todos&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;write_file&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;read_file&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;edit_file&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">add_langgraph_fastapi_endpoint&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">app&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">LangGraphAGUIAgent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;research_assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">...&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它展示了一个 AG-UI 特有的实用点——&lt;code>emit_tool_calls&lt;/code>:&lt;strong>只暴露你想让前端看到的工具,子 agent 内部的一堆工具噪声可以过滤掉&lt;/strong>。自己设计事件协议时会漏掉这一点,然后前端被内部工具刷屏。&lt;/p>
&lt;h2 id="五产物文件agent-生成的文件怎么投递">五、产物文件：Agent 生成的文件怎么投递
&lt;/h2>&lt;p>文本和工具事件可以进流,二进制文件不适合塞进 SSH 流。产物通道要为两类对象解耦:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Agent 侧:写完文件 → 产出 artifact 记录(artifact_id + 元数据 + 访问 URL)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">接口侧: GET /artifacts/{id} → 返回文件内容(支持 Range 分段下载)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>事件流里只携带轻量的 artifact 信息,调用方拿到后知识它去 GET。支持 Range 的必要性在于:前端播放音频/视频需要分段请求,断点续传依赖它。&lt;/p>
&lt;p>awesome-llm-apps 的 beifong 就是这个模式——&lt;code>/stream-audio/{filename}&lt;/code> 端点用 &lt;code>StreamingResponse&lt;/code> + Range 头做音频分段播放,Agent 跑完后把音频路径落库,前端拿 URL 再来拉。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># beifong 的 Range 处理（摘要）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">header&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">headers&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Range&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># &amp;#34;bytes=0-1023&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">header&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">file_size&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">StreamingResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_streamer&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">status_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">206&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">headers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Content-Range&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;bytes &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">-&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">end&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">file_size&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">...&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="六决策速查表">六、决策速查表
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>方案&lt;/th>
&lt;th>关键选型&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>离线批处理,只要最终答案&lt;/td>
&lt;td>A 非流式 REST&lt;/td>
&lt;td>返回完整 transcript 数组&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>线上对话,要过程可见/可中断&lt;/td>
&lt;td>B SSE 流式&lt;/td>
&lt;td>事件命名抄 Anthropic 或 OpenAI SDK&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>对接 CopilotKit 等现成前端&lt;/td>
&lt;td>C AG-UI 标准&lt;/td>
&lt;td>用 &lt;code>emit_tool_calls&lt;/code> 过滤子 agent 噪声&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>产物文件(音频/报告/图片)&lt;/td>
&lt;td>独立下载端点&lt;/td>
&lt;td>artifact_id + URL,配 Range 分段&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>工具调用&amp;quot;线&amp;quot;格式&lt;/td>
&lt;td>消息协议&lt;/td>
&lt;td>&lt;code>tool_calls&lt;/code>(JSON 字符串入参)+ &lt;code>role: tool&lt;/code> 回灌&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="七结论">七、结论
&lt;/h2>&lt;p>Agent 输出规范的问题,本质是一个&lt;strong>投递分层&lt;/strong>问题:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>协议层&lt;/strong>(工具调用在消息里的形状)——由 OpenAI/Anthropic 兼容格式定义,不讨论,直接用。&lt;/li>
&lt;li>&lt;strong>投递层&lt;/strong>(怎么把三类输出送出去)——本地给路径,远程走事件流;事件命名跟着你的运行框架走,别自创。&lt;/li>
&lt;li>&lt;strong>标准层&lt;/strong>(要不要对齐 AG-UI)——自用方案 B 够用;要对接开源前端,直接上 AG-UI,别重新发明轮子。&lt;/li>
&lt;/ul>
&lt;p>先把没一行输出删除——把 Agent 的输出当协议设计,而不是当日志设计,是&amp;quot;能上生产&amp;quot;和&amp;quot;能跑 Demo&amp;quot;的分界线。&lt;/p>
&lt;p>&lt;sub>文中仓库实证来自 awesome-llm-apps:流式事件教程 &lt;code>openai_sdk_crash_course/.../4_4_streaming_events/agent.py&lt;/code>、AG-UI 部署 &lt;code>generative_ui_agents/ai-deep-research-agent/agent/main.py&lt;/code>、产物分段下载 &lt;code>beifong/main.py&lt;/code>。&lt;/sub>&lt;/p></description></item><item><title>Agent 沙箱选型指南：隔离边界、产品对比与判断标准</title><link>https://www.zata.cc/p/agent-%E6%B2%99%E7%AE%B1%E9%80%89%E5%9E%8B%E6%8C%87%E5%8D%97%E9%9A%94%E7%A6%BB%E8%BE%B9%E7%95%8C%E4%BA%A7%E5%93%81%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%88%A4%E6%96%AD%E6%A0%87%E5%87%86/</link><pubDate>Wed, 02 Sep 2026 18:00:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E6%B2%99%E7%AE%B1%E9%80%89%E5%9E%8B%E6%8C%87%E5%8D%97%E9%9A%94%E7%A6%BB%E8%BE%B9%E7%95%8C%E4%BA%A7%E5%93%81%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%88%A4%E6%96%AD%E6%A0%87%E5%87%86/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E6%B2%99%E7%AE%B1%E9%80%89%E5%9E%8B%E6%8C%87%E5%8D%97%E9%9A%94%E7%A6%BB%E8%BE%B9%E7%95%8C%E4%BA%A7%E5%93%81%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%88%A4%E6%96%AD%E6%A0%87%E5%87%86/images/index/index.svg" alt="Featured image of post Agent 沙箱选型指南：隔离边界、产品对比与判断标准" />&lt;p>事情是这样的。&lt;/p>
&lt;p>这两天我在给自己的通用 Agent Runtime 加代码执行能力。&lt;/p>
&lt;p>前面都挺顺，模型接上了，文件能传了，多轮对话也存进 SQLite 了。然后我顺手看了一眼 Deep Agents 里面的 &lt;code>LocalShellBackend&lt;/code>，发现只要换掉一行代码，Agent 就能直接运行 Shell。&lt;/p>
&lt;p>那一瞬间确实有点爽。&lt;/p>
&lt;p>前一秒它还只是个会聊天、会读文件的脑子，后一秒它就能写 Python、跑测试、装依赖、改项目。像是给一个飘在空气里的灵魂，突然塞进去一双手。&lt;/p>
&lt;p>然后我又顺手看了一眼它的底层实现。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">command&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shell&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cwd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cwd&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我当时就冷静了。&lt;/p>
&lt;p>这哪是什么沙箱。&lt;/p>
&lt;p>这分明是把我 Mac 的终端密码写在纸条上，然后递给了一个会自己做决定的 AI 牛马。。。&lt;/p>
&lt;p>它当然能干活。但它也能读我的 &lt;code>.env&lt;/code>，能翻 SSH Key，能删文件，能装软件，能访问网络。只要一次 Prompt Injection，一份被投毒的 README，或者模型单纯抽了一下风，事情就可能从「帮我跑个测试」变成「哥们你项目怎么没了」。&lt;/p>
&lt;p>所以我开始认真研究 Agent 沙箱。&lt;/p>
&lt;p>研究完以后，我发现这个领域最容易让人误解的地方，不是产品太少，而是大家把四五种完全不同的东西，全叫成了 Sandbox。&lt;/p>
&lt;p>有的只是限制 Python 能调用哪些函数。&lt;/p>
&lt;p>有的是共享宿主机内核的容器。&lt;/p>
&lt;p>有的是一秒钟启动一台微型虚拟机。&lt;/p>
&lt;p>还有的干脆给 Agent 准备了一台可以暂停、快照、分叉的云电脑。&lt;/p>
&lt;p>名字都一样，安全边界差得十万八千里。&lt;/p>
&lt;p>这篇文章，我就想把这件事彻底聊清楚。&lt;/p>
&lt;p>不一定全对，我自己也还在给 Runtime 做选型。但至少下次再看到「安全执行任意代码」这几个字，我们可以先别急着信，先问一句。&lt;/p>
&lt;p>你这个安全，到底安全在哪？&lt;/p>
&lt;p>1&lt;/p>
&lt;p>先把一个最大的误会拆掉。&lt;/p>
&lt;p>工作目录，不等于沙箱。&lt;/p>
&lt;p>很多 Agent 框架会让你配置一个 &lt;code>workspace&lt;/code>，然后告诉模型，所有文件都在这个目录里操作。听起来像是给它圈了一块地，但如果底层只是普通的 &lt;code>subprocess&lt;/code>，这个目录通常只决定命令从哪里开始运行。&lt;/p>
&lt;p>Agent 依然可以执行 &lt;code>cd ..&lt;/code>，可以读取绝对路径，也可以访问整个网络。&lt;/p>
&lt;p>&lt;code>chroot&lt;/code> 不是完整沙箱，Python 虚拟环境不是沙箱，Conda 不是沙箱，给 Agent 单独建个文件夹更不是沙箱。&lt;/p>
&lt;p>甚至 &lt;code>virtual_mode=True&lt;/code> 这种路径限制，也只能约束框架提供的 &lt;code>read_file&lt;/code> 和 &lt;code>write_file&lt;/code>。一旦 Agent 拿到了 Shell，它直接执行 &lt;code>cat ~/.ssh/id_ed25519&lt;/code>，前面的路径规则就跟门口贴的「闲人免进」差不多。&lt;/p>
&lt;p>LocalShellBackend 也一样。&lt;/p>
&lt;p>它很适合你在完全可信的个人开发环境里快速试验，因为简单，快，而且没有云端延迟。但 Deep Agents 自己在源码里写得很直白，它没有进程隔离，没有资源限制，命令直接以当前用户权限在宿主机运行。&lt;/p>
&lt;p>所以它是执行器，不是沙箱。&lt;/p>
&lt;p>这句话可以记一下。&lt;/p>
&lt;p>能执行代码，和能安全执行代码，中间隔着一整套基础设施。&lt;/p>
&lt;p>那一套基础设施至少要回答5个问题。&lt;/p>
&lt;p>Agent 能看到哪些文件，能不能碰宿主机，能不能访问网络，能拿到哪些密钥，CPU、内存、进程数和执行时间有没有上限。&lt;/p>
&lt;p>少回答一个，那个洞以后都可能变成事故入口。&lt;/p>
&lt;p>2&lt;/p>
&lt;p>最轻的一层，是语言级沙箱。&lt;/p>
&lt;p>典型代表是 WebAssembly、Wasmtime、Deno 权限系统，还有一些基于 QuickJS、Pyodide 的代码解释器。&lt;/p>
&lt;p>这类方案不是给 Agent 一台完整电脑，而是给它一个能力受限的语言运行时。&lt;/p>
&lt;p>以 Wasmtime 为例，WebAssembly 代码默认拿不到宿主机文件、网络和系统调用。它想读某个目录，宿主程序必须通过 WASI 明确把这个能力交给它。官方把这套模型称为 capability-based security，也就是你不给钥匙，它连门在哪里都不知道。&lt;a class="link" href="https://docs.wasmtime.dev/security.html" target="_blank" rel="noopener"
>Wasmtime Security&lt;/a>&lt;/p>
&lt;p>Deno 也有类似的味道。默认情况下，程序不能随便读文件、访问网络、读取环境变量或者启动子进程，必须显式添加 &lt;code>--allow-read&lt;/code>、&lt;code>--allow-net&lt;/code>、&lt;code>--allow-env&lt;/code> 这类权限。&lt;a class="link" href="https://docs.deno.com/runtime/reference/permissions/" target="_blank" rel="noopener"
>Deno Permissions&lt;/a>&lt;/p>
&lt;p>这类沙箱最大的优点就是轻。&lt;/p>
&lt;p>启动快，资源开销小，很适合让 Agent 做公式计算、数据转换、运行一小段 JavaScript 或 Python 子集。你有10万个用户，每个人偶尔让 AI 算个表格，没必要给每个人启动一台 Linux 虚拟机。&lt;/p>
&lt;p>但限制也非常明显。&lt;/p>
&lt;p>真实的软件工程世界，根本不是一个纯函数。&lt;/p>
&lt;p>Agent 可能要运行 &lt;code>git&lt;/code>，装 &lt;code>npm&lt;/code> 包，调用 &lt;code>ffmpeg&lt;/code>，编译 Rust，起一个 PostgreSQL，再用 Playwright 打开浏览器。到了这一步，语言级沙箱很快就会开始劝退你。&lt;/p>
&lt;p>不是它不安全，而是它太安全了。&lt;/p>
&lt;p>安全到很多活干不了。&lt;/p>
&lt;p>所以我的判断很简单。如果你的 Agent 只运行短小、单语言、输入输出明确的代码，Wasm 或受限解释器非常香。如果你想做 Claude Code、Codex 这类真正的软件工程 Agent，就别硬拗了，直接往完整 Linux 环境走。&lt;/p>
&lt;p>3&lt;/p>
&lt;p>再往上一层，是容器。&lt;/p>
&lt;p>也就是大家最熟悉的 Docker。&lt;/p>
&lt;p>Docker 通过 Linux Namespace 隔离进程、网络和挂载点，再用 cgroups 限制 CPU、内存和 I/O。相比在宿主机直接跑 &lt;code>subprocess&lt;/code>，已经安全了太多。Docker 官方的安全文档也把 Namespace、cgroups、Capabilities、seccomp 和 AppArmor 这些能力列为主要防线。&lt;a class="link" href="https://docs.docker.com/engine/security/" target="_blank" rel="noopener"
>Docker Engine Security&lt;/a>&lt;/p>
&lt;p>而且它真的太方便了。&lt;/p>
&lt;p>一个 &lt;code>Dockerfile&lt;/code> 就能把 Python、Node.js、浏览器和项目依赖钉死。镜像可以缓存，容器可以秒级创建，出了问题直接删掉重来。对本地开发、CI 和内部可信 Agent 来说，性价比高得离谱。&lt;/p>
&lt;p>但普通 Linux 容器有一个绕不开的问题。&lt;/p>
&lt;p>它和宿主机共享内核。&lt;/p>
&lt;p>这就像酒店里每个房间都有自己的门锁，但大家共用同一套地基和管道。大多数时候完全够用，可一旦攻击者找到内核漏洞或容器配置错误，边界就可能被打穿。&lt;/p>
&lt;p>如果你想保留容器的使用方式，又不想让应用直接面对宿主机内核，中间还有 gVisor 这条路。它用一个由 Go 编写的用户态应用内核拦截系统调用，Docker 和 Kubernetes 仍然可以通过 OCI Runtime &lt;code>runsc&lt;/code> 来运行容器。代价也很直接，系统调用多的程序会慢一点，部分 Linux 能力不完全兼容。&lt;a class="link" href="https://gvisor.dev/docs/" target="_blank" rel="noopener"
>What is gVisor&lt;/a>&lt;/p>
&lt;p>所以 gVisor 很像夹在普通容器和虚拟机之间的一层。比共享内核的原生容器多一道墙，又没有完整虚拟机那么重。&lt;/p>
&lt;p>更危险的往往还不是内核漏洞，而是我们自己手欠。&lt;/p>
&lt;p>为了让 Agent 能构建镜像，顺手把 &lt;code>/var/run/docker.sock&lt;/code> 挂进容器。&lt;/p>
&lt;p>为了方便改代码，直接把整个项目甚至用户目录读写挂载进去。&lt;/p>
&lt;p>为了少处理几个权限问题，加一个 &lt;code>--privileged&lt;/code>。&lt;/p>
&lt;p>好家伙。&lt;/p>
&lt;p>三板斧下去，沙箱基本只剩图标了。&lt;/p>
&lt;p>Docker 官方也明确提醒，能控制 Docker daemon 的用户，本身就拥有接近宿主机 root 的能力。因为它完全可以创建一个容器，再把宿主机根目录挂进去。&lt;a class="link" href="https://docs.docker.com/engine/security/" target="_blank" rel="noopener"
>Docker daemon attack surface&lt;/a>&lt;/p>
&lt;p>如果你只是做个人开发，Docker 容器依然是一个很现实的起点。但至少要做到非 root 用户、只挂载必要目录、禁止 privileged、不挂 Docker Socket、默认断网、限制 CPU 和内存、设置超时、执行后销毁。&lt;/p>
&lt;p>一开始可能会有点烦。&lt;/p>
&lt;p>尤其依赖缓存、文件同步、Git 权限这些东西，搞起来很容易让人想直接 &lt;code>chmod 777&lt;/code> 然后躺平。但你相信我，沙箱配置里每一次为了省事而开的口子，最后都会变成 Agent 最自由发挥的地方。&lt;/p>
&lt;p>4&lt;/p>
&lt;p>比较骚的事来了。&lt;/p>
&lt;p>就在很多人还把 Docker 容器当成 Agent 沙箱的时候，Docker 自己已经推出了一个就叫 Docker Sandboxes 的产品。&lt;/p>
&lt;p>它不是普通容器套壳，而是给每个编码 Agent 启动独立的 microVM。每个沙箱有自己的内核、文件系统、网络和 Docker Engine，Agent 可以在里面 &lt;code>sudo&lt;/code>、装包、跑 Compose，但碰不到宿主机的 Docker daemon。&lt;a class="link" href="https://docs.docker.com/ai/sandboxes/" target="_blank" rel="noopener"
>Docker Sandboxes&lt;/a>&lt;/p>
&lt;p>而且它已经直接支持 Claude Code、Codex、Copilot、Cursor、Gemini、OpenCode 等一堆 Agent。&lt;a class="link" href="https://docs.docker.com/ai/sandboxes/agents/" target="_blank" rel="noopener"
>Supported agents&lt;/a>&lt;/p>
&lt;p>对本地开发者来说，这个方向我是真的觉得很对。&lt;/p>
&lt;p>以前你要安全运行 Codex，大概有两个选择。要么自己折腾 Docker 和一堆安全参数，要么把代码扔给远程沙箱。现在变成一句命令。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sbx run codex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>微虚拟机里甚至还有一套独立 Docker Engine。Agent 要构建镜像、起数据库、跑 Docker Compose，都在那台 VM 里面折腾。炸了也是炸自己的小房间，不会把宿主机 Docker 一锅端。&lt;/p>
&lt;p>但这里有一颗非常值得提醒的雷。&lt;/p>
&lt;p>Docker Sandboxes 默认会把当前工作区直接读写挂进 VM。Agent 在里面删除代码，你宿主机上的代码也会同步消失。官方提供 &lt;code>--clone&lt;/code> 模式，让 Agent 在 VM 内的私有副本工作，原仓库只读挂载，但这不是默认值。&lt;a class="link" href="https://docs.docker.com/ai/sandboxes/security/" target="_blank" rel="noopener"
>Docker Sandboxes Security&lt;/a>&lt;/p>
&lt;p>所以真要用，我会优先开 clone 模式。&lt;/p>
&lt;p>另外它默认禁止未授权的出站 TCP，凭证通过宿主机代理注入，请求发到被允许的域名时才补上真实密钥。这个设计非常关键，因为密钥压根不以明文进入沙箱。&lt;a class="link" href="https://docs.docker.com/ai/sandboxes/configuration/credentials/" target="_blank" rel="noopener"
>Docker Sandbox Credentials&lt;/a>&lt;/p>
&lt;p>这也是我研究这圈产品以后越来越在意的一条标准。&lt;/p>
&lt;p>一个沙箱如果把 API Key 塞进环境变量，然后告诉我「放心，环境是隔离的」，我会打一个问号。&lt;/p>
&lt;p>因为 Prompt Injection 不需要逃逸沙箱。Agent 自己就能执行 &lt;code>env&lt;/code>，然后把 Key 发出去。&lt;/p>
&lt;p>真正更稳的做法，是让密钥永远留在沙箱外面，由受控代理在指定域名的请求出口临时注入。&lt;/p>
&lt;p>墙是一层。&lt;/p>
&lt;p>不把金库钥匙放进墙里，是另一层。&lt;/p>
&lt;p>5&lt;/p>
&lt;p>如果要把这种微虚拟机能力做成云 API，最知名的玩家之一就是 E2B。&lt;/p>
&lt;p>E2B 用 Firecracker microVM 运行沙箱。它的架构文档里讲得很细，每个 Sandbox 是一个有独立内核的 Linux 微虚拟机，模板会提前启动并做内存、磁盘和 VM 状态快照。创建沙箱时不是从零开机，而是恢复快照，文件系统再用 Copy-on-Write，只拉取真正访问到的数据。&lt;a class="link" href="https://github.com/e2b-dev/infra/blob/main/docs/ARCHITECTURE.md" target="_blank" rel="noopener"
>E2B Architecture&lt;/a>&lt;/p>
&lt;p>所以它能同时拿到两个以前看起来有点冲突的东西。&lt;/p>
&lt;p>虚拟机级隔离，和接近容器的启动速度。&lt;/p>
&lt;p>E2B 的产品心智也特别清晰，就是给 Agent 一台临时 Linux 电脑。你可以运行 Shell，可以读写文件，可以自定义模板。如果只是做数据分析，还有单独的 Code Interpreter SDK，直接运行 Python 或 JavaScript，并返回图表和执行结果。项目本身开源，也支持自托管。&lt;a class="link" href="https://github.com/e2b-dev/e2b" target="_blank" rel="noopener"
>E2B GitHub&lt;/a>&lt;/p>
&lt;p>如果你正在做一个模型无关的代码解释器、数据分析 Agent，或者需要大量短生命周期环境，E2B 很顺手。&lt;/p>
&lt;p>它的问题也很现实。&lt;/p>
&lt;p>这是远程环境，文件要上传，结果要下载，每次工具调用都有网络延迟。自托管虽然开源，但底下是 Firecracker、网络、快照、调度、对象存储和一整套控制面，绝不是周五下午 &lt;code>docker compose up&lt;/code> 一下就能收工的东西。&lt;/p>
&lt;p>我自己看完它的架构，只剩一个感受。&lt;/p>
&lt;p>可以自己部署，和适合自己部署，完全是两回事。。。&lt;/p>
&lt;p>6&lt;/p>
&lt;p>Daytona 和 E2B 看起来很像，但气质不太一样。&lt;/p>
&lt;p>E2B 更像给 AI 应用提供一个通用的安全执行层。Daytona 更像是给 Agent 准备完整、可组合、可长期工作的开发机。&lt;/p>
&lt;p>Daytona 官方把 Sandbox 描述为拥有独立内核、文件系统、网络栈和 vCPU、内存、磁盘配额的计算环境，支持 Python、JavaScript、TypeScript、Shell、持久化会话和快照。&lt;a class="link" href="https://www.daytona.io/docs/en/" target="_blank" rel="noopener"
>Daytona Documentation&lt;/a>&lt;/p>
&lt;p>它比较打动我的一个设计，是 Secret 不一定要真的进入 Sandbox。&lt;/p>
&lt;p>Daytona 可以在沙箱里只放一个占位 Token，出站 HTTPS 请求经过代理时，只有目标域名命中允许列表，代理才把占位符替换成真实密钥。沙箱里的代码看不到明文，日志里也不该出现明文。&lt;a class="link" href="https://www.daytona.io/docs/en/secrets/" target="_blank" rel="noopener"
>Daytona Secrets&lt;/a>&lt;/p>
&lt;p>这个能力对企业内部 Agent 特别重要。&lt;/p>
&lt;p>因为很多 Agent 不是只跑一段 Python，它要拉私有仓库、访问内部 API、查数据库。你不可能永远断网，但你也绝对不想把一个万能 Token 塞给它。域名绑定的代理注入，至少把「能使用凭证」和「能偷走凭证」分开了一点。&lt;/p>
&lt;p>Daytona 还支持有状态解释器、后台 Session、PTY 和长进程，比较适合 Coding Agent、数据流水线，以及需要多轮保留环境的任务。&lt;a class="link" href="https://www.daytona.io/docs/en/process-code-execution/" target="_blank" rel="noopener"
>Daytona Process Execution&lt;/a>&lt;/p>
&lt;p>如果你的 Agent 工作方式像一个工程师，要在同一台机器上连续干几十分钟甚至几小时，我会重点看 Daytona。&lt;/p>
&lt;p>7&lt;/p>
&lt;p>再往工程师工作站这个方向走，就是 Runloop。&lt;/p>
&lt;p>它把沙箱叫 Devbox。&lt;/p>
&lt;p>这个名字其实很诚实，因为它提供的已经不只是安全执行一段代码，而是一台面向 Agent 的云开发机。可以拉仓库、编译代码、跑浏览器、保留状态、暂停恢复，还能使用自定义 Blueprint 和 Snapshot。&lt;a class="link" href="https://docs.runloop.ai/docs/devboxes/overview" target="_blank" rel="noopener"
>Runloop Devbox&lt;/a>&lt;/p>
&lt;p>Runloop 最有意思的场景，是分叉。&lt;/p>
&lt;p>假设 Agent 面前有3种修 Bug 的方案。你可以先给当前磁盘做一个快照，再从同一个快照启动3台 Devbox，让3个 Agent 各走一条路，最后跑测试选最好的那一个。&lt;a class="link" href="https://docs.runloop.ai/docs/devboxes/snapshots" target="_blank" rel="noopener"
>Runloop Snapshots&lt;/a>&lt;/p>
&lt;p>这一下就不只是安全问题了。&lt;/p>
&lt;p>沙箱开始变成 Agent 的时间机器。&lt;/p>
&lt;p>可以回滚，可以复制，可以并行探索。以前工程师在 Git 分支上做的事，现在整个操作系统状态都能分支。&lt;/p>
&lt;p>当然，能力越完整，成本和生命周期治理就越重要。快照如果不清理，会一直占存储。长生命周期 Devbox 如果忘了暂停，账单也会用自己的方式提醒你什么叫长期记忆。&lt;/p>
&lt;p>所以 Runloop 更适合复杂 Coding Agent、自动修复、代码评测和并行实验，不是拿来算 &lt;code>1+1&lt;/code> 的。&lt;/p>
&lt;p>8&lt;/p>
&lt;p>Modal 又是另一种气质。&lt;/p>
&lt;p>它本来就是 Serverless AI 基础设施，Sandbox 只是其中一个能力。所以它特别适合需要弹性并发、定制镜像，甚至 GPU 的 Agent 任务。&lt;/p>
&lt;p>Modal Sandbox 可以动态创建容器，执行任意命令，保留同一沙箱里的状态，设置超时，也可以挂载 Volume。官方甚至专门给了 Claude Code 和 LangGraph Coding Agent 的完整例子。&lt;a class="link" href="https://modal.com/docs/guide/sandboxes" target="_blank" rel="noopener"
>Modal Sandboxes&lt;/a>&lt;/p>
&lt;p>比较夸张的是，你可以直接给 Agent 的沙箱挂一张 T4，让它在里面跑模型或处理视频。&lt;a class="link" href="https://modal.com/docs/examples/agent" target="_blank" rel="noopener"
>Modal LangGraph Agent&lt;/a>&lt;/p>
&lt;p>如果你的 Agent 要做的是普通代码解释，Modal 可能有点像开跑车送外卖。&lt;/p>
&lt;p>但如果任务是视频生成、模型推理、GPU 数据处理，或者突然并发出几千个沙箱，Modal 的基础设施属性就出来了。它不是最纯粹的 Agent Sandbox 产品，但它是一个很强的通用计算平台。&lt;/p>
&lt;p>这块选型其实看任务，不看名气。&lt;/p>
&lt;p>要一台会长期工作的开发机，看 Daytona、Runloop。&lt;/p>
&lt;p>要大量短时解释器，看 E2B、Deno Sandbox。&lt;/p>
&lt;p>要 GPU 和 Serverless 弹性，看 Modal。&lt;/p>
&lt;p>9&lt;/p>
&lt;p>Deno Sandbox 是最近让我有点惊喜的一个新选手。&lt;/p>
&lt;p>它不是前面讲的 Deno 语言权限系统，而是真正的 Linux microVM。官方文档显示，每个沙箱都在 Hypervisor 层隔离，毫秒级启动，可以执行命令、使用持久卷，并且默认是临时环境。&lt;a class="link" href="https://docs.deno.com/sandbox/" target="_blank" rel="noopener"
>Deno Sandbox&lt;/a>&lt;/p>
&lt;p>它在安全设计上也比较激进。&lt;/p>
&lt;p>出站网络可以做严格策略，Secret 不进入环境变量，只在访问批准域名时由平台替换，而且会做结果脱敏。&lt;a class="link" href="https://docs.deno.com/sandbox/security/" target="_blank" rel="noopener"
>Deno Sandbox Security&lt;/a>&lt;/p>
&lt;p>目前它更像一个快速发展的新产品，默认资源和会话时长有明确限制，区域也不像成熟云厂商那么广。但对于 TypeScript、Deno 生态和边缘应用来说，它非常值得关注。&lt;/p>
&lt;p>顺便说一句，这里特别容易混淆。&lt;/p>
&lt;p>&lt;code>deno run&lt;/code> 的权限沙箱，是语言级能力控制。&lt;/p>
&lt;p>Deno Sandbox，是云端 Linux microVM。&lt;/p>
&lt;p>一个名字，两层边界。&lt;/p>
&lt;p>买东西之前真的得看说明书，不然很容易拿到一把儿童安全剪刀，然后以为自己租了一间银行金库。&lt;/p>
&lt;p>10&lt;/p>
&lt;p>如果公司已经深度在 AWS 里，Amazon Bedrock AgentCore Code Interpreter 会更顺。&lt;/p>
&lt;p>它提供托管的 Python 执行环境，能做计算、数据分析、可视化和结果校验。网络可以选择 Sandbox、Public 或 VPC 模式，访问 AWS 资源则由 IAM Role 控制。&lt;a class="link" href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/code-interpreter-resource-management.html" target="_blank" rel="noopener"
>AgentCore Code Interpreter&lt;/a>&lt;/p>
&lt;p>它的优势不是「最适合所有 Agent」，而是 AWS 那套治理能力。&lt;/p>
&lt;p>IAM、VPC、Security Group、CloudTrail、企业账户、合规控制，这些东西一旦进了大公司，比单纯启动快几十毫秒重要得多。Agent 要访问 S3、EFS、内部数据库，也有一条相对原生的路。&lt;/p>
&lt;p>但 AWS 的代价大家也懂。&lt;/p>
&lt;p>概念多，配置多，权限策略写着写着，人会逐渐进入一种我是谁我在哪的哲学状态。&lt;/p>
&lt;p>LangSmith Sandbox 也属于生态型选择。它和 LangGraph、Deep Agents 的衔接自然，支持命令、文件、端口隧道和工作区权限。但截至我写这篇文章时，官方文档仍标记为 Private Preview，所以更适合已经重度使用 LangSmith、愿意跟着产品一起迭代的团队。&lt;a class="link" href="https://docs.langchain.com/langsmith/sandbox-permissions" target="_blank" rel="noopener"
>LangSmith Sandbox Permissions&lt;/a>&lt;/p>
&lt;p>11&lt;/p>
&lt;p>聊了这么多，我做了一张尽量不骗人的表。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>方案&lt;/th>
&lt;th>隔离边界&lt;/th>
&lt;th>最适合什么&lt;/th>
&lt;th>最明显的短板&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LocalShellBackend&lt;/td>
&lt;td>没有隔离&lt;/td>
&lt;td>可信个人实验&lt;/td>
&lt;td>直接执行宿主机命令&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Wasm、受限解释器&lt;/td>
&lt;td>语言运行时&lt;/td>
&lt;td>短代码、计算、转换&lt;/td>
&lt;td>系统工具和依赖受限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>普通 Docker&lt;/td>
&lt;td>共享内核容器&lt;/td>
&lt;td>本地开发、CI、可信内部任务&lt;/td>
&lt;td>配置不当容易穿透边界&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Docker Sandboxes&lt;/td>
&lt;td>本地 microVM&lt;/td>
&lt;td>Codex、Claude Code 等编码 Agent&lt;/td>
&lt;td>默认直挂工作区仍有风险&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>E2B&lt;/td>
&lt;td>云端 Firecracker microVM&lt;/td>
&lt;td>Code Interpreter、短时 Agent 任务&lt;/td>
&lt;td>远程延迟，自托管复杂&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Daytona&lt;/td>
&lt;td>云端隔离开发环境&lt;/td>
&lt;td>有状态 Coding Agent、企业内部 Agent&lt;/td>
&lt;td>引入外部平台与费用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Runloop&lt;/td>
&lt;td>云端虚拟 Devbox&lt;/td>
&lt;td>长任务、快照分叉、并行修复&lt;/td>
&lt;td>生命周期和存储治理更重&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Modal&lt;/td>
&lt;td>Serverless 隔离容器&lt;/td>
&lt;td>高并发、GPU、定制计算&lt;/td>
&lt;td>对简单解释器可能偏重&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Deno Sandbox&lt;/td>
&lt;td>云端 Linux microVM&lt;/td>
&lt;td>快速临时环境、Deno 与 TS 生态&lt;/td>
&lt;td>产品较新，资源与区域有限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AgentCore&lt;/td>
&lt;td>AWS 托管解释器&lt;/td>
&lt;td>AWS 企业环境、VPC 与 IAM 集成&lt;/td>
&lt;td>云绑定和配置复杂度高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangSmith Sandbox&lt;/td>
&lt;td>LangChain 托管沙箱&lt;/td>
&lt;td>LangGraph、Deep Agents 团队&lt;/td>
&lt;td>当前仍处预览阶段&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>如果屏幕前的你，现在就在做自己的 Agent，我自己的不成熟建议是这样。&lt;/p>
&lt;p>个人在 Mac 上玩 Coding Agent，先看 Docker Sandboxes，尽量用 &lt;code>--clone&lt;/code>，别让 Agent 直接改宿主机工作区。只是偶尔跑一点可信代码，硬化后的 Docker 也够用。&lt;/p>
&lt;p>做一个面向用户的 Code Interpreter，优先试 E2B、Deno Sandbox，或者 Daytona。先把文件上传、命令执行、超时、结果下载这条链跑通，再考虑自建。&lt;/p>
&lt;p>做企业内部 Coding Agent，看 Daytona、Runloop，或者你所在云厂商的托管方案。重点不是 Demo 跑得多快，而是身份、审计、网络、密钥和数据驻留能不能交代清楚。&lt;/p>
&lt;p>需要 GPU、高并发和复杂镜像，看 Modal。&lt;/p>
&lt;p>已经全家桶 AWS，就认真评估 AgentCore，别为了技术洁癖硬造一套 IAM 和 VPC。&lt;/p>
&lt;p>至于自己用 Docker 或 Firecracker 搭一套，我不是说不行。&lt;/p>
&lt;p>但你得诚实评估一下，你到底是在做 Agent 产品，还是准备顺便创业做一家云计算公司。&lt;/p>
&lt;p>12&lt;/p>
&lt;p>最后再说几个我觉得比产品名字更重要的判断标准。&lt;/p>
&lt;p>沙箱是不是每个用户、每个 Thread 独立。不同用户共用一个长生命周期环境，文件和进程串了，那就不是记忆，是串门。&lt;/p>
&lt;p>网络是不是默认拒绝。只要默认全网可达，Prompt Injection 就有了天然的数据出口。&lt;/p>
&lt;p>密钥是不是明文进环境变量。最好由外部代理按域名注入，而且日志、错误信息和响应都要脱敏。&lt;/p>
&lt;p>宿主机目录是不是直接读写挂载。尤其 &lt;code>.git/hooks&lt;/code>、CI 配置、IDE Task、&lt;code>.claude&lt;/code>、&lt;code>.codex&lt;/code> 这些文件，有些改动甚至不会出现在普通 &lt;code>git diff&lt;/code> 里。&lt;/p>
&lt;p>有没有 CPU、内存、磁盘、PID、输出大小和墙钟时间限制。超时只杀父进程不杀进程组，也可能留下一窝后台孤儿。&lt;/p>
&lt;p>有没有快照和销毁策略。沙箱太短，装一次依赖等半天。沙箱太长，污染、成本和跨任务泄漏一起上来。&lt;/p>
&lt;p>有没有完整审计。谁在什么时间，以哪个 Agent 身份，执行了哪条命令，读写了哪些文件，访问了哪个域名，最后退出码是什么。&lt;/p>
&lt;p>还有一个经常被忘掉的点。&lt;/p>
&lt;p>沙箱防得住代码逃到宿主机，但防不住 Agent 在沙箱里做坏事。&lt;/p>
&lt;p>如果网络是开放的，Agent 仍然可以把用户上传的文件发走。如果你把数据库密钥放进去，它仍然可以把库删掉。如果它能调用宿主机上的高权限 MCP，那个 MCP 就是墙上新开的一扇门。&lt;/p>
&lt;p>所以沙箱从来不是一句「安全了」。&lt;/p>
&lt;p>它只是把事故半径，从整台电脑，缩小到一个可控房间。&lt;/p>
&lt;p>我写到这里，突然想起计算机安全里一个特别老的原则，最小权限。&lt;/p>
&lt;p>这个词听起来一点都不性感，甚至有点像公司安全培训里最容易被跳过的那页 PPT。&lt;/p>
&lt;p>但 Agent 时代，它突然变得非常具体。&lt;/p>
&lt;p>以前的软件权限是开发者写死的。一个图片处理程序，正常情况下不会突然决定去翻你的 SSH Key。&lt;/p>
&lt;p>Agent 不一样。&lt;/p>
&lt;p>它的能力边界是动态的，它会读新的内容，会形成新的计划，会把几个看起来无害的工具串起来。模型越聪明，越能自己找到完成目标的路，也越需要我们提前决定，哪些路从物理上就不应该存在。&lt;/p>
&lt;p>这有点像养一只特别聪明的哈士奇。&lt;/p>
&lt;p>你不能把家门钥匙、银行卡和电锯全扔给它，然后靠 System Prompt 写一句「你是一只可靠、简洁、不会拆家的狗」。&lt;/p>
&lt;p>它今天不拆，不代表这套系统是安全的。&lt;/p>
&lt;p>真正的安全，是它就算想拆，也只能拆自己的玩具屋。&lt;/p>
&lt;p>回到我最开始那行 &lt;code>subprocess.run&lt;/code>。&lt;/p>
&lt;p>代码还是那几行，Agent 也还是那个 Agent。但当它从宿主机 Shell 被放进一个有独立文件系统、受控网络、外置密钥和资源上限的沙箱以后，它才真正从一个危险的 Demo，开始有了一点产品的样子。&lt;/p>
&lt;p>手，是给了。&lt;/p>
&lt;p>笼子，也得跟上。&lt;/p>
&lt;p>大时代啊，朋友们。&lt;/p>
&lt;p>以上，既然看到这里了，如果觉得不错，随手点个赞、在看、转发三连吧，如果想第一时间收到推送，也可以给我个星标⭐～&lt;/p>
&lt;p>谢谢你看我的文章，我们，下次再见。&lt;/p></description></item><item><title>让用户选择指定 Skill：从社区实践到生产级 API 设计</title><link>https://www.zata.cc/p/%E8%AE%A9%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E6%8C%87%E5%AE%9A-skill%E4%BB%8E%E7%A4%BE%E5%8C%BA%E5%AE%9E%E8%B7%B5%E5%88%B0%E7%94%9F%E4%BA%A7%E7%BA%A7-api-%E8%AE%BE%E8%AE%A1/</link><pubDate>Wed, 02 Sep 2026 15:00:00 +0800</pubDate><guid>https://www.zata.cc/p/%E8%AE%A9%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E6%8C%87%E5%AE%9A-skill%E4%BB%8E%E7%A4%BE%E5%8C%BA%E5%AE%9E%E8%B7%B5%E5%88%B0%E7%94%9F%E4%BA%A7%E7%BA%A7-api-%E8%AE%BE%E8%AE%A1/</guid><description>&lt;img src="https://www.zata.cc/p/%E8%AE%A9%E7%94%A8%E6%88%B7%E9%80%89%E6%8B%A9%E6%8C%87%E5%AE%9A-skill%E4%BB%8E%E7%A4%BE%E5%8C%BA%E5%AE%9E%E8%B7%B5%E5%88%B0%E7%94%9F%E4%BA%A7%E7%BA%A7-api-%E8%AE%BE%E8%AE%A1/images/index/index.svg" alt="Featured image of post 让用户选择指定 Skill：从社区实践到生产级 API 设计" />&lt;p>最近在做 Agent 运行接口时，我碰到了一个看起来很小、真正实现起来却牵动整条运行链的问题。&lt;/p>
&lt;p>系统已经加载了多个 Skill，模型也会根据用户问题自动挑选。现在产品希望再往前走一步，让用户在发送消息前直接选择「这次就用 freight-quote」。接口该怎么设计？是不是在请求里加一个 &lt;code>skill_id&lt;/code> 就结束了？&lt;/p>
&lt;p>我的判断是，&lt;code>skill_id&lt;/code> 确实应该加，但它只是入口。一个能上线的显式 Skill 选择方案，还必须同时解决可见范围、授权、强制激活、版本快照、幂等和审计。否则界面看起来是用户选中了 Skill，Runtime 实际上仍可能没用它。&lt;/p>
&lt;h2 id="一先分清三个经常混在一起的概念">一、先分清三个经常混在一起的概念
&lt;/h2>&lt;p>讨论接口之前，先把 Skill 的三个状态拆开。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Agent 被允许使用哪些 Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">用户这次请求选择了哪个 Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Runtime 最终激活了哪些 Skill
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它们分别对应能力范围、用户意图和执行事实。&lt;/p>
&lt;p>一个 Agent 可以被管理员配置为允许使用 &lt;code>freight-quote&lt;/code> 和 &lt;code>document-recognition&lt;/code>。用户本次只选择 &lt;code>freight-quote&lt;/code>，Runtime 就应当加载它，并把最终激活结果记录下来。用户不能因为知道另一个 Skill 的名字，就越过 Agent 配置直接调用。&lt;/p>
&lt;p>如果不做这层区分，后面很容易出现两类问题。&lt;/p>
&lt;p>一类是权限问题。前端隐藏了 Skill，但用户手工构造请求仍能调用；另一类是事实问题。用户选择了 Skill，后端只是往 Prompt 里加了一句「请使用 freight-quote」，模型最后没有读取对应的 &lt;code>SKILL.md&lt;/code>，系统却把这次执行展示成已使用。&lt;/p>
&lt;p>所以，显式选择不只是一个 UI 功能，它是新的运行契约。&lt;/p>
&lt;h2 id="二agent-skills-规范解决了什么又没解决什么">二、Agent Skills 规范解决了什么，又没解决什么
&lt;/h2>&lt;p>&lt;a class="link" href="https://agentskills.io/specification" target="_blank" rel="noopener"
>Agent Skills 开放规范&lt;/a>定义的是 Skill 包格式。一个 Skill 至少包含一个 &lt;code>SKILL.md&lt;/code>，front matter 中必须有稳定的 &lt;code>name&lt;/code> 和用于发现的 &lt;code>description&lt;/code>，还可以包含 &lt;code>scripts/&lt;/code>、&lt;code>references/&lt;/code> 和 &lt;code>assets/&lt;/code>。&lt;/p>
&lt;p>它推荐渐进式披露。Agent 启动时只读取所有 Skill 的名称和描述；决定激活某个 Skill 后，再加载完整指令；执行过程中按需读取脚本和参考资料。这样既能保留大量能力，又不会一开始就把所有内容塞进上下文。&lt;/p>
&lt;p>但规范没有定义 HTTP 请求应该长什么样，也没有规定 &lt;code>/skill-name&lt;/code>、Picker 或 &lt;code>skill_id&lt;/code>。这不是规范遗漏，而是边界划分。Skill 包应该能够跨宿主复用，至于谁能调用、怎么选择、如何审计，需要由 Codex、VS Code、Deep Agents 或业务平台自己决定。&lt;/p>
&lt;p>因此，在自己的 API 中增加结构化 &lt;code>skill_id&lt;/code>，不会破坏 Agent Skills 兼容性。真正需要避免的是把平台权限、运行状态等私有字段硬塞进通用 &lt;code>SKILL.md&lt;/code>，让内容包和业务控制面耦合在一起。&lt;/p>
&lt;h2 id="三开源社区的几种典型做法">三、开源社区的几种典型做法
&lt;/h2>&lt;h3 id="1-deep-agents让模型根据描述自动选择">1. Deep Agents，让模型根据描述自动选择
&lt;/h3>&lt;p>&lt;a class="link" href="https://docs.langchain.com/oss/python/deepagents/skills" target="_blank" rel="noopener"
>LangChain Deep Agents&lt;/a>在创建 Agent 时接收 Skill 来源目录。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_deep_agent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">skills&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/skills/&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>SkillsMiddleware&lt;/code> 扫描目录，将每个 Skill 的 &lt;code>name&lt;/code> 和 &lt;code>description&lt;/code> 注入系统上下文。模型判断任务匹配后，再通过文件能力读取完整 &lt;code>SKILL.md&lt;/code>。&lt;/p>
&lt;p>这种模式很适合自动路由，也支持给不同 Agent 或 Subagent 配置不同 Skill 集合。但它没有提供一个通用的 &lt;code>invoke(skill_id=...)&lt;/code> 契约。也就是说，Deep Agents 原生解决的是「哪些 Skill 可被发现」，不是「用户指定后如何强制激活」。&lt;/p>
&lt;h3 id="2-vs-code把-skill-变成可选择的-slash-command">2. VS Code，把 Skill 变成可选择的 Slash Command
&lt;/h3>&lt;p>&lt;a class="link" href="https://code.visualstudio.com/docs/agent-customization/agent-skills" target="_blank" rel="noopener"
>VS Code 的 Agent Skills&lt;/a>同时支持模型自动加载和用户显式调用。用户可以在聊天框输入：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">/webapp-testing for the login page
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Skill 还可以通过 front matter 控制两个方向的调用权限。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>配置&lt;/th>
&lt;th style="text-align: right">用户可调用&lt;/th>
&lt;th style="text-align: right">模型可自动调用&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>默认&lt;/td>
&lt;td style="text-align: right">是&lt;/td>
&lt;td style="text-align: right">是&lt;/td>
&lt;td>通用 Skill&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>user-invocable: false&lt;/code>&lt;/td>
&lt;td style="text-align: right">否&lt;/td>
&lt;td style="text-align: right">是&lt;/td>
&lt;td>只作为后台知识或流程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>disable-model-invocation: true&lt;/code>&lt;/td>
&lt;td style="text-align: right">是&lt;/td>
&lt;td style="text-align: right">否&lt;/td>
&lt;td>必须由用户主动启动&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>两者同时设置&lt;/td>
&lt;td style="text-align: right">否&lt;/td>
&lt;td style="text-align: right">否&lt;/td>
&lt;td>暂停或隐藏&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这里最值得借鉴的不是 Slash Command 的外观，而是显式调用后的语义。宿主会直接加载对应 Skill 的完整指令，不会把 &lt;code>/webapp-testing&lt;/code> 当成一段普通用户文字，再赌模型能不能正确理解。&lt;/p>
&lt;h3 id="3-jan把调用方式和能力白名单拆开">3. Jan，把调用方式和能力白名单拆开
&lt;/h3>&lt;p>&lt;a class="link" href="https://www.jan.ai/docs/agent/skill-invocation" target="_blank" rel="noopener"
>Jan 的 Skill Invocation&lt;/a>支持 &lt;code>/release&lt;/code> 和 &lt;code>/skill:release staging&lt;/code> 这类显式命令，同时还提供独立的 Skill 白名单。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">skills&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">enabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;release&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;security-review&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这体现了一个很重要的工程原则。Skill 是否启用，与这一次由用户还是模型触发，是两个正交维度。一个 Skill 必须先在 Agent 的可用集合里，之后才谈得上显式或自动调用。&lt;/p>
&lt;h3 id="4-codex允许自动选择也允许用户点名">4. Codex，允许自动选择，也允许用户点名
&lt;/h3>&lt;p>OpenAI 在 &lt;a class="link" href="https://openai.com/index/introducing-the-codex-app/" target="_blank" rel="noopener"
>Codex app 的公开介绍&lt;/a>中明确说明，用户可以要求 Codex 使用指定 Skill，也可以让 Codex根据任务自动选择。&lt;/p>
&lt;p>社区方案看起来各不相同，但方向其实已经很清楚。产品一般不会在自动和手动之间二选一，而是同时保留两种入口，再由宿主系统负责权限和激活。&lt;/p>
&lt;h2 id="四为什么只往-prompt-里拼一句话不够">四、为什么只往 Prompt 里拼一句话不够
&lt;/h2>&lt;p>最省事的实现大概是这样。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">question&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;请使用 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">skill_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> skill。&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_question&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它能在 Demo 里工作，却很难成为可信的运行边界。&lt;/p>
&lt;p>模型可能看到了 Skill 名称，但没有读取 &lt;code>SKILL.md&lt;/code>；用户输入也可能尝试覆盖前面的要求；Skill 内容更新后，历史 Run 无法证明当时使用的是哪个版本。更麻烦的是，审计日志只能证明系统发送过一句提示，不能证明 Runtime 真正激活过对应流程。&lt;/p>
&lt;p>这里要承认一个边界。即使后端完整注入了 Skill 指令，也无法数学意义上保证概率模型百分之百遵守。不过平台至少应该保证三件可验证的事实。&lt;/p>
&lt;ol>
&lt;li>用户选择的 Skill 经过了授权校验。&lt;/li>
&lt;li>运行上下文确实加载了这个版本的完整 Skill。&lt;/li>
&lt;li>执行记录保存了请求选择和实际激活结果。&lt;/li>
&lt;/ol>
&lt;p>把这三件事做好，才算把不确定性限制在模型执行层，而不是让接口和运行时本身也含糊不清。&lt;/p>
&lt;h2 id="五一套适合生产系统的-api-设计">五、一套适合生产系统的 API 设计
&lt;/h2>&lt;h3 id="1-先让前端查询这个-agent-能用什么">1. 先让前端查询这个 Agent 能用什么
&lt;/h3>&lt;p>建议提供 Agent 作用域下的 Skill 列表，而不是一个不带权限语境的全局列表。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-http" data-lang="http">&lt;span class="line">&lt;span class="cl">&lt;span class="err">GET /api/agents/{agent_id}/skills
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;items&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;freight-quote&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;运费报价&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;根据尺寸、重量和运输方式估算运费&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;user_invocable&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;model_invocable&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个接口只返回当前用户可见、当前 Agent 已授权并且运行环境可用的 Skill。文件路径、服务器目录和内部凭据不应该出现在响应里。&lt;/p>
&lt;h3 id="2-run-请求使用结构化选择">2. Run 请求使用结构化选择
&lt;/h3>&lt;p>创建 Run 时可以引入 &lt;code>skill_selection&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;agent_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;agent_123&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skill_selection&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;required&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skill_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;freight-quote&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2 个箱子，每个 1×1×1 米，毛重 300kg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我倾向于保留三种模式。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>mode&lt;/th>
&lt;th>Runtime 行为&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>auto&lt;/code>&lt;/td>
&lt;td>从 Agent 允许集合中自动匹配&lt;/td>
&lt;td>普通对话和兼容现有行为&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>required&lt;/code>&lt;/td>
&lt;td>强制加载指定 Skill&lt;/td>
&lt;td>用户在 Picker 或 Slash Command 中点名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>none&lt;/code>&lt;/td>
&lt;td>本次不向模型暴露 Skill&lt;/td>
&lt;td>基础问答、隔离测试或安全场景&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>第一期如果想控制改动范围，也可以只增加可空的 &lt;code>skill_id&lt;/code>，未传时等价于 &lt;code>auto&lt;/code>。但显式 &lt;code>mode&lt;/code> 更容易表达「不使用 Skill」，也为以后支持多个 Skill 留出了边界。&lt;/p>
&lt;h3 id="3-slash-command-只是一种输入方式">3. Slash Command 只是一种输入方式
&lt;/h3>&lt;p>前端完全可以提供熟悉的交互。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">/freight-quote 帮我计算这票货
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>但发送前应该把它解析成结构化请求。数据库和 Runtime 不应依赖解析原始文本中的 &lt;code>/&lt;/code> 命令，因为移动端 Picker、工作流节点和 API 客户端未必使用同一种文本语法。&lt;/p>
&lt;h2 id="六runtime-应该如何真正激活指定-skill">六、Runtime 应该如何真正激活指定 Skill
&lt;/h2>&lt;p>对 &lt;code>auto&lt;/code> 模式，可以继续使用 Deep Agents 默认的渐进式发现，让模型在允许集合中选择。&lt;/p>
&lt;p>对 &lt;code>required&lt;/code> 模式，我更推荐由宿主在 invocation 开始前读取并验证 &lt;code>SKILL.md&lt;/code>，把完整指令放入受信任的运行上下文，同时只暴露该 Skill 所需且被授权的资源。这样做会失去一点渐进加载带来的 token 优势，但一次只加载一个指定 Skill，成本通常可控，语义也更明确。&lt;/p>
&lt;p>如果系统在启动时编译一个包含全部 Skill 的 Agent 单例，还要再做一步改造。运行时实例应该按有效配置缓存，而不是让所有 Agent 和所有 Run 永远共用同一个 Skill 集合。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">runtime_connection_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ runtime_source_agent_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ skill_bundle_checksum
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+ model_configuration_checksum
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">= compiled agent cache key
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样既不必每次 Run 都重新构建 Agent，也不会让一个只允许使用 &lt;code>freight-quote&lt;/code> 的 Agent 意外看到另一个部门的 Skill。&lt;/p>
&lt;p>对于存在依赖的 Skill，可以把请求选择和有效集合分开。用户选择一个主 Skill，后端解析出它允许依赖的辅助 Skill，最终形成不可变的 effective bundle。不要允许客户端直接提交任意路径或任意依赖列表。&lt;/p>
&lt;h2 id="七为什么-skill-快照必须进入-run">七、为什么 Skill 快照必须进入 Run
&lt;/h2>&lt;p>生产系统中的 Run 不只是一次临时函数调用，它往往还承担重放、审计、故障分析和结果归因。&lt;/p>
&lt;p>建议在 Run 创建时冻结以下信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skill_selection&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;required&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requested_skill_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;freight-quote&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;effective_skill_ids&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;freight-quote&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skill_snapshots&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skill_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;freight-quote&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;content_checksum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sha256:...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;platform&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里的 checksum 很关键。Skill 作者明天修改了 &lt;code>SKILL.md&lt;/code>，历史记录仍然能够说明昨天的 Run 看到了什么。它还应该进入幂等请求摘要，否则同一个 &lt;code>Idempotency-Key&lt;/code> 在不同 Skill 下可能错误地重放同一条 Run。&lt;/p>
&lt;p>如果 Skill 自带脚本和参考文件，仅计算 &lt;code>SKILL.md&lt;/code> 并不总是够。更稳妥的做法是为整个可执行 Skill 包计算内容清单和 bundle checksum，至少覆盖本次可能读取的受管资源。&lt;/p>
&lt;h2 id="八安全边界不能交给前端">八、安全边界不能交给前端
&lt;/h2>&lt;p>用户选择 Skill 后，后端至少要执行下面这条校验链。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">解析 skill_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">确认 Skill 存在且版本可用
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">确认当前用户有权运行该 Agent
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">确认 Skill 属于该 Agent 的 allowed_skill_ids
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">确认允许用户显式调用
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">冻结快照并创建 Run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Runtime 加载有效 Skill bundle
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>客户端只能传稳定 ID，不能传 &lt;code>skills_paths&lt;/code>、&lt;code>SKILL.md&lt;/code> 内容或本机目录。对普通用户，未找到和无权限最好采用防枚举语义，不泄露其他 Agent 安装了什么能力。&lt;/p>
&lt;p>另外，Skill 的 &lt;code>allowed-tools&lt;/code> 目前在开放规范中仍属于实验字段，不同宿主支持程度不同。生产系统不能只靠 front matter 当权限控制。真正的工具白名单、文件边界、网络访问和高风险操作确认，仍要由 Runtime 和基础设施层强制执行。&lt;/p>
&lt;h2 id="九怎么分阶段落地">九、怎么分阶段落地
&lt;/h2>&lt;p>这项改造不需要一步做到完整市场和版本仓库，可以分成三个阶段。&lt;/p>
&lt;h3 id="阶段一跑通单-skill-显式选择">阶段一，跑通单 Skill 显式选择
&lt;/h3>&lt;ul>
&lt;li>解析现有 &lt;code>SKILL.md&lt;/code> 的 &lt;code>name&lt;/code> 和 &lt;code>description&lt;/code>&lt;/li>
&lt;li>提供 Agent 作用域的 Skill 查询接口&lt;/li>
&lt;li>Run 请求增加 &lt;code>skill_selection&lt;/code>&lt;/li>
&lt;li>校验 &lt;code>skill_id&lt;/code>，并在 &lt;code>required&lt;/code> 模式预加载完整 Skill&lt;/li>
&lt;li>把 Skill ID 和内容 checksum 写入 Run 快照及幂等摘要&lt;/li>
&lt;/ul>
&lt;h3 id="阶段二建立-agent-与-skill-的授权关系">阶段二，建立 Agent 与 Skill 的授权关系
&lt;/h3>&lt;ul>
&lt;li>为 Agent 增加 &lt;code>allowed_skill_ids&lt;/code>&lt;/li>
&lt;li>管理端支持绑定和解绑&lt;/li>
&lt;li>区分 &lt;code>user_invocable&lt;/code> 与 &lt;code>model_invocable&lt;/code>&lt;/li>
&lt;li>Runtime 按有效 Skill bundle 缓存编译结果&lt;/li>
&lt;li>事件或 Trace 中记录 Skill 激活事实&lt;/li>
&lt;/ul>
&lt;h3 id="阶段三再考虑版本和依赖治理">阶段三，再考虑版本和依赖治理
&lt;/h3>&lt;ul>
&lt;li>Skill 版本发布、启用与回滚&lt;/li>
&lt;li>依赖解析和 bundle checksum&lt;/li>
&lt;li>组织、部门和用户级可见性&lt;/li>
&lt;li>风险分级、签名校验和供应链扫描&lt;/li>
&lt;li>对 Skill 选择正确率、失败率和成本做评估&lt;/li>
&lt;/ul>
&lt;p>这里容易踩的坑，是一开始就做一个很重的 Skill Marketplace，却还没有证明用户为什么要选、选完后能否稳定执行。先把「选择、授权、激活、记录」这条最短闭环跑通，通常更划算。&lt;/p>
&lt;h2 id="十我的判断">十、我的判断
&lt;/h2>&lt;p>如果系统只是个人助手，用户在 Prompt 中写一句「使用某某 Skill」可能已经够用。它实现快，失败后影响也有限。&lt;/p>
&lt;p>但只要系统出现多租户、多个 Business Agent、高风险工具或运行历史，Skill 就不再只是 Prompt 模板。它开始接近一个带权限、版本和执行策略的能力包。此时，显式选择必须成为结构化 API 和不可变运行事实。&lt;/p>
&lt;p>我最终会采用这样的组合。&lt;/p>
&lt;ul>
&lt;li>用 Agent Skills 规范维护可移植的内容包&lt;/li>
&lt;li>用 Agent 的 &lt;code>allowed_skill_ids&lt;/code> 管理能力边界&lt;/li>
&lt;li>用 &lt;code>skill_selection&lt;/code> 表达本次用户意图&lt;/li>
&lt;li>用宿主预加载实现 &lt;code>required&lt;/code> 语义&lt;/li>
&lt;li>用 Skill snapshot 和 checksum 保证历史可追踪&lt;/li>
&lt;li>用 Picker 或 Slash Command 改善交互，但不让文本语法成为后端契约&lt;/li>
&lt;/ul>
&lt;p>这套设计比简单增加一个字段多做了一些工作，但这些工作不是过度设计。它们正好对应一个生产 Agent 最难回答的几个问题：谁允许它做、用户要求它做什么、它运行时到底加载了什么，以及事后能不能证明。&lt;/p>
&lt;h2 id="总结">总结
&lt;/h2>&lt;p>用户显式选择 Skill，真正要落地的是一条完整的执行链，而不是一句 Prompt。&lt;/p>
&lt;ol>
&lt;li>Agent Skills 规范负责内容包和渐进加载，不负责业务 API。&lt;/li>
&lt;li>社区主流是自动选择与显式选择并存，由宿主负责调用语义。&lt;/li>
&lt;li>Agent 允许集合、用户请求选择和 Runtime 实际激活必须分开记录。&lt;/li>
&lt;li>&lt;code>required&lt;/code> 模式应该由后端加载完整 Skill，不能再交给模型二次决定。&lt;/li>
&lt;li>Skill 版本和内容 checksum 应进入 Run 快照与幂等摘要。&lt;/li>
&lt;/ol>
&lt;p>界面上的 Skill Picker 很轻，背后真正有价值的，是把一次看似随意的模型选择，变成可以授权、复现和审计的运行事实。&lt;/p></description></item><item><title>Agent 用户记忆与 Skill 沉淀：开源项目参考与架构设计</title><link>https://www.zata.cc/p/agent-%E7%94%A8%E6%88%B7%E8%AE%B0%E5%BF%86%E4%B8%8E-skill-%E6%B2%89%E6%B7%80%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%8F%82%E8%80%83%E4%B8%8E%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</link><pubDate>Mon, 31 Aug 2026 18:00:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E7%94%A8%E6%88%B7%E8%AE%B0%E5%BF%86%E4%B8%8E-skill-%E6%B2%89%E6%B7%80%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%8F%82%E8%80%83%E4%B8%8E%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E7%94%A8%E6%88%B7%E8%AE%B0%E5%BF%86%E4%B8%8E-skill-%E6%B2%89%E6%B7%80%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%8F%82%E8%80%83%E4%B8%8E%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/images/index/index.svg" alt="Featured image of post Agent 用户记忆与 Skill 沉淀：开源项目参考与架构设计" />&lt;p>当 Agent 可以替换、模型持续升级时，真正应该长期留在平台中的，不是某个 Agent 的私有会话状态，而是用户拥有的资料、记忆和可复用 Skill。&lt;/p>
&lt;p>目前还没有一个开源项目同时做好跨 Agent 用户记忆、来源追溯、权限、删除、Skill 版本、评测和发布治理。比较现实的路线是组合借鉴：用成熟项目解决提取、检索和加载问题，由平台自己负责资产归属与治理。&lt;/p>
&lt;p>本文回答三个问题：&lt;/p>
&lt;ol>
&lt;li>用户记忆可以参考哪些项目？&lt;/li>
&lt;li>Skill 应该如何沉淀、加载和演进？&lt;/li>
&lt;li>如何把这些机制组合成一个面向生产环境的 Agent Harness？&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="1-先区分-memoryexperience-和-skill">1. 先区分 Memory、Experience 和 Skill
&lt;/h2>&lt;p>长期数据不应全部塞进一个向量库。更清晰的分类是：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>回答的问题&lt;/th>
&lt;th>推荐资产&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Semantic Memory&lt;/td>
&lt;td>用户是谁、偏好什么、有哪些稳定事实？&lt;/td>
&lt;td>&lt;code>MemoryItem&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Episodic Memory&lt;/td>
&lt;td>过去发生了什么、结果如何？&lt;/td>
&lt;td>&lt;code>Run / Event / Outcome / Experience&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Procedural Memory&lt;/td>
&lt;td>怎样做某件事更可靠？&lt;/td>
&lt;td>&lt;code>SkillCandidate / SkillVersion&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>LangGraph 的长期记忆概念也采用 semantic、episodic、procedural 分类，并区分交互热路径写入与后台写入。参考：&lt;a class="link" href="https://docs.langchain.com/oss/python/concepts/memory" target="_blank" rel="noopener"
>LangGraph Memory Overview&lt;/a>。&lt;/p>
&lt;p>这个边界很重要：用户偏好不是 Skill，一次成功运行也不是 Skill。只有经过抽象、验证、去除个案数据并且能够复用的过程，才适合晋升为 Skill。&lt;/p>
&lt;hr>
&lt;h2 id="2-用户记忆项目对比">2. 用户记忆项目对比
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项目&lt;/th>
&lt;th>擅长什么&lt;/th>
&lt;th>值得借鉴&lt;/th>
&lt;th>不宜直接照搬&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Mem0 / OpenMemory&lt;/td>
&lt;td>结构化提取、检索和 Memory CRUD&lt;/td>
&lt;td>scope、变更历史、过滤删除、混合检索&lt;/td>
&lt;td>Agent 可直接修改正式记忆，治理层较弱&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Letta / MemGPT&lt;/td>
&lt;td>分层记忆和后台整理&lt;/td>
&lt;td>Memory Block、共享记忆、sleep-time consolidation&lt;/td>
&lt;td>记忆通常围绕 Agent 组织&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Zep / Graphiti&lt;/td>
&lt;td>时间变化的事实和关系&lt;/td>
&lt;td>Episode、Entity、Relation、valid time&lt;/td>
&lt;td>第一阶段直接引入图数据库成本较高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangGraph / LangMem&lt;/td>
&lt;td>记忆分类和写入时机&lt;/td>
&lt;td>semantic/episodic/procedural、hot/background write&lt;/td>
&lt;td>是组件工具箱，不是完整治理系统&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="21-mem0参考第一版-api-和检索流水线">2.1 Mem0：参考第一版 API 和检索流水线
&lt;/h3>&lt;p>Mem0 的典型流程是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Conversation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 提取事实或偏好
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 判断 ADD / UPDATE / DELETE / NOOP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 按 user / agent / run 等 scope 保存
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → 向量或图检索
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它已经提供更新、删除、历史和反馈等实际能力。更新操作可以纠正旧事实；删除支持按用户、Agent、Run 和 metadata 过滤，并对无过滤条件的全量删除增加保护。参考：&lt;a class="link" href="https://docs.mem0.ai/core-concepts/memory-operations/update" target="_blank" rel="noopener"
>Mem0 Update Memory&lt;/a>、&lt;a class="link" href="https://docs.mem0.ai/core-concepts/memory-operations/delete" target="_blank" rel="noopener"
>Mem0 Delete Memory&lt;/a>。&lt;/p>
&lt;p>适合借鉴的部分包括：&lt;/p>
&lt;ul>
&lt;li>标准化的 &lt;code>add/search/update/delete/history&lt;/code> 能力；&lt;/li>
&lt;li>用户、应用、Agent 和 Run 等检索 namespace；&lt;/li>
&lt;li>提取、冲突判断与索引更新分离；&lt;/li>
&lt;li>显式批量删除和防误删设计；&lt;/li>
&lt;li>用 feedback 修正后续记忆。&lt;/li>
&lt;/ul>
&lt;p>生产平台应在 Mem0 之上增加治理层。模型不能直接执行正式的 &lt;code>ADD/UPDATE/DELETE&lt;/code>，而应先提交候选：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">MemoryCandidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → policy / user review
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → MemoryRevision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → active MemoryItem projection
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="22-zep--graphiti参考会变化的事实">2.2 Zep / Graphiti：参考会变化的事实
&lt;/h3>&lt;p>Graphiti 使用时间知识图谱保存动态上下文，主要对象包括：&lt;/p>
&lt;ul>
&lt;li>Episode：原始会话、文档或业务事件；&lt;/li>
&lt;li>Entity：用户、组织、项目、地点等实体；&lt;/li>
&lt;li>Relation/Fact：实体之间的关系与事实；&lt;/li>
&lt;li>时间信息：事实何时观察到、何时有效、何时失效。&lt;/li>
&lt;/ul>
&lt;p>Graphiti 支持增量更新，不要求每次重新批处理全部历史。参考：&lt;a class="link" href="https://help.getzep.com/graphiti/getting-started/welcome" target="_blank" rel="noopener"
>Graphiti 官方介绍&lt;/a>、&lt;a class="link" href="https://help.getzep.com/v2/understanding-the-graph" target="_blank" rel="noopener"
>Zep Graph 数据模型&lt;/a>。&lt;/p>
&lt;p>例如，下面两条偏好不应该简单覆盖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">2026-01：用户优先海运
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2026-06：用户现在优先空运
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>更合理的表达是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Preference(&amp;#34;shipping_mode&amp;#34;, &amp;#34;sea&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">valid_from = 2026-01
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">valid_to = 2026-06
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Preference(&amp;#34;shipping_mode&amp;#34;, &amp;#34;air&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">valid_from = 2026-06
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">valid_to = null
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>第一版不一定需要图数据库，但数据模型应预留：&lt;/p>
&lt;ul>
&lt;li>&lt;code>observed_at&lt;/code>&lt;/li>
&lt;li>&lt;code>valid_from / valid_to&lt;/code>&lt;/li>
&lt;li>&lt;code>supersedes_memory_id&lt;/code>&lt;/li>
&lt;li>&lt;code>contradicts_memory_id&lt;/code>&lt;/li>
&lt;li>&lt;code>source_refs&lt;/code>&lt;/li>
&lt;li>&lt;code>status=active/superseded/disputed/retracted&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>否则长期记忆很容易退化成不断被覆盖、无法审计的用户画像 JSON。&lt;/p>
&lt;h3 id="23-letta参考分层上下文和后台整理">2.3 Letta：参考分层上下文和后台整理
&lt;/h3>&lt;p>Letta 将一部分长期状态组织成持续出现在上下文中的 Memory Block，并允许多个 Agent 共享 Block。参考：&lt;a class="link" href="https://docs.letta.com/api/typescript/resources/agents/subresources/blocks" target="_blank" rel="noopener"
>Letta Memory Blocks&lt;/a>。&lt;/p>
&lt;p>它提出的 sleep-time compute 也很有价值：主 Agent 完成交互后，由后台 Agent 整理、压缩和重构记忆，把高延迟的 consolidation 移出用户交互路径。参考：&lt;a class="link" href="https://www.letta.com/blog/sleep-time-compute/" target="_blank" rel="noopener"
>Letta Sleep-time Compute&lt;/a>。&lt;/p>
&lt;p>可以映射成下面的后台流水线：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Run completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Candidate extraction
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → contradiction detection
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → clustering / deduplication
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → sensitivity classification
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → user or policy review
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → index refresh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要注意的是：Canonical Memory 应属于用户或组织，而不是某个 Agent。Agent 只能通过当前 Run 的授权 ContextPack 获得只读快照。&lt;/p>
&lt;hr>
&lt;h2 id="3-skill-沉淀项目对比">3. Skill 沉淀项目对比
&lt;/h2>&lt;h3 id="31-agent-skills作为可携带交换格式">3.1 Agent Skills：作为可携带交换格式
&lt;/h3>&lt;p>Agent Skills 使用一个简单目录表达可复用能力：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">my-skill/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── SKILL.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── scripts/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── references/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── assets/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它采用 Progressive Disclosure：&lt;/p>
&lt;ol>
&lt;li>启动时只暴露名称和描述；&lt;/li>
&lt;li>任务匹配时加载完整 &lt;code>SKILL.md&lt;/code>；&lt;/li>
&lt;li>执行时才按需读取脚本、参考资料和资源。&lt;/li>
&lt;/ol>
&lt;p>这样可以让大量 Skill 共存，而不必把所有指令一次性塞入上下文。参考：&lt;a class="link" href="https://github.com/Open-Dot-Agents/SKILL.md" target="_blank" rel="noopener"
>Agent Skills 规范&lt;/a>、&lt;a class="link" href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview" target="_blank" rel="noopener"
>Anthropic Agent Skills&lt;/a>。&lt;/p>
&lt;p>推荐把 Agent Skills 当成导入、导出和适配器交付格式，而不是平台事实源：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">SkillVersion（平台事实源）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓ build / export
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent Skills bundle（可携带交换格式）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓ adapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Codex / Claude / OpenHands / 自有 Agent
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>平台内部继续维护不可变 &lt;code>SkillVersion&lt;/code>，每个版本可以构建成标准 Skill Bundle，并带有 checksum、依赖、权限和兼容性信息。&lt;/p>
&lt;h3 id="32-openhands参考多级作用域和加载优先级">3.2 OpenHands：参考多级作用域和加载优先级
&lt;/h3>&lt;p>OpenHands 支持项目级、用户级、组织级和全局 Skill，并区分 always-on instructions 与按需加载 Skill。参考：&lt;a class="link" href="https://docs.openhands.dev/overview/skills" target="_blank" rel="noopener"
>OpenHands Skills&lt;/a>。&lt;/p>
&lt;p>平台可以采用类似作用域：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">platform baseline
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt; organization Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt; workspace or project Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt; user Skill
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;lt; Run explicitly selected Skill
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>但不能只用“同名文件覆盖”处理冲突。每次 Run 应记录：&lt;/p>
&lt;ul>
&lt;li>实际选择的 SkillVersion；&lt;/li>
&lt;li>选择原因：用户指定、规则匹配或 Agent 请求；&lt;/li>
&lt;li>匹配分数与被排除原因；&lt;/li>
&lt;li>依赖解析结果；&lt;/li>
&lt;li>实际注入的文件和 token 成本；&lt;/li>
&lt;li>执行过的脚本、工具和权限。&lt;/li>
&lt;/ul>
&lt;p>这些信息应成为 ContextPack 和 Run Evidence 的一部分。&lt;/p>
&lt;h3 id="33-voyager参考从成功轨迹生成-skill">3.3 Voyager：参考从成功轨迹生成 Skill
&lt;/h3>&lt;p>Voyager 在任务执行成功并通过验证后，将可复用实现抽象进 Skill Library；新任务再根据描述检索、组合已有 Skill。Skill Library 是它实现跨任务泛化的重要部分。参考：&lt;a class="link" href="https://openreview.net/pdf?id=P8E4Br72j3" target="_blank" rel="noopener"
>Voyager 论文&lt;/a>。&lt;/p>
&lt;p>值得借鉴的机制包括：&lt;/p>
&lt;ul>
&lt;li>只从经过验证的成功轨迹中蒸馏；&lt;/li>
&lt;li>Skill 具有明确描述、前置条件和适用场景；&lt;/li>
&lt;li>新 Skill 与已有 Skill 做相似性和能力重叠检查；&lt;/li>
&lt;li>Skill 可以组合，但依赖必须显式；&lt;/li>
&lt;li>失败用于修复候选，不直接污染正式 Skill 库。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-推荐的双流水线设计">4. 推荐的双流水线设计
&lt;/h2>&lt;p>Memory 和 Skill 可以共享来源追踪、Review 和 Eval 基础设施，但生命周期不能混在一起。&lt;/p>
&lt;h3 id="41-memory-pipeline">4.1 Memory Pipeline
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Run / Event / Artifact
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → MemoryCandidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Review or low-risk policy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → MemoryRevision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → MemoryItem projection
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Retrieval evaluation
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MemoryItem 不应原地覆盖，推荐使用“不可变 Revision + 当前投影”：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">MemoryItem
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- owner_scope
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- memory_kind
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- active_revision_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MemoryRevision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- memory_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- content
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- structured_claim
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- source_refs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- confidence
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- sensitivity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- observed_at
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- valid_from / valid_to
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- created_by
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- decision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- supersedes_revision_id
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>纠正时创建新 Revision，旧版本保留审计但不再进入新的 ContextPack。删除还应区分：&lt;/p>
&lt;ul>
&lt;li>Logical retraction：禁止召回，但保留必要审计记录；&lt;/li>
&lt;li>Physical purge：清除正文、embedding、缓存、图索引和派生副本；&lt;/li>
&lt;li>Tombstone：只保留不能反推出原文的删除证明。&lt;/li>
&lt;/ul>
&lt;h3 id="42-skill-pipeline">4.2 Skill Pipeline
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Successful Run + Outcome + Ground Truth
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → SkillCandidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Draft SkillVersion
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Sandbox Eval
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Review
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Published SkillVersion
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Canary / rollback
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>一个可运行的 SkillVersion 至少需要：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">SkillVersion
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- instructions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- description / trigger hints
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- references
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- scripts / assets checksums
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- tool and runtime dependencies
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- required capabilities
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- requested permissions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- compatibility constraints
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- source run / outcome refs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- eval suite and version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- publish status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- rollback target
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 &lt;code>description&lt;/code> 不只是展示信息，它参与 Skill 召回，因此也必须版本化并接受评测。&lt;/p>
&lt;hr>
&lt;h2 id="5-contextpack-必须说明为什么选中">5. ContextPack 必须说明“为什么选中”
&lt;/h2>&lt;p>只记录“本次用了哪条记忆和哪个 Skill”还不够。为了调试召回错误、权限问题和效果退化，建议记录检索证据：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ContextPackEntry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- asset_id / version_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- asset_type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- selection_reason
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- scope_match
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- relevance_score
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- recency_score
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- trust / confidence
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- token_cost
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- policy_decision_id
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- redaction_applied
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样才能回答：&lt;/p>
&lt;ul>
&lt;li>为什么选中了这条记忆？&lt;/li>
&lt;li>为什么另一条没有进入上下文？&lt;/li>
&lt;li>是召回、重排、权限过滤还是 token budget 导致遗漏？&lt;/li>
&lt;li>更换 Agent 或模型后，ContextPack 是否仍然一致？&lt;/li>
&lt;/ul>
&lt;p>ContextPack 应是每个 Run 的不可变授权快照，而不是 Agent 对用户资产仓库的一次开放查询权限。&lt;/p>
&lt;hr>
&lt;h2 id="6-skill-的安全晋升路径">6. Skill 的安全晋升路径
&lt;/h2>&lt;p>不要从一次成功运行直接自动发布正式 Skill。更安全的顺序是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Observed pattern
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Suggested procedure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → SkillCandidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Draft
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Sandbox verified
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Human approved
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Published
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Canary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> → Stable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>只有同时满足以下条件，才适合创建 SkillCandidate：&lt;/p>
&lt;ul>
&lt;li>至少有一次带明确 Ground Truth 的成功；&lt;/li>
&lt;li>能从业务输入中移除用户个案和敏感数据；&lt;/li>
&lt;li>相对已有 Skill 有新增价值；&lt;/li>
&lt;li>没有扩大工具或数据权限；&lt;/li>
&lt;li>有可重复执行的测试 fixture；&lt;/li>
&lt;li>能明确描述适用条件和失败边界。&lt;/li>
&lt;/ul>
&lt;p>自动提炼可以提高效率，但发布必须由 Eval、权限检查和明确门禁控制。&lt;/p>
&lt;hr>
&lt;h2 id="7-推荐的组合方案">7. 推荐的组合方案
&lt;/h2>&lt;p>没有必要选择一个框架承包全部能力。更实际的组合是：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>设计领域&lt;/th>
&lt;th>推荐参考&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Memory API 与基础检索流水线&lt;/td>
&lt;td>Mem0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>时间、冲突和事实失效模型&lt;/td>
&lt;td>Graphiti / Zep&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>后台整理与 consolidation&lt;/td>
&lt;td>Letta&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>记忆类型和写入时机&lt;/td>
&lt;td>LangGraph / LangMem&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Skill Bundle 与渐进加载&lt;/td>
&lt;td>Agent Skills / SKILL.md&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>用户、组织、项目作用域&lt;/td>
&lt;td>OpenHands&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>从成功经验蒸馏 Skill&lt;/td>
&lt;td>Voyager&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>发布、验证与回滚&lt;/td>
&lt;td>平台自己的 Eval、Ground Truth 与 Verifier&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>整体架构可以概括为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">UserResource / Run / Event / Artifact
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├──→ Memory Candidate ──→ Review ──→ Memory Revision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ ContextPack Retrieval
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──→ Skill Candidate ───→ Eval ────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Authorized Agent Run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Mem0 和 Graphiti 主要解决“怎样记住和找到”，Agent Skills 解决“怎样包装和按需加载”。生产级 Agent Harness 还需要回答：&lt;/p>
&lt;blockquote>
&lt;p>这是谁的资产、来源是什么、为什么可信、谁批准、哪个 Agent 可以使用、运行时用了哪个版本、效果是否真的改善，以及如何纠正、撤回和删除。&lt;/p>
&lt;/blockquote>
&lt;p>这些治理能力，才是用户记忆和 Skill 能够跨 Agent 长期沉淀的关键。&lt;/p></description></item><item><title>主流 Agent 框架对比与多框架统一接口设计</title><link>https://www.zata.cc/p/%E4%B8%BB%E6%B5%81-agent-%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%A4%9A%E6%A1%86%E6%9E%B6%E7%BB%9F%E4%B8%80%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1/</link><pubDate>Mon, 31 Aug 2026 16:00:00 +0800</pubDate><guid>https://www.zata.cc/p/%E4%B8%BB%E6%B5%81-agent-%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%A4%9A%E6%A1%86%E6%9E%B6%E7%BB%9F%E4%B8%80%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1/</guid><description>&lt;img src="https://www.zata.cc/p/%E4%B8%BB%E6%B5%81-agent-%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94%E4%B8%8E%E5%A4%9A%E6%A1%86%E6%9E%B6%E7%BB%9F%E4%B8%80%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1/images/index/index.svg" alt="Featured image of post 主流 Agent 框架对比与多框架统一接口设计" />&lt;p>Agent 框架没有一个绝对的“最优解”。文件研究 Agent、强类型业务 Agent、确定性审批流和云厂商原生 Agent，面对的是不同问题。真正稳定的架构不是押注一个框架，而是把&lt;strong>业务协议&lt;/strong>与&lt;strong>框架运行时&lt;/strong>分开。&lt;/p>
&lt;p>本文回答三个问题：&lt;/p>
&lt;ol>
&lt;li>主流 Agent 框架分别擅长什么？&lt;/li>
&lt;li>它们的输入与返回结构是否兼容？&lt;/li>
&lt;li>如何让不同任务使用不同框架，同时保持统一 API？&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="1-先理解-agent-框架的层次">1. 先理解 Agent 框架的层次
&lt;/h2>&lt;p>不同产品都被称为“Agent 框架”，但抽象层次并不相同：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">业务 API / Web / App
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">业务 Agent：客服、研究、抽取、编码
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent Harness：Prompt、Tools、Skills、Memory、Subagents
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Workflow Runtime：状态图、Checkpoint、Interrupt、恢复
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">模型与工具 Provider
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>例如：&lt;/p>
&lt;ul>
&lt;li>LangGraph 更接近可持久化的 Workflow Runtime。&lt;/li>
&lt;li>LangChain &lt;code>create_agent&lt;/code> 是轻量 Agent Harness。&lt;/li>
&lt;li>Deep Agents 是建立在 LangChain 与 LangGraph 上的“电池齐全”Harness。&lt;/li>
&lt;li>OpenAI Agents SDK 同时封装 Agent Loop、Handoff、Guardrail、Session 与 Tracing。&lt;/li>
&lt;li>PydanticAI 更强调 Python 类型、依赖注入和结构化结果。&lt;/li>
&lt;/ul>
&lt;p>如果不区分层次，很容易拿“工作流引擎”和“开箱即用的研究 Agent”直接比较。&lt;/p>
&lt;hr>
&lt;h2 id="2-主流框架速查">2. 主流框架速查
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>框架&lt;/th>
&lt;th>核心优势&lt;/th>
&lt;th>更适合&lt;/th>
&lt;th>主要代价&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Deep Agents&lt;/td>
&lt;td>文件上下文、Skills、Subagents、沙箱、压缩&lt;/td>
&lt;td>研究、编码、文档处理、长任务&lt;/td>
&lt;td>默认能力多，升级时要关注 Harness 行为变化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangGraph&lt;/td>
&lt;td>状态图、Checkpoint、Interrupt、可恢复执行&lt;/td>
&lt;td>确定性流程、审批、长事务&lt;/td>
&lt;td>需要自己设计节点、状态和路由&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangChain &lt;code>create_agent&lt;/code>&lt;/td>
&lt;td>轻量、模型与工具生态广&lt;/td>
&lt;td>普通工具调用 Agent&lt;/td>
&lt;td>文件工作区和复杂编排需要自行补充&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenAI Agents SDK&lt;/td>
&lt;td>&lt;code>Agent + Runner&lt;/code>、Handoff、Guardrail、Tracing&lt;/td>
&lt;td>OpenAI 技术栈、客服、业务协作&lt;/td>
&lt;td>与 OpenAI Responses 生态结合更紧&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PydanticAI&lt;/td>
&lt;td>强类型、依赖注入、结构化输出&lt;/td>
&lt;td>FastAPI 后端、抽取、业务自动化&lt;/td>
&lt;td>文件型 Harness 和复杂工作区能力较少&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Google ADK&lt;/td>
&lt;td>Sequential/Parallel/Loop、多 Agent、Vertex 集成&lt;/td>
&lt;td>Gemini、GCP、A2A 场景&lt;/td>
&lt;td>跨云项目的迁移价值要单独评估&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Microsoft Agent Framework&lt;/td>
&lt;td>Workflow、Memory、Middleware、Azure 托管&lt;/td>
&lt;td>Azure、C#、微软企业生态&lt;/td>
&lt;td>对非微软栈未必是最低成本选择&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="21-deep-agents长上下文任务-harness">2.1 Deep Agents：长上下文任务 Harness
&lt;/h3>&lt;p>Deep Agents 的价值不只是“能调用子 Agent”，而是一组协同工作的默认能力：&lt;/p>
&lt;ul>
&lt;li>通过虚拟文件系统保存和卸载大块上下文；&lt;/li>
&lt;li>通过 Summarization 控制长对话；&lt;/li>
&lt;li>用 Subagents 隔离搜索、代码执行等中间过程；&lt;/li>
&lt;li>用 Skills 按需加载工作流，避免把所有规则塞进 System Prompt；&lt;/li>
&lt;li>用 Backend 对接本地目录、状态存储、持久 Store 或沙箱；&lt;/li>
&lt;li>继承 LangGraph 的流式执行、Checkpoint 和 Human-in-the-loop。&lt;/li>
&lt;/ul>
&lt;p>适合：代码 Agent、深度研究、长文档分析、需要沙箱和文件产物的任务。&lt;/p>
&lt;p>不适合：只调用两三个业务 API 的简单客服。此时完整 Harness 可能比业务本身还复杂。&lt;/p>
&lt;blockquote>
&lt;p>从 &lt;code>0.7.0&lt;/code> 开始，Deep Agents 默认 Prompt 更精简，&lt;code>TodoListMiddleware&lt;/code> 改为显式启用，文件 Backend 默认使用更安全的虚拟路径模式。升级旧项目时还要检查 &lt;code>write_file&lt;/code> 覆盖语义和新增的递归 &lt;code>delete&lt;/code> 能力。&lt;/p>
&lt;/blockquote>
&lt;p>参考：&lt;a class="link" href="https://docs.langchain.com/oss/python/deepagents/overview" target="_blank" rel="noopener"
>Deep Agents 官方概览&lt;/a>、&lt;a class="link" href="https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/CHANGELOG.md" target="_blank" rel="noopener"
>Deep Agents Changelog&lt;/a>。&lt;/p>
&lt;h3 id="22-langgraph确定性主流程">2.2 LangGraph：确定性主流程
&lt;/h3>&lt;p>当业务流程本身清晰时，不要把全部控制权交给模型：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">输入校验 → 分类 → 检索 → 人工审批 → 执行 → 验证 → 结束
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种流程适合直接实现为 LangGraph。模型只负责需要语义判断的节点，路由、重试、上限和失败处理仍由代码决定。&lt;/p>
&lt;p>推荐组合：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">确定性主流程：LangGraph
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">开放式复杂节点：Deep Agent 或其他 Agent
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-langchain-create_agent轻量通用-agent">2.3 LangChain &lt;code>create_agent&lt;/code>：轻量通用 Agent
&lt;/h3>&lt;p>如果需求只是“模型根据问题选择工具，拿到结果后回答”，&lt;code>create_agent&lt;/code> 通常已经足够。它保留 LangChain 的模型、工具和 Middleware 生态，又不强制引入完整文件工作区与子 Agent。&lt;/p>
&lt;h3 id="24-openai-agents-sdkopenai-原生体验">2.4 OpenAI Agents SDK：OpenAI 原生体验
&lt;/h3>&lt;p>OpenAI Agents SDK 使用 &lt;code>Agent + Runner&lt;/code> 管理工具、轮次、Handoff、Guardrail 和 Session，并提供内置 Tracing。它适合：&lt;/p>
&lt;ul>
&lt;li>项目主要使用 OpenAI 模型和 Responses API；&lt;/li>
&lt;li>需要不同专业 Agent 之间 Handoff；&lt;/li>
&lt;li>希望快速加入输入、输出和工具 Guardrail；&lt;/li>
&lt;li>不想自己维护 Agent Loop。&lt;/li>
&lt;/ul>
&lt;p>如果需要完全跨模型、虚拟文件系统或复杂状态图，Deep Agents/LangGraph 通常更自然。&lt;/p>
&lt;p>参考：&lt;a class="link" href="https://openai.github.io/openai-agents-python/agents/" target="_blank" rel="noopener"
>OpenAI Agents SDK&lt;/a>、&lt;a class="link" href="https://openai.github.io/openai-agents-python/guardrails/" target="_blank" rel="noopener"
>Guardrails&lt;/a>。&lt;/p>
&lt;h3 id="25-pydanticai强类型业务-agent">2.5 PydanticAI：强类型业务 Agent
&lt;/h3>&lt;p>PydanticAI 很适合已有 Pydantic/FastAPI 技术栈的团队：&lt;/p>
&lt;ul>
&lt;li>输入依赖和运行上下文容易注入；&lt;/li>
&lt;li>输出可以直接是 Pydantic 模型；&lt;/li>
&lt;li>类型检查和测试体验清晰；&lt;/li>
&lt;li>可结合 Temporal、DBOS、Prefect、Restate 实现 Durable Execution。&lt;/li>
&lt;/ul>
&lt;p>典型任务包括票据抽取、合同分类、字段补全和调用内部业务 API。它们更像“带工具的类型化服务”，不一定需要一个文件型 Agent OS。&lt;/p>
&lt;p>参考：&lt;a class="link" href="https://pydantic.dev/docs/ai/capabilities/durable_execution/overview/" target="_blank" rel="noopener"
>PydanticAI Durable Execution&lt;/a>。&lt;/p>
&lt;h3 id="26-google-adkgcp-与多-agent-workflow">2.6 Google ADK：GCP 与多 Agent Workflow
&lt;/h3>&lt;p>Google ADK 提供 Sequential、Parallel、Loop 以及动态工作流，可以混合确定性执行节点与 LLM Agent。对于 Gemini、Vertex AI、A2A 和 GCP 托管场景，它具有明显的平台整合优势。&lt;/p>
&lt;p>参考：&lt;a class="link" href="https://github.com/google/adk-docs/blob/main/docs/workflows/index.md" target="_blank" rel="noopener"
>Google ADK Workflows&lt;/a>。&lt;/p>
&lt;h3 id="27-microsoft-agent-framework微软企业栈">2.7 Microsoft Agent Framework：微软企业栈
&lt;/h3>&lt;p>Microsoft Agent Framework 覆盖 Agent、Workflow、Memory、Middleware、Checkpoint、Human-in-the-loop 和 Azure 托管，并提供 AutoGen、Semantic Kernel 的迁移路线。Azure、C# 和微软企业集成是它最自然的使用环境。&lt;/p>
&lt;p>参考：&lt;a class="link" href="https://learn.microsoft.com/en-gb/agent-framework/" target="_blank" rel="noopener"
>Microsoft Agent Framework&lt;/a>。&lt;/p>
&lt;hr>
&lt;h2 id="3-它们的返回接口一样吗">3. 它们的返回接口一样吗？
&lt;/h2>&lt;p>不一样。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>框架&lt;/th>
&lt;th>常见调用&lt;/th>
&lt;th>最终结果入口&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Deep Agents / LangGraph&lt;/td>
&lt;td>&lt;code>agent.ainvoke(...)&lt;/code>&lt;/td>
&lt;td>&lt;code>state[&amp;quot;messages&amp;quot;][-1]&lt;/code> / &lt;code>state[&amp;quot;structured_response&amp;quot;]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenAI Agents SDK&lt;/td>
&lt;td>&lt;code>Runner.run(...)&lt;/code>&lt;/td>
&lt;td>&lt;code>result.final_output&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PydanticAI&lt;/td>
&lt;td>&lt;code>agent.run(...)&lt;/code>&lt;/td>
&lt;td>&lt;code>result.output&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Google ADK&lt;/td>
&lt;td>Runner/Event API&lt;/td>
&lt;td>从事件或最终响应中提取&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Microsoft Agent Framework&lt;/td>
&lt;td>Agent/Workflow API&lt;/td>
&lt;td>Response、Message 或 Event&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>差异不仅是字段名。各框架的内部对象还承载不同语义：&lt;/p>
&lt;ul>
&lt;li>LangChain 有 &lt;code>HumanMessage&lt;/code>、&lt;code>AIMessage&lt;/code>、&lt;code>ToolMessage&lt;/code>；&lt;/li>
&lt;li>OpenAI Agents SDK 有 Run Item、Handoff 与原始 Response Item；&lt;/li>
&lt;li>PydanticAI 有自己的 Model Message；&lt;/li>
&lt;li>ADK 和 Microsoft Framework 以各自的 Event/Message 表达执行过程。&lt;/li>
&lt;/ul>
&lt;p>因此，不应把框架原始对象直接作为 HTTP 响应。否则前端会被某个框架绑定，切换框架时 API、流式协议和会话结构都要一起重写。&lt;/p>
&lt;hr>
&lt;h2 id="4-统一业务协议而不是统一框架内部">4. 统一业务协议，而不是统一框架内部
&lt;/h2>&lt;p>建议只统一四样东西：&lt;/p>
&lt;ol>
&lt;li>请求 &lt;code>AgentRequest&lt;/code>&lt;/li>
&lt;li>最终响应 &lt;code>AgentResponse&lt;/code>&lt;/li>
&lt;li>流式事件 &lt;code>AgentEvent&lt;/code>&lt;/li>
&lt;li>业务会话 ID 到框架会话 ID 的映射&lt;/li>
&lt;/ol>
&lt;p>不要强行统一：&lt;/p>
&lt;ul>
&lt;li>框架内部 Message；&lt;/li>
&lt;li>LangGraph Checkpoint；&lt;/li>
&lt;li>OpenAI Session/Conversation；&lt;/li>
&lt;li>Provider 原始 Response；&lt;/li>
&lt;li>框架专属的恢复状态。&lt;/li>
&lt;/ul>
&lt;h3 id="41-统一请求与响应">4.1 统一请求与响应
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Protocol&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Field&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;统一的 Agent 请求。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">thread_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">default_factory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Usage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;统一的模型用量。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">total_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;与框架无关的最终响应。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">framework&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;deepagents&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;langgraph&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;openai-agents&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;pydantic-ai&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;google-adk&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;microsoft-agent-framework&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">usage&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Usage&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">trace_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">default_factory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentAdapter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Protocol&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;所有 Agent Adapter 必须实现的业务接口。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AgentRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;运行 Agent 并返回标准结果。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>output&lt;/code> 用于展示给人，&lt;code>data&lt;/code> 用于程序消费。结构化数据不要从自然语言中二次解析，应优先使用框架的 Structured Output 能力。&lt;/p>
&lt;h3 id="42-deep-agents-adapter">4.2 Deep Agents Adapter
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Any&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">DeepAgentsAdapter&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;将 Deep Agents 状态转换成业务响应。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AgentRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">thread_id&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;configurable&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;thread_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">thread_id&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ainvoke&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;structured_response&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_dump&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nb">hasattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;model_dump&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="n">output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">task_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">framework&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;deepagents&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nb">isinstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="43-pydanticai-adapter">4.3 PydanticAI Adapter
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Any&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">PydanticAIAdapter&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;将 PydanticAI 结果转换成业务响应。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AgentRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_dump&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nb">hasattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;model_dump&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">task_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">framework&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;pydantic-ai&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="44-openai-agents-sdk-adapter">4.4 OpenAI Agents SDK Adapter
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Any&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">agents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Runner&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">OpenAIAgentsAdapter&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;将 OpenAI Agents SDK 结果转换成业务响应。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AgentRequest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">Runner&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">agent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">final_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model_dump&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nb">hasattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;model_dump&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">task_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">framework&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;openai-agents&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;last_agent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">last_agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="5-流式接口才是多框架适配的难点">5. 流式接口才是多框架适配的难点
&lt;/h2>&lt;p>最终结果容易统一，流式事件更难。不同框架可能输出 Token、Message、Node Update、Tool Event、Handoff、Approval 或 Artifact。&lt;/p>
&lt;p>建议定义最小公共事件集：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;面向前端的统一流式事件。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sequence&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">type&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;run.started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text.delta&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tool.started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tool.completed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;handoff.started&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;approval.required&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;artifact.created&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;run.completed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;run.failed&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tool_call_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tool&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">default_factory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Adapter 负责映射：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">LangGraph model token → text.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">LangGraph tool node → tool.started / tool.completed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OpenAI response event → text.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OpenAI handoff item → handoff.started
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PydanticAI text delta → text.delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ADK function event → tool.started / tool.completed
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>建议使用 SSE 或 WebSocket 对外发送这些业务事件，同时将框架原始事件保存在可观测系统中，而不是全部暴露给前端。&lt;/p>
&lt;hr>
&lt;h2 id="6-会话和恢复如何处理">6. 会话和恢复如何处理
&lt;/h2>&lt;p>不同框架的持久化机制不能直接互换：&lt;/p>
&lt;ul>
&lt;li>Deep Agents/LangGraph 使用 &lt;code>thread_id + checkpointer&lt;/code>；&lt;/li>
&lt;li>OpenAI Agents SDK 可以使用 Session、Conversation 或 Previous Response；&lt;/li>
&lt;li>PydanticAI 可以传入历史消息，长任务可接 Durable Execution；&lt;/li>
&lt;li>ADK 和 Microsoft Framework 有各自的 Session/Event/Checkpoint。&lt;/li>
&lt;/ul>
&lt;p>业务数据库只保存映射：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentSession&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;业务会话与框架会话的映射。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">framework&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">external_session_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">checkpoint_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">None&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Any&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Field&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">default_factory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不要尝试把 LangGraph Checkpoint 转换成 OpenAI Session。需要迁移框架时，使用业务层保存的用户消息、结构化结果和必要摘要重新构造上下文。&lt;/p>
&lt;hr>
&lt;h2 id="7-推荐的多框架架构">7. 推荐的多框架架构
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">HTTP / SSE / WebSocket
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">AgentRequest / AgentEvent / AgentResponse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">任务路由器
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── 文件与深度研究 → DeepAgentsAdapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── 确定性长流程 → LangGraphAdapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── 结构化业务任务 → PydanticAIAdapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── OpenAI 客服 → OpenAIAgentsAdapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── 云厂商原生任务 → ADK / Microsoft Adapter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">框架自己的 State、Session、Tracing 和 Runtime
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>任务路由器不应该根据用户的一句话临时“猜框架”，而应该基于明确的任务类型、能力需求和部署策略选择：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentRouter&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;根据任务类型选择 Agent Adapter。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">adapters&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AgentAdapter&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">adapters&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">adapters&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">request&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">AgentRequest&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">AgentResponse&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">adapter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">adapters&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">adapter&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;不支持的任务类型：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">ValueError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">adapter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="8-实际选型建议">8. 实际选型建议
&lt;/h2>&lt;h3 id="选择-deep-agents如果">选择 Deep Agents，如果
&lt;/h3>&lt;ul>
&lt;li>工具会产生大量文本或文件；&lt;/li>
&lt;li>需要独立子 Agent 隔离上下文；&lt;/li>
&lt;li>需要沙箱执行代码；&lt;/li>
&lt;li>任务持续时间长且步骤开放；&lt;/li>
&lt;li>Skills、Memory、Filesystem 是核心能力。&lt;/li>
&lt;/ul>
&lt;h3 id="选择-pydanticai如果">选择 PydanticAI，如果
&lt;/h3>&lt;ul>
&lt;li>输出必须严格符合业务 Schema；&lt;/li>
&lt;li>Agent 是 FastAPI 服务的一部分；&lt;/li>
&lt;li>依赖注入和 Python 类型体验优先；&lt;/li>
&lt;li>工作流主要是调用业务 API，而不是操作文件工作区。&lt;/li>
&lt;/ul>
&lt;h3 id="选择-openai-agents-sdk如果">选择 OpenAI Agents SDK，如果
&lt;/h3>&lt;ul>
&lt;li>主要使用 OpenAI Responses API；&lt;/li>
&lt;li>Handoff、Guardrail、Session 和 Tracing 是核心需求；&lt;/li>
&lt;li>接受较强的 OpenAI 生态结合。&lt;/li>
&lt;/ul>
&lt;h3 id="选择-langgraph如果">选择 LangGraph，如果
&lt;/h3>&lt;ul>
&lt;li>流程有明确状态机；&lt;/li>
&lt;li>必须可靠暂停、恢复和重试；&lt;/li>
&lt;li>人工审批是正式流程节点；&lt;/li>
&lt;li>需要精确控制每一步，而不是让模型自由规划。&lt;/li>
&lt;/ul>
&lt;h3 id="选择-adk-或-microsoft-agent-framework如果">选择 ADK 或 Microsoft Agent Framework，如果
&lt;/h3>&lt;ul>
&lt;li>部署平台、身份、监控和企业集成本身就在对应云生态；&lt;/li>
&lt;li>平台整合收益高于跨框架可移植性。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="9-最后的工程原则">9. 最后的工程原则
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>框架是实现细节，业务协议才是长期资产。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>统一请求、响应与前端事件，不统一内部消息和 Checkpoint。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>确定性流程交给代码，开放式任务交给 Agent。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>结构化输出由 Schema 保证，不要解析自然语言。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>安全边界放在工具、权限和沙箱，不要只依赖 Prompt。&lt;/strong>&lt;/li>
&lt;li>&lt;strong>每种框架独立做回归评测，再决定路由策略。&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>一个健康的多框架系统最终应该做到：替换某个 Agent 实现时，前端 API、业务数据库和其他 Agent 都无需跟着重写。&lt;/p></description></item><item><title>数据库初始化与迁移：从创建那一刻就要钉死的三件事</title><link>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/</link><pubDate>Thu, 23 Jul 2026 11:21:08 +0800</pubDate><guid>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/</guid><description>&lt;img src="https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/images/index/index.svg" alt="Featured image of post 数据库初始化与迁移：从创建那一刻就要钉死的三件事" />&lt;blockquote>
&lt;p>&lt;strong>一句话：版本 + 字符集 + 排序规则，三件事必须在 &lt;code>CREATE&lt;/code> 那一刻写死。&lt;/strong> 之后任何跨环境、迁移、同步都建立在这三件事一致的基础上。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/images/index/index.svg"
loading="lazy"
alt="封面图：迁移时 90% 报错来自三个不一致"
>&lt;/p>
&lt;h2 id="一三件事在四个数据库里的标准值">一、三件事在四个数据库里的标准值
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>数据库&lt;/th>
&lt;th>版本&lt;/th>
&lt;th>字符集&lt;/th>
&lt;th>排序规则&lt;/th>
&lt;th>改的代价&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MySQL 5.7&lt;/td>
&lt;td>&lt;code>5.7.x&lt;/code> LTS&lt;/td>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4_general_ci&lt;/code>&lt;/td>
&lt;td>单 ALTER&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MySQL 8.0&lt;/td>
&lt;td>&lt;code>8.0.x&lt;/code> / &lt;code>8.4.x&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4_0900_ai_ci&lt;/code>&lt;/td>
&lt;td>单 ALTER&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PostgreSQL&lt;/td>
&lt;td>&lt;code>15.x&lt;/code> / &lt;code>16.x&lt;/code>&lt;/td>
&lt;td>&lt;code>UTF8&lt;/code> (client)&lt;/td>
&lt;td>ICU 默认&lt;/td>
&lt;td>&lt;strong>库级改不了&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SQL Server&lt;/td>
&lt;td>&lt;code>2019&lt;/code> / &lt;code>2022&lt;/code>&lt;/td>
&lt;td>见 collation&lt;/td>
&lt;td>&lt;code>SQL_Latin1_General_CP1_CI_AS&lt;/code>&lt;/td>
&lt;td>可改，需重建索引&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>永远显式写。&lt;strong>不能依赖&amp;quot;实例默认值&amp;quot;&lt;/strong>——开发/测试/预发/线上四个环境经常不一样。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="二emoji-演示utf8--utf8mb4">二、emoji 演示：&lt;code>utf8&lt;/code> ≠ &lt;code>utf8mb4&lt;/code>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">INSERT INTO users (name) VALUES (&amp;#39;😀&amp;#39;); -- 字符: 4 字节
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字符集&lt;/th>
&lt;th>字节数上限&lt;/th>
&lt;th>结果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>latin1&lt;/code>&lt;/td>
&lt;td>1 字节&lt;/td>
&lt;td>❌ 中文都存不了&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>utf8&lt;/code>&lt;/td>
&lt;td>&lt;strong>3 字节&lt;/strong> ⚠️&lt;/td>
&lt;td>❌ emoji 报错 &lt;code>ERROR 1366&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>4 字节&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>MySQL 的 &lt;code>utf8&lt;/code> 是历史包袱，&lt;strong>只支持 3 字节&lt;/strong>。JDBC / ORM 里写 &lt;code>utf8&lt;/code> 的，emoji 入库一律失败。&lt;strong>永远写 &lt;code>utf8mb4&lt;/code>&lt;/strong>。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="三四套初始化模板直接复制">三、四套初始化模板（直接复制）
&lt;/h2>&lt;h3 id="mysql-57">MySQL 5.7
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHARACTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4_general_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UNSIGNED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTO_INCREMENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURRENT_TIMESTAMP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uk_email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ENGINE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">InnoDB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHARSET&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4_general_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mysql-80">MySQL 8.0
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHARACTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4_0900_ai_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UNSIGNED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTO_INCREMENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JSON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURRENT_TIMESTAMP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uk_email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">-- 8.0 才支持的表达式索引
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idx_payload_user_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="k">CAST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="o">-&amp;gt;&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;$.userId&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">))))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ENGINE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">InnoDB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHARSET&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4_0900_ai_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="postgresql">PostgreSQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 库级字符集在 CREATE DATABASE 时定型,后续改不动!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ENCODING&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LC_COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;en_US.UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LC_CTYPE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;en_US.UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">TEMPLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">template0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BIGSERIAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JSONB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TIMESTAMPTZ&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NOW&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idx_payload_user_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="o">-&amp;gt;&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;userId&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>PG 的字符集分两层：库级 &lt;code>ENCODING&lt;/code> + 连接级 &lt;code>client_encoding&lt;/code>。JDBC 必须显式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">jdbc:postgresql://host:5432/my_app?charSet=UTF8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;h3 id="sql-server">SQL Server
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 实例默认 collation 不一定是这个,必须显式写
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NVARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SYSUTCDATETIME&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="四迁移报错-4-种症状--原因--修复">四、迁移报错 4 种「症状 → 原因 → 修复」
&lt;/h2>&lt;h3 id="-症状-1emoji--中文入库乱码或报错">🔴 症状 1：emoji / 中文入库乱码或报错
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 报错信息
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">ERROR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1366&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HY000&lt;/span>&lt;span class="p">):&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Incorrect&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;\xF0\x9F\x98\x80&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">column&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>表字符集不是 &lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>&lt;code>ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>JDBC 连接串 &lt;code>characterEncoding=utf8&lt;/code>、表级显式 &lt;code>CHARSET=utf8mb4&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-2order-by-name-顺序变了--where-col-命中行数变化">🔴 症状 2：&lt;code>ORDER BY name&lt;/code> 顺序变了 / &lt;code>WHERE col='...'&lt;/code> 命中行数变化
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>排序规则从 &lt;code>general_ci&lt;/code> → &lt;code>0900_ai_ci&lt;/code>，UCA 9.0 比较更激进&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>&lt;code>ORDER BY name COLLATE utf8mb4_bin&lt;/code> 锁定；或应用层排序&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>&lt;strong>业务层不依赖 DB 排序结果做正确性判断&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-3insert-截断字符串--整个应用雪崩">🔴 症状 3：&lt;code>INSERT&lt;/code> 截断字符串 → 整个应用雪崩
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>8.0 默认 &lt;code>STRICT_TRANS_TABLES&lt;/code>，截断从 warning 变 error&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>灰度期间 &lt;code>SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,...';&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>上线前用真实流量回放，发现 silent-truncate 全部修掉&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-4本地能连ci线上连不上">🔴 症状 4：本地能连，CI/线上连不上
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>5.7 &lt;code>mysql_native_password&lt;/code> → 8.0 &lt;code>caching_sha2_password&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>升级 JDBC/ORM 驱动；禁掉 &lt;code>mysql_native_password&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>临时方案&lt;/strong>&lt;/td>
&lt;td>&lt;code>ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'xxx';&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="五其他数据库的独有坑速查">五、其他数据库的独有坑（速查）
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>数据库&lt;/th>
&lt;th>坑&lt;/th>
&lt;th>一句话修复&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>字符串默认大小写敏感&lt;/td>
&lt;td>&lt;code>LOWER(name) = LOWER(?)&lt;/code> 或用 &lt;code>citext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>10+ 隐式类型转换收紧&lt;/td>
&lt;td>所有比较显式 &lt;code>CAST&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>&lt;code>BIGSERIAL ≠ AUTO_INCREMENT&lt;/code>&lt;/td>
&lt;td>跨库迁移重置序列或改 UUID&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>collation 在 3 个层级设置&lt;/td>
&lt;td>列级 &lt;code>COLLATE ...&lt;/code> 钉死&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>&lt;code>DATETIME&lt;/code> vs &lt;code>DATETIME2&lt;/code>&lt;/td>
&lt;td>迁移时零日期映射为 &lt;code>NULL&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>&lt;code>OFFSET&lt;/code> 在大表慢&lt;/td>
&lt;td>永远用 keyset 分页（&lt;code>WHERE id &amp;gt; last_id&lt;/code>）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="六迁移前-5-分钟-checklist直接跑">六、迁移前 5 分钟 checklist（直接跑）
&lt;/h2>&lt;h3 id="mysql">MySQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 1. 实例信息
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="k">version&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">character_set_server&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">collation_server&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">sql_mode&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 2. 找出非 utf8mb4 的表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_collation&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">character_set_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">tables&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">collation_character_set_applicability&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">collation_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">table_collation&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;sys&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;information_schema&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;performance_schema&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">character_set_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;utf8mb4&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 3. 找出表达式索引 (8.0+ 特性)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">statistics&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">expression&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;sys&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;information_schema&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;performance_schema&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 4. 找出 mysql_native_password 账号
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">host&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mysql&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;mysql_native_password&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="postgresql-1">PostgreSQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">version&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datcollate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datctype&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datistemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">length&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="sql-server-1">SQL Server
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SERVERPROPERTY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Collation&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">server_collation&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">collation_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;1900-01-01&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="七迁移流程七步走">七、迁移流程（七步走）
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1. 备份 (mysqldump --single-transaction --hex-blob)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 准备目标实例 (兼容 sql_mode)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 导入备份 (不要用 source,用 mysql ... &amp;lt; backup.sql)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 跑上面的 checklist SQL
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. CONVERT 字符集 + 验证表达式索引兼容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6. 灰度切流量 5% → 20% → 100%
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7. 完全切完后再升级 ORM / 驱动
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>回滚三件套&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>保留 7 天以上源端快照&lt;/li>
&lt;li>迁移脚本&lt;strong>幂等&lt;/strong>（跑两遍不报错也不丢数据）&lt;/li>
&lt;li>DNS / 负载均衡能&lt;strong>秒级切回&lt;/strong>&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="八agent-系统特别提醒">八、Agent 系统特别提醒
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>#&lt;/th>
&lt;th>提醒&lt;/th>
&lt;th>一句话&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>&lt;strong>向量存储&lt;/strong>&lt;/td>
&lt;td>别用原生 PG/MySQL 向量扩展做主索引,规模上来性能不行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>&lt;strong>对话历史&lt;/strong>&lt;/td>
&lt;td>高频追加→单独 &lt;code>messages&lt;/code> 表+定期归档&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>&lt;strong>状态机持久化&lt;/strong> (LangGraph checkpoint)&lt;/td>
&lt;td>高频 UPDATE 同一行 → &lt;code>SKIP LOCKED&lt;/code> + 多分片&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>&lt;strong>审计 / Trace 表&lt;/strong>&lt;/td>
&lt;td>可能比业务表大 10 倍 → 单独 schema + TTL + 列存&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="速查表">速查表
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 创建数据库那一刻要钉死的三件事: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ VERSION → 5.7 / 8.0 / PG15 / 2019 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ CHARSET → utf8mb4 (永远,别用 utf8) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ COLLATION → utf8mb4_0900_ai_ci (8.0) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 任何&amp;#34;看起来能跑&amp;#34;的 SQL 上生产前必查: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ EXPLAIN + CHARSET + COLLATION + TIMEOUT │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>下一篇：&lt;strong>Tool Calling 工程化&lt;/strong>——从 schema 设计到 Human-in-the-loop 的具体落地。&lt;/p>
&lt;h2 id="参考">参考
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://dev.mysql.com/doc/refman/8.0/en/charset.html" target="_blank" rel="noopener"
>MySQL 8.0 Charset &amp;amp; Collation&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.postgresql.org/docs/current/multibyte.html" target="_blank" rel="noopener"
>PostgreSQL: Character Set Support&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.postgresql.org/docs/current/sql-createdatabase.html" target="_blank" rel="noopener"
>PostgreSQL: CREATE DATABASE&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://learn.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support" target="_blank" rel="noopener"
>SQL Server: Collation and Unicode Support&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://pgloader.io/" target="_blank" rel="noopener"
>pgloader: MySQL → PostgreSQL&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Agent 工程实战开篇：从 Demo 到生产还有多远</title><link>https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/</link><pubDate>Thu, 23 Jul 2026 10:31:54 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/images/index/index.svg" alt="Featured image of post Agent 工程实战开篇：从 Demo 到生产还有多远" />&lt;blockquote>
&lt;p>这是 Agent 工程实战系列的第一篇。我会把在生产环境里踩过的坑、验证过的方案，按主题整理成可复用的工程手册。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一句话定义-agent-工程">一句话定义 Agent 工程
&lt;/h2>&lt;p>&lt;strong>Agent 工程 = 把一个在本地能跑 80% 的 LLM 循环，变成一个在真实流量下 99% 可用、可观测、可评估、可控成本的服务。&lt;/strong>&lt;/p>
&lt;p>把这句话拆开，至少有五件事要做：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>可控&lt;/strong> — 输出可控、行为可控、失败可控&lt;/li>
&lt;li>&lt;strong>可观测&lt;/strong> — 每一步都留下 trace，事后能复盘&lt;/li>
&lt;li>&lt;strong>可评估&lt;/strong> — 不是&amp;quot;看着还不错&amp;quot;，是有数据证明它确实不错&lt;/li>
&lt;li>&lt;strong>可扩展&lt;/strong> — 用户从 10 个到 10 万个，延迟和成本不会爆&lt;/li>
&lt;li>&lt;strong>可治理&lt;/strong> — 安全、合规、可解释、可下线&lt;/li>
&lt;/ol>
&lt;p>下面聊聊从 Demo 走到生产最容易翻车的几个点。&lt;/p>
&lt;h2 id="1-别再把-prompt-当成代码">1. 别再把 Prompt 当成&amp;quot;代码&amp;quot;
&lt;/h2>&lt;p>Demo 阶段你可以在 &lt;code>system prompt&lt;/code> 里写一段 200 字的话，模型听话；但到了生产：&lt;/p>
&lt;ul>
&lt;li>不同模型的 instruction-following 能力差异巨大&lt;/li>
&lt;li>用户输入会污染 prompt 结构（prompt injection）&lt;/li>
&lt;li>Prompt 一改，行为就会漂移，且没人能精确描述改了什么&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Prompt 模板独立仓库管理，版本化（每条上线记录到变更日志）&lt;/li>
&lt;li>把&amp;quot;工具说明&amp;quot;、&amp;ldquo;角色设定&amp;rdquo;、&amp;ldquo;业务规则&amp;rdquo;、&amp;ldquo;用户输入&amp;quot;在拼接时显式分段，而不是塞进同一个 f-string&lt;/li>
&lt;li>关键 prompt 用 &lt;strong>LLM 评审 / 回归集&lt;/strong> 守门，而不是靠人肉感觉&lt;/li>
&lt;/ul>
&lt;h2 id="2-tool-calling-不是-if-name--">2. Tool Calling 不是 &lt;code>if name == &amp;quot;...&amp;quot;:&lt;/code>
&lt;/h2>&lt;p>很多 Demo 是这样的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="s2">&amp;#34;天气&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">get_weather&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种&amp;quot;伪 function calling&amp;quot;看起来工作，但生产里会立刻暴露问题：&lt;/p>
&lt;ul>
&lt;li>用户可以构造输入绕过规则&lt;/li>
&lt;li>没有 schema 校验，模型幻觉出来的参数会直接打到下游&lt;/li>
&lt;li>没有权限隔离，所有工具对所有 prompt 开放&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>用真正的 tool/function schema（OpenAI / Anthropic 风格），让模型输出结构化参数&lt;/li>
&lt;li>用 &lt;strong>Pydantic / Zod&lt;/strong> 在边界做参数校验&lt;/li>
&lt;li>工具分级别：&lt;strong>只读工具&lt;/strong> / &lt;strong>写工具&lt;/strong> / &lt;strong>外部副作用工具&lt;/strong>，按风险分桶调用&lt;/li>
&lt;li>高风险工具强制 &lt;strong>Human-in-the-loop&lt;/strong> 确认&lt;/li>
&lt;/ul>
&lt;h2 id="3-agent-loop-必须有上限">3. Agent Loop 必须有上限
&lt;/h2>&lt;p>最经典的 bug：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">用户问了一个含糊的问题 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 没拿到足够信息 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 又去调用工具 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">工具又返回模糊结果 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 再调工具 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... → token 烧光 / 循环 100 次 / 超时
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>显式设置 &lt;strong>max_steps&lt;/strong>（建议 ≤ 10）&lt;/li>
&lt;li>区分&amp;quot;成功终止&amp;rdquo;、&amp;ldquo;最大步数终止&amp;rdquo;、&amp;ldquo;循环检测终止&amp;rdquo;&lt;/li>
&lt;li>检测到重复 state（同一组工具调用两次以上）→ 主动 break 并 fallback 到&amp;quot;我不知道&amp;quot;&lt;/li>
&lt;li>关键决策点强制 &lt;strong>reflection / self-critique&lt;/strong>，但要限制其调用次数&lt;/li>
&lt;/ul>
&lt;h2 id="4-可观测性先有-trace再谈优化">4. 可观测性：先有 Trace，再谈优化
&lt;/h2>&lt;p>没有 trace 的 Agent 优化就是&amp;quot;蒙眼调参&amp;quot;。生产里至少要有：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Span&lt;/strong>：每一次 LLM 调用、工具调用、记忆读写的输入 / 输出 / 耗时 / token&lt;/li>
&lt;li>&lt;strong>Trace ID&lt;/strong>：贯穿一次完整任务的所有 span&lt;/li>
&lt;li>&lt;strong>成本字段&lt;/strong>：每次调用的 input/output token 与单价&lt;/li>
&lt;li>&lt;strong>失败分类&lt;/strong>：timeout / 工具异常 / 解析失败 / 内容安全拦截&lt;/li>
&lt;/ul>
&lt;p>推荐起步就用 &lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>LangSmith&lt;/a>、&lt;a class="link" href="https://langfuse.com/" target="_blank" rel="noopener"
>Langfuse&lt;/a>、&lt;a class="link" href="https://phoenix.arize.com/" target="_blank" rel="noopener"
>Arize Phoenix&lt;/a> 这类平台之一，自建成本不低。&lt;/p>
&lt;h2 id="5-评估不是上线后再想的事">5. 评估不是上线后再想的事
&lt;/h2>&lt;p>生产里最容易回答不出的问题：&amp;ldquo;这个版本比上个版本到底好了多少？&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>最小可用的评估体系：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>离线评测集&lt;/strong>：50~200 个有标准答案或参考回答的 case，回归门禁&lt;/li>
&lt;li>&lt;strong>LLM-as-judge&lt;/strong>：用一个更强的模型给输出打分，但要注意：评分模型本身也会偏&lt;/li>
&lt;li>&lt;strong>线上指标&lt;/strong>：任务成功率、人工接管率、单任务成本、p95 延迟&lt;/li>
&lt;li>&lt;strong>红队 / 注入集&lt;/strong>：专门挑 prompt injection 与越权场景，定期跑&lt;/li>
&lt;/ul>
&lt;h2 id="6-成本治理模型路由--缓存--流式">6. 成本治理：模型路由 + 缓存 + 流式
&lt;/h2>&lt;p>Agent 系统烧钱的速度比传统服务快得多，因为：&lt;/p>
&lt;ul>
&lt;li>多轮 LLM 调用 × 长上下文 × 复杂工具链&lt;/li>
&lt;li>&amp;ldquo;小问题用大模型&amp;quot;是常态浪费&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>模型路由&lt;/strong>：简单任务走小模型（如分类、提取），复杂任务才走大模型&lt;/li>
&lt;li>&lt;strong>语义缓存&lt;/strong>：相似 query 直接返回上次结果，注意要带 cache key 的失效策略&lt;/li>
&lt;li>&lt;strong>流式输出&lt;/strong>：长生成场景下，TTFT 比总时长更重要&lt;/li>
&lt;li>&lt;strong>预算熔断&lt;/strong>：单任务 / 单用户 / 单租户的 token 上限，超限自动降级&lt;/li>
&lt;/ul>
&lt;h2 id="7-安全默认假设你的-prompt-会被攻破">7. 安全：默认假设你的 Prompt 会被攻破
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>不要&lt;/strong> 把系统提示视为秘密，它一定会被泄露&lt;/li>
&lt;li>&lt;strong>不要&lt;/strong> 让 LLM 决定要不要执行高风险工具（删库、转账、发邮件），必须硬规则守门&lt;/li>
&lt;li>&lt;strong>必须&lt;/strong> 对工具返回值做清洗——它也是用户可控输入&lt;/li>
&lt;li>&lt;strong>必须&lt;/strong> 审计日志：谁、什么时候、通过哪个 agent、调了哪个工具、传了什么参数&lt;/li>
&lt;/ul>
&lt;h2 id="这个系列接下来会写什么">这个系列接下来会写什么？
&lt;/h2>&lt;p>按上面七个主题，每个都会展开成一篇实战文章，配可运行示例：&lt;/p>
&lt;ol>
&lt;li>Prompt 工程化：从 f-string 到模板系统&lt;/li>
&lt;li>Tool Calling 工程化：schema、权限、Human-in-the-loop&lt;/li>
&lt;li>Agent Loop 设计：状态机视角的 ReAct&lt;/li>
&lt;li>可观测性实战：Langfuse / LangSmith 接入&lt;/li>
&lt;li>评估体系搭建：离线集 + LLM-judge + 线上指标&lt;/li>
&lt;li>成本与性能：模型路由、缓存、流式&lt;/li>
&lt;li>安全与合规：Prompt injection 防护与审计&lt;/li>
&lt;/ol>
&lt;p>如果你也在做 Agent，欢迎把你踩过的坑写在评论区——下一篇我会挑留言里出现最多的那个话题。&lt;/p>
&lt;h2 id="参考">参考
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.anthropic.com/engineering/building-effective-agents" target="_blank" rel="noopener"
>Anthropic: Building effective agents&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://platform.openai.com/docs/guides/agents" target="_blank" rel="noopener"
>OpenAI: A practical guide to building agents&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>LangSmith Documentation&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://langfuse.com/" target="_blank" rel="noopener"
>Langfuse: Open Source LLM Engineering&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Gliding Horse Agent OS 介绍：Rust 构建的工业级 AI Agent 操作系统</title><link>https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/</link><pubDate>Mon, 29 Jun 2026 10:00:00 +0800</pubDate><guid>https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/</guid><description>&lt;img src="https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/images/index/index.svg" alt="Featured image of post Gliding Horse Agent OS 介绍：Rust 构建的工业级 AI Agent 操作系统" />&lt;h2 id="项目概览">项目概览
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/images/index/index.svg"
loading="lazy"
alt="Gliding Horse Agent OS 总览"
>&lt;/p>
&lt;p>&lt;strong>Gliding Horse Agent OS&lt;/strong> 是由 &lt;a class="link" href="https://github.com/doiito" target="_blank" rel="noopener"
>doiito&lt;/a> 在 GitHub 上开源的 &lt;strong>Rust 编写的工业级 AI Agent 操作系统&lt;/strong>。项目名称源自三国时期诸葛亮发明的&amp;quot;&lt;strong>木牛流马&lt;/strong>&amp;quot;——一种能在险峻山路上自主运输粮草的机械装置,象征&amp;quot;以基础设施驾驭集体智能&amp;quot;:不仅构建 Agent,更构建&lt;strong>让多个 Agent 协同调度、自主演化、可审计&lt;/strong>的底层系统。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>仓库&lt;/strong>:&lt;a class="link" href="https://github.com/doiito/gliding_horse" target="_blank" rel="noopener"
>doiito/gliding_horse&lt;/a>&lt;/li>
&lt;li>&lt;strong>许可&lt;/strong>:MIT&lt;/li>
&lt;li>&lt;strong>当前版本&lt;/strong>:&lt;code>v0.1.2.preview&lt;/code>(2026-06 发布)&lt;/li>
&lt;li>&lt;strong>语言占比&lt;/strong>:Rust ≈ 80.9%&lt;/li>
&lt;li>&lt;strong>定位&lt;/strong>:面向企业级 AI Agent 系统的多智能体编排框架,提供完整的中文文档&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&amp;ldquo;We don&amp;rsquo;t just build agents; we build the &lt;strong>infrastructure that harnesses their collective intelligence&lt;/strong>.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="设计哲学">设计哲学
&lt;/h2>&lt;p>借鉴&amp;quot;木牛流马&amp;quot;的范式:古代机械并不是替换人力,而是&lt;strong>把人从机械性劳动中解放出来&lt;/strong>。Gliding Horse 同样不追求把 Agent 框死在某种刚性流程里,而是提供一套&lt;strong>自适应任务复杂度&lt;/strong>的编排基础设施——从一次性的即时查询,到多周的长流程项目,同一套引擎都能覆盖。&lt;/p>
&lt;p>核心理念可以概括为一句话:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;灵活编排适应任务复杂度,而非刚性框架强迫任务就范&amp;rdquo;&lt;/strong>(The wise adapt their methods to circumstances, just as water shapes its course according to the ground over which it flows.)&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="整体架构">整体架构
&lt;/h2>&lt;p>仓库分为多个 Workspace,核心层次按职能拆分:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层级&lt;/th>
&lt;th>目录&lt;/th>
&lt;th>技术栈&lt;/th>
&lt;th>职责&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>核心调度&lt;/strong>&lt;/td>
&lt;td>&lt;code>crates/&lt;/code> · &lt;code>src/&lt;/code>&lt;/td>
&lt;td>Rust 2021 · PDCA · 5W2H · EventBus&lt;/td>
&lt;td>Agent 编排与生命周期&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>应用层&lt;/strong>&lt;/td>
&lt;td>&lt;code>apps/software_engineering_team/&lt;/code>&lt;/td>
&lt;td>Center (Go+Temporal) + Edge (Rust+axum) + VS Code Plugin&lt;/td>
&lt;td>完整 SDLC 联邦&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>终端助手&lt;/strong>&lt;/td>
&lt;td>&lt;code>crates/gliding_code/&lt;/code>&lt;/td>
&lt;td>Rust (musl 静态二进制)&lt;/td>
&lt;td>零依赖命令行 AI 助手&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>技能定义&lt;/strong>&lt;/td>
&lt;td>&lt;code>skills/&lt;/code>&lt;/td>
&lt;td>RDF / YAML&lt;/td>
&lt;td>可插拔的 Skill 描述&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>数据契约&lt;/strong>&lt;/td>
&lt;td>&lt;code>proto/&lt;/code> · &lt;code>workflow.jsonld&lt;/code>&lt;/td>
&lt;td>gRPC Proto · JSON-LD 1.1&lt;/td>
&lt;td>跨进程/跨语言接口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>文档&lt;/strong>&lt;/td>
&lt;td>&lt;code>docs/&lt;/code>&lt;/td>
&lt;td>Markdown (中英双语)&lt;/td>
&lt;td>设计文档与设计哲学&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="核心机制详解">核心机制详解
&lt;/h2>&lt;h3 id="1-广义-pdca--7-级自适应执行">1. 广义 PDCA —— 7 级自适应执行
&lt;/h3>&lt;p>PDCA(Plan-Do-Check-Act)是经典的戴明环。Gliding Horse 将其&lt;strong>广义化为 7 级复杂度&lt;/strong>,通过任务的 &lt;code>5W2H&lt;/code> 元数据动态选择:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>级别&lt;/th>
&lt;th>含义&lt;/th>
&lt;th>触发场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>L0&lt;/strong>&lt;/td>
&lt;td>即时响应&lt;/td>
&lt;td>简单问答、不需要工具&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L1&lt;/strong>&lt;/td>
&lt;td>单步工具调用&lt;/td>
&lt;td>单次搜索、单次代码执行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L2&lt;/strong>&lt;/td>
&lt;td>多步推理&lt;/td>
&lt;td>多轮 ReAct,需要思考&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L3&lt;/strong>&lt;/td>
&lt;td>子任务分解&lt;/td>
&lt;td>任务拆解后并行执行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L4&lt;/strong>&lt;/td>
&lt;td>长链 Pipeline&lt;/td>
&lt;td>多阶段流水线&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L5&lt;/strong>&lt;/td>
&lt;td>递归 PDCA&lt;/td>
&lt;td>任务作为其他任务的子节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L6&lt;/strong>&lt;/td>
&lt;td>应急模式&lt;/td>
&lt;td>异常升级、人工接管&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>一个引擎覆盖从 L0 的即时查询到 L6 的应急处置&lt;/strong>——这是 Gliding Horse 区别于其他编排框架的关键:不要求开发者为不同复杂度写不同框架。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/images/inline/pdca-7-levels.svg"
loading="lazy"
alt="PDCA 7 级自适应执行"
>&lt;/p>
&lt;h3 id="2-5w2h-本体级审计--告别黑盒-passfail">2. 5W2H 本体级审计 —— 告别黑盒 PASS/FAIL
&lt;/h3>&lt;p>传统 Agent 评测只给一个&amp;quot;通过/不通过&amp;quot;的二值信号。Gliding Horse 对每一次执行结果按 &lt;strong>5W2H 七个维度&lt;/strong>独立审计:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>失败时动作&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>What&lt;/strong>(产物对不对)&lt;/td>
&lt;td>重新生成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Why&lt;/strong>(决策理由是否充分)&lt;/td>
&lt;td>重新分析&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>How&lt;/strong>(方法是否合理)&lt;/td>
&lt;td>重新规划&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Where&lt;/strong>(执行环境/上下文是否合适)&lt;/td>
&lt;td>重新规划&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>When&lt;/strong>(时序/截止日期)&lt;/td>
&lt;td>有条件放行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HowMuch&lt;/strong>(资源消耗是否在预算内)&lt;/td>
&lt;td>有条件放行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>(附加)Who/Which&lt;/strong>&lt;/td>
&lt;td>角色/工具匹配&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这让&lt;strong>精确回滚&lt;/strong>成为可能——你可以精准定位&amp;quot;哪一维度出问题&amp;quot;,而不是把整个流程推倒重做。&lt;/p>
&lt;h3 id="3-cpu-缓存启发的-4-层记忆--mesi-一致性">3. CPU 缓存启发的 4 层记忆 + MESI 一致性
&lt;/h3>&lt;p>Gliding Horse 最具创新性的设计之一是&lt;strong>把 CPU 缓存架构搬到多 Agent 记忆系统中&lt;/strong>:&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/gliding-horse-agent-os-%E4%BB%8B%E7%BB%8Drust-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%B7%A5%E4%B8%9A%E7%BA%A7-ai-agent-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/images/inline/memory-4-layers.svg"
loading="lazy"
alt="Memory 4-Layer &amp;#43; MESI"
>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>层&lt;/th>
&lt;th>类比&lt;/th>
&lt;th>技术实现&lt;/th>
&lt;th>性能指标&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>L0&lt;/strong>&lt;/td>
&lt;td>寄存器/缓存&lt;/td>
&lt;td>&lt;code>Sled&lt;/code> KV + &lt;code>Qdrant&lt;/code> 向量库&lt;/td>
&lt;td>读取 ~1ms · 1000 ops/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L1&lt;/strong>&lt;/td>
&lt;td>L1 缓存&lt;/td>
&lt;td>上下文窗口 / 最近对话&lt;/td>
&lt;td>由 LLM 决定&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L2&lt;/strong>&lt;/td>
&lt;td>L2 缓存&lt;/td>
&lt;td>&lt;code>Oxigraph&lt;/code> RDF 三元组存储&lt;/td>
&lt;td>写入 ~2ms · 500 ops/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>L3&lt;/strong>&lt;/td>
&lt;td>L3 缓存 / 内存&lt;/td>
&lt;td>&lt;code>SPARQL 1.1&lt;/code> 投影视图&lt;/td>
&lt;td>查询 ~15ms · 66 ops/sec&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>更关键的是引入了 CPU 的 &lt;strong>MESI 缓存一致性协议&lt;/strong>(Modified / Exclusive / Shared / Invalid)的记忆改造版,解决多 Agent 共享记忆时的不一致问题。配合&lt;strong>扩散激活式预取&lt;/strong>(Spreading Activation),常用记忆提前加载,&lt;strong>感知延迟下降约 90%&lt;/strong>。&lt;/p>
&lt;h3 id="4-json-ld-11-通用数据总线">4. JSON-LD 1.1 通用数据总线
&lt;/h3>&lt;p>Agent 之间共享数据时,字段命名冲突是隐形大坑。Gliding Horse 用 &lt;a class="link" href="https://www.w3.org/TR/json-ld11/" target="_blank" rel="noopener"
>JSON-LD 1.1&lt;/a> 作为&lt;strong>跨子系统、跨语言的通用数据契约&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>@context&lt;/code> —— 鸭子类型,消除字段命名冲突&lt;/li>
&lt;li>&lt;code>@id&lt;/code> —— 零成本跨 Agent 实体合并(同一个对象在不同子系统里指向同一 &lt;code>@id&lt;/code> 即视为同一对象)&lt;/li>
&lt;li>&lt;code>@graph&lt;/code> (Named Graphs) —— 命名图机制,允许不同子系统并行写入而不冲突&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>配套的 &lt;code>workflow.jsonld&lt;/code> 把&amp;quot;工作流描述&amp;quot;也建模为带 &lt;code>@id&lt;/code> 的实体,可以直接写入 RDF 图并被 SPARQL 查询。&lt;/p>
&lt;/blockquote>
&lt;h3 id="5-统一知识图谱--oxigraph-rdf">5. 统一知识图谱 —— Oxigraph RDF
&lt;/h3>&lt;p>所有子系统(技能、记忆、任务、代码知识)&lt;strong>共享同一个 &lt;code>Oxigraph&lt;/code> RDF 存储&lt;/strong>,通过命名图隔离,通过 &lt;code>@id&lt;/code> 互联:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>代码 AST&lt;/strong> 由 &lt;code>tree-sitter&lt;/code> 自动解析为 RDF 三元组入图&lt;/li>
&lt;li>跨子系统的 &lt;strong>SPARQL JOIN&lt;/strong> 让&amp;quot;代码模块 ↔ 任务 ↔ 记忆&amp;quot;可以一次性查询&lt;/li>
&lt;li>单一 &lt;code>@id&lt;/code> 保证实体在所有上下文中的身份一致&lt;/li>
&lt;/ul>
&lt;p>这一设计的核心收益是&lt;strong>消除信息孤岛&lt;/strong>:跨子系统的关联不再需要额外的 ETL 管道,直接在图上做关联查询即可。&lt;/p>
&lt;h3 id="6-自演化-skill-graph">6. 自演化 Skill Graph
&lt;/h3>&lt;p>Skill 不是一个静态的 YAML 列表,而是一张&lt;strong>自演化的认知网络&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>约 &lt;strong>7,500+ LOC&lt;/strong> 的动态 RDF 网络&lt;/li>
&lt;li>&lt;strong>6 种语义链接&lt;/strong>:Prerequisite(前置)、Composition(组合)、Related(相关)、Conflict(冲突)、Refine(精炼)、Deprecate(废弃)&lt;/li>
&lt;li>&lt;strong>任务后学习&lt;/strong>:&lt;code>/learn&lt;/code> 机制根据执行结果创建新知识片段与新链接&lt;/li>
&lt;li>&lt;strong>去重压缩&lt;/strong>:&lt;code>/reduce&lt;/code> 机制定期合并冗余节点&lt;/li>
&lt;/ul>
&lt;p>简单说,系统跑得越多,这张&amp;quot;能力地图&amp;quot;&lt;strong>自动生长&lt;/strong>得越完备。&lt;/p>
&lt;h3 id="7-主动感知引擎">7. 主动感知引擎
&lt;/h3>&lt;p>不是等任务失败再处理,Gliding Horse 内置&lt;strong>主动监控&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>10 类执行触发器&lt;/strong>:截止日期、Token 预算超 80%、角色失配、环境冲突、循环检测……&lt;/li>
&lt;li>&lt;strong>60 秒异常去重窗&lt;/strong>:避免同一异常反复打扰&lt;/li>
&lt;li>&lt;strong>自动升级人工&lt;/strong>:超出 Agent 能力的异常自动升级到人介入&lt;/li>
&lt;/ul>
&lt;h3 id="8-微工具系统micro-tool">8. 微工具系统(Micro-Tool)
&lt;/h3>&lt;p>当 Agent 输出超过 &lt;strong>8 KB&lt;/strong> 的结果时,Gliding Horse 自动把它&lt;strong>包装为一组对话式微工具&lt;/strong>(例如 &lt;code>search_in_results&lt;/code>、&lt;code>summarize_section&lt;/code>)。原本 50 KB+ 难以一次性塞进上下文的结果,变成可在上下文里&lt;strong>交互式查询&lt;/strong>的对象——极大降低 LLM 上下文压力。&lt;/p>
&lt;h3 id="9-mcp-协议接入">9. MCP 协议接入
&lt;/h3>&lt;p>原生支持 &lt;a class="link" href="https://modelcontextprotocol.io/" target="_blank" rel="noopener"
>Model Context Protocol&lt;/a>,&lt;strong>一个协议接入 GitHub、Slack、Jira 等所有 MCP 兼容服务&lt;/strong>,运行时动态发现工具,告别&amp;quot;每接一个外部服务就写一套对接代码&amp;quot;。&lt;/p>
&lt;h3 id="10-检查点与恢复">10. 检查点与恢复
&lt;/h3>&lt;p>长任务最怕崩溃丢失进度。Gliding Horse 在关键节点&lt;strong>对会话状态打快照&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>崩溃后&lt;strong>完整恢复&lt;/strong>到最近检查点,无需重头开始&lt;/li>
&lt;li>支持&lt;strong>事后回放调试&lt;/strong>(post-mortem replay)&lt;/li>
&lt;li>支撑小时级甚至天级的 Agent 长任务&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="center--edge-联邦架构">Center + Edge 联邦架构
&lt;/h2>&lt;p>这是一个独立于以上机制之上的&lt;strong>部署拓扑&lt;/strong>,值得单独说。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">VS Code Plugin (TypeScript)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ WebSocket / REST
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Edge Daemon (Rust · axum)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ API Server (ws / chat / health)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ Agent Core (SupervisorAgent · DoAgent · LLM Client)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ Docker Sandbox (安全执行: 编译 / 测试)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Graph Layer (本地 Sled + Delta Sync)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ gRPC + REST
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ▼
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Center (Go · Gin)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ HTTP API (/api/v1/* · /ws)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ Temporal Workflow (编排引擎)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ Agent Manager (注册 · 心跳 · 派发)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├─ Executors (req → design → coding → review → test → cicd → deploy)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ Store (SQLite · gRPC Client · Graph Sync)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>角色&lt;/th>
&lt;th>语言 / 框架&lt;/th>
&lt;th>核心职责&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Center&lt;/strong>&lt;/td>
&lt;td>Go + Gin + Temporal&lt;/td>
&lt;td>全局工作流编排、项目生命周期、Agent 注册中心、图谱同步&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Edge&lt;/strong>&lt;/td>
&lt;td>Rust + axum&lt;/td>
&lt;td>本地 LLM 执行、Docker 沙箱、VS Code WebSocket 桥&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>VS Code Plugin&lt;/strong>&lt;/td>
&lt;td>TypeScript&lt;/td>
&lt;td>Chat Panel + Graph View + Task Panel,实时呈现 Agent 协作&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>设计哲学:&lt;strong>Center 负责编排,Edge 负责执行,VS Code 负责感知&lt;/strong>——三者解耦,任一节点宕机不影响其他局部。&lt;/p>
&lt;hr>
&lt;h2 id="两个旗舰应用">两个旗舰应用
&lt;/h2>&lt;h3 id="1-software-engineering-team">1. Software Engineering Team
&lt;/h3>&lt;p>最完整的多 Agent 协作&lt;strong>软件工程团队&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">需求 → 设计 → 编码 → 评审 → 测试 → CI/CD → 部署
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>提供完整 Dashboard(项目总览、Agent 状态、Pipeline 进度)以及 VS Code 插件(Chat Panel / Graph View / Task Panel),让开发者可以&lt;strong>实时看到多个 Agent 在背后协作&lt;/strong>。&lt;/p>
&lt;blockquote>
&lt;p>这种&amp;quot;联邦式&amp;quot;设计的好处是:Center/Edge 可以独立扩展,VS Code 插件可以独立演进,核心调度引擎不必关心 UI 细节。&lt;/p>
&lt;/blockquote>
&lt;h3 id="2-gliding-code">2. Gliding Code
&lt;/h3>&lt;p>&lt;strong>零依赖终端 AI 编码助手&lt;/strong>——Gliding Horse 知识图谱与编排能力的&amp;quot;轻量入口&amp;quot;:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>平台&lt;/th>
&lt;th>包大小&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Linux (x86_64, musl)&lt;/td>
&lt;td>13.9 MB&lt;/td>
&lt;td>完全静态链接&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Linux (aarch64, musl)&lt;/td>
&lt;td>12.9 MB&lt;/td>
&lt;td>-&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>macOS (Apple Silicon)&lt;/td>
&lt;td>12.1 MB&lt;/td>
&lt;td>-&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Windows (x86_64)&lt;/td>
&lt;td>11.6 MB&lt;/td>
&lt;td>-&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 下载即用,无需安装任何依赖&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar xzf glidingcode-*.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 设置 API Key(支持 DeepSeek 或任意 OpenAI 兼容端点)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">DEEPSEEK_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;sk-...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 交互式会话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./glidingcode
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或一次性任务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./glidingcode &lt;span class="s2">&amp;#34;Explain how Rust&amp;#39;s borrow checker works&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它的特点是&lt;strong>把整套&amp;quot;知识图谱 + Agent 编排&amp;quot;塞进一个 13 MB 的二进制&lt;/strong>——这是 musl 全静态链接的功劳。对于想体验 Gliding Horse 但又不想搭建完整 Center/Edge 的开发者,&lt;strong>Gliding Code 是最佳入口&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 id="性能基线">性能基线
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>操作&lt;/th>
&lt;th>延迟&lt;/th>
&lt;th>吞吐&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>L2 节点写入 (Oxigraph)&lt;/td>
&lt;td>~2 ms&lt;/td>
&lt;td>500 ops/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>L3 SPARQL 投影&lt;/td>
&lt;td>~15 ms&lt;/td>
&lt;td>66 ops/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>L0 Sled KV 读取&lt;/td>
&lt;td>~1 ms&lt;/td>
&lt;td>1000 ops/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Agent ReAct 一轮&lt;/td>
&lt;td>1–5 s&lt;/td>
&lt;td>0.2–1 turns/sec&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>空闲内存&lt;/strong>&lt;/td>
&lt;td>~200 MB&lt;/td>
&lt;td>随任务规模线性增长&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>注意:吞吐数据为仓库 README 公布的基线,实际表现取决于 LLM 调用频次与上下文长度。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="上手指南">上手指南
&lt;/h2>&lt;h3 id="快速体验gliding-code零依赖">快速体验:Gliding Code(零依赖)
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/doiito/gliding_horse.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> gliding_horse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在 releases 页面下载对应平台的预编译包&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./glidingcode --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="完整搭建software-engineering-team">完整搭建:Software Engineering Team
&lt;/h3>&lt;p>前置依赖:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Rust ≥ 1.94&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Go ≥ 1.24&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Docker&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Temporal Server&lt;/strong>(本地或远端)&lt;/li>
&lt;li>一个 &lt;strong>OpenAI 兼容的 LLM API Key&lt;/strong>(DeepSeek、OpenAI、本地 vLLM 均可)&lt;/li>
&lt;/ul>
&lt;p>启动步骤:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1) 启动 Center&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> apps/software_engineering_team/center
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp center/config.yaml center/config.local.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 编辑 config.local.yaml,填入 LLM Key、Temporal 地址&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go run ./cmd/server/... &lt;span class="c1"># API server on :8080&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go run ./cmd/worker/... &lt;span class="c1"># Temporal worker&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2) 启动 Edge Daemon&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ../edge/daemon
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cargo run -- daemon start &lt;span class="c1"># Agent daemon on :7890&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3) 安装 VS Code 插件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">code --install-extension apps/software_engineering_team/edge/vscode/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以&lt;strong>直接调用 API&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl http://localhost:8080/api/v1/projects &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -X POST -H &lt;span class="s2">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="s1">&amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;My Project&amp;#34;,&amp;#34;description&amp;#34;:&amp;#34;Build a microservice&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="路线图">路线图
&lt;/h2>&lt;p>&lt;strong>核心 OS(持续推进)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>扩展 MCP 工具生态与动态发现&lt;/li>
&lt;li>多模型路由优化(成本感知调度)&lt;/li>
&lt;li>知识图谱查询性能与规模优化&lt;/li>
&lt;li>带版本化 Prompt 继承的模板引擎&lt;/li>
&lt;li>细粒度订阅过滤的事件系统&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>应用层(未来)&lt;/strong>:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>时间&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Q3 2026&lt;/strong>&lt;/td>
&lt;td>原生 Web 仪表板 · Python/TypeScript SDK&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Q4 2026&lt;/strong>&lt;/td>
&lt;td>Kubernetes Operator · 多轮对话记忆压缩 · Skill Marketplace 原型&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>2027&lt;/strong>&lt;/td>
&lt;td>跨 Edge 节点的分布式 Agent Mesh · 多模态 Agent(视觉/音频) · 社区插件注册中心&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="一些深度观察">一些深度观察
&lt;/h2>&lt;p>作为一份完整的概览,以下是我认为 Gliding Horse 与同类项目相比的几个显著差异点:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PDCA 7 级自适应&lt;/strong>让它对&amp;quot;什么算 Agent 框架&amp;quot;的定义更宽泛——同一套引擎既可做即时问答,又可跑周级项目,这种&amp;quot;弹性&amp;quot;是 LangChain / AutoGen 目前都没做到的。&lt;/li>
&lt;li>&lt;strong>CPU 缓存架构 + MESI 一致性&lt;/strong>是从硬件架构借来的概念,工程上不是新东西,但&lt;strong>移植到多 Agent 记忆&lt;/strong>是很新鲜的一手;若实现得当,能极大缓解&amp;quot;Agent 之间互相覆盖记忆&amp;quot;的常见 bug。&lt;/li>
&lt;li>&lt;strong>JSON-LD 作为跨子系统数据总线&lt;/strong>使得&amp;quot;技能/记忆/任务/代码&amp;quot;得以在 RDF 图层统一——这一点与 &lt;a class="link" href="https://www.zata.cc/p/graph-rag-%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%85%A8%E6%99%AF%E4%BB%8E%E5%BE%AE%E8%BD%AF-graphrag-%E5%88%B0-lightrag/" >GraphRAG 全景&lt;/a> 思路一脉相承,但走得更远。&lt;/li>
&lt;li>&lt;strong>OpenAI 兼容 API + MCP 双协议&lt;/strong>意味着开发者不强制绑定任何单一模型生态,DeepSeek、Qwen、本地 vLLM 都可以无缝接入,适合国内闭源/开源混合场景。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>目前的不确定性&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>项目仍处 &lt;code>v0.1.2.preview&lt;/code>,&lt;strong>生产环境稳定性未充分验证&lt;/strong>&lt;/li>
&lt;li>官方 Issue 中已有用户反馈&amp;quot;流程太繁琐、跑半天没结果、重问又从头开始&amp;quot;(Issue #3),&lt;strong>会话连续性与状态恢复&lt;/strong>仍是体验痛点&lt;/li>
&lt;li>实时统计(stargazer_count / watcher_count)GitHub API 返回 &lt;code>null&lt;/code>,需访问仓库页确认当前社区热度&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>适合谁:正在搭建&lt;strong>多 Agent 协作平台 / 企业级 Agent 工作流 / 知识密集型 Agent 系统&lt;/strong>的团队。
不适合谁:只要做一次性 prompt 调用或单 Agent RAG 的项目(用 LangChain / LlamaIndex 更轻)。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="相关资源">相关资源
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>项目主页&lt;/strong>:&lt;a class="link" href="https://github.com/doiito/gliding_horse" target="_blank" rel="noopener"
>github.com/doiito/gliding_horse&lt;/a>&lt;/li>
&lt;li>&lt;strong>中文 README&lt;/strong>:&lt;code>README.zh.md&lt;/code>(仓库根目录)&lt;/li>
&lt;li>&lt;strong>设计文档&lt;/strong>:&lt;code>docs/DESIGN_DETAIL.md&lt;/code> · &lt;code>docs/DESIGN_DETAIL.zh.md&lt;/code>&lt;/li>
&lt;li>&lt;strong>核心哲学&lt;/strong>:&lt;code>docs/CORE_DESIGN_PHILOSOPHY.md&lt;/code> · &lt;code>docs/CORE_DESIGN_PHILOSOPHY.zh.md&lt;/code>&lt;/li>
&lt;li>&lt;strong>gRPC 协议定义&lt;/strong>:&lt;code>proto/pdca_core.proto&lt;/code>&lt;/li>
&lt;li>&lt;strong>作者博客&lt;/strong>:
&lt;ul>
&lt;li>Medium(英文):&lt;a class="link" href="https://medium.com/@doiito-sun" target="_blank" rel="noopener"
>medium.com/@doiito-sun&lt;/a>&lt;/li>
&lt;li>掘金:&lt;a class="link" href="https://juejin.cn/column/7647868075887165450" target="_blank" rel="noopener"
>juejin.cn/column/7647868075887165450&lt;/a>&lt;/li>
&lt;li>SegmentFault:&lt;a class="link" href="https://segmentfault.com/u/doiito/articles" target="_blank" rel="noopener"
>segmentfault.com/u/doiito&lt;/a>&lt;/li>
&lt;li>CSDN:&lt;a class="link" href="https://blog.csdn.net/2604_96270735" target="_blank" rel="noopener"
>blog.csdn.net/2604_96270735&lt;/a>&lt;/li>
&lt;li>B 站:&lt;a class="link" href="https://space.bilibili.com/1547455799/lists" target="_blank" rel="noopener"
>space.bilibili.com/1547455799&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="系列文章">系列文章
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/" >智能体编排设计工程师学习指南&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/" >AI Agent Loop 工程:原理、模式与实现&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/agent-%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9D%97%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/" >记忆模块技术文档&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/graph-rag-%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%85%A8%E6%99%AF%E4%BB%8E%E5%BE%AE%E8%BD%AF-graphrag-%E5%88%B0-lightrag/" >GraphRAG 开源项目全景&lt;/a>&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>注:Hugo 的相对路径解析对含空格目录使用 URL 编码(&lt;code>%20&lt;/code>);若仍出现 broken 链接,请以博客最终渲染为准。&lt;/p>
&lt;/blockquote></description></item><item><title>AI Agent Loop 工程：原理、模式与实现</title><link>https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Fri, 26 Jun 2026 15:40:07 +0800</pubDate><guid>https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description>&lt;img src="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/images/index/index.svg" alt="Featured image of post AI Agent Loop 工程：原理、模式与实现" />&lt;blockquote>
&lt;p>一句话概括 Agent 工程的核心:&lt;strong>把 LLM 放进一个可控的循环里,让它在&amp;quot;思考—行动—观察—反思&amp;quot;之间反复迭代,直到任务收敛。&lt;/strong> 这个&amp;quot;循环&amp;quot;——也就是 Agent Loop——就是本文的主角。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="0-为什么是-loop">0. 为什么是 &amp;ldquo;Loop&amp;rdquo;?
&lt;/h2>&lt;p>如果你把 LLM 当成&amp;quot;一个函数 &lt;code>f(prompt) -&amp;gt; response&lt;/code>&amp;quot;,那你写出的就是 prompt 工程;但如果你把 LLM 当成&amp;quot;一个可以被调用的决策者&amp;quot;,你就会发现:&lt;strong>几乎所有复杂的 Agent 行为,本质上都是一个循环&lt;/strong>。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>单次调用能做到吗?&lt;/th>
&lt;th>为什么需要 Loop?&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>让模型查今天北京天气&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>一次生成即可&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>让模型读完 50 页 PDF 后回答&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>需要循环:分块 → 读取 → 累计 → 汇总&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>让模型调用 5 个 API 完成订单&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>需要循环:规划 → 调 API → 处理异常 → 重试&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>让模型写出能跑通的代码&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>需要循环:写代码 → 执行 → 报错 → 修正&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>让模型通过多轮对话解决开放问题&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>需要循环:追问 → 反思 → 补充 → 收敛&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Loop 是 Agent 与&amp;quot;普通 LLM 应用&amp;quot;的分水岭&lt;/strong>。没有循环的 LLM 只是个文本生成器;有了循环,它才有机会成为&amp;quot;会思考、会试错、会自我修正&amp;quot;的智能体。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/images/agent-loop-overview.svg"
loading="lazy"
alt="Agent Loop 总览:一次完整的智能体循环由 5 个阶段构成"
>&lt;/p>
&lt;hr>
&lt;h2 id="1-agent-loop-的解剖">1. Agent Loop 的解剖
&lt;/h2>&lt;p>无论你用 ReAct、Reflection 还是 LangGraph,一个标准的 Agent Loop 都由 5 个固定角色组成:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>角色&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>工程实现&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>State(状态)&lt;/strong>&lt;/td>
&lt;td>当前任务的所有上下文:目标、历史、记忆、工具结果&lt;/td>
&lt;td>一个 &lt;code>TypedDict&lt;/code> / Pydantic 模型&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Policy(策略)&lt;/strong>&lt;/td>
&lt;td>决定下一步该做什么&lt;/td>
&lt;td>通常是一次 LLM 调用 + 结构化输出&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Action(动作)&lt;/strong>&lt;/td>
&lt;td>执行策略选定的动作&lt;/td>
&lt;td>工具调用 / 子 Agent 调用 / 写文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Observer(观察)&lt;/strong>&lt;/td>
&lt;td>把动作结果回填到状态&lt;/td>
&lt;td>&lt;code>tool_result&lt;/code> 追加到 messages&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Terminator(终止器)&lt;/strong>&lt;/td>
&lt;td>判断循环是否该结束&lt;/td>
&lt;td>显式 finish / 步数上限 / 置信度阈值&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>把这 5 个角色串起来,就是最经典的循环骨架:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">init_state&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_goal&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">step&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_steps&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decision&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">policy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># LLM 决定下一步&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">terminator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">decision&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="c1"># 是否结束?&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">final_answer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">decision&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">observation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">action&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">decision&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 调工具 / 子 Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">update&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">observation&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 更新状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">forced_finish&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 兜底:步数耗尽&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>后续所有&amp;quot;花式&amp;quot;Loop(ReAct、Reflection、Reflexion、Plan-and-Execute)都只是&lt;strong>对这 5 个角色的不同实现与重组&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 id="2-经典-loop-模式">2. 经典 Loop 模式
&lt;/h2>&lt;h3 id="21-reactreason--act推理与行动交替">2.1 ReAct:Reason + Act(推理与行动交替)
&lt;/h3>&lt;p>&lt;strong>ReAct&lt;/strong>(Yao et al., 2022)是最广为人知的 Agent Loop 范式,它强制让 LLM 在每一步都按 &amp;ldquo;Thought → Action → Observation&amp;rdquo; 的格式输出:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Thought 1: 我需要先查一下用户的订单状态
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Action 1: get_order(order_id=&amp;#34;12345&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Observation 1: {&amp;#34;status&amp;#34;: &amp;#34;shipped&amp;#34;, &amp;#34;tracking&amp;#34;: &amp;#34;SF123...&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thought 2: 订单已发货,接下来需要根据物流信息估算送达时间
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Action 2: get_eta(tracking=&amp;#34;SF123...&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Observation 2: {&amp;#34;eta&amp;#34;: &amp;#34;2026-06-28&amp;#34;}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thought 3: 信息齐全,可以回答用户了
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Action 3: finish(answer=&amp;#34;您的订单预计 6 月 28 日送达&amp;#34;)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Loop 视角的关键点&lt;/strong>:ReAct 的循环不是&amp;quot;调 LLM 一次&amp;quot;,而是**&amp;ldquo;调 LLM → 解析动作 → 执行 → 把结果塞回 prompt → 再调 LLM&amp;rdquo;**。LLM 本身是无状态的,Loop 才是它&amp;quot;持续思考&amp;quot;的载体。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/images/react-loop.svg"
loading="lazy"
alt="ReAct Loop 的运行机制:Thought / Action / Observation 持续循环,直到 finish"
>&lt;/p>
&lt;p>&lt;strong>最小可运行的 ReAct Loop&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TOOLS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;get_weather&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">city&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">city&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 今天晴, 25°C&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;get_time&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;现在是 2026-06-26 15:00&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SYSTEM&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;你是一个 Agent。每轮必须严格输出:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Thought: &amp;lt;你的推理&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Action: &amp;lt;JSON,形如 {&amp;#34;name&amp;#34;: &amp;#34;工具名&amp;#34;, &amp;#34;args&amp;#34;: {...}} 或 {&amp;#34;name&amp;#34;: &amp;#34;finish&amp;#34;, &amp;#34;args&amp;#34;: {&amp;#34;answer&amp;#34;: &amp;#34;...&amp;#34;}}&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">react_loop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_goal&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">SYSTEM&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_goal&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">step&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_steps&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">resp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o-mini&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">resp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 解析 Action&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;Action:\s*(\{.*\})&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">S&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">ValueError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;step &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">step&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: 模型未输出 Action&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">action&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;finish&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">action&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 执行工具 → Observation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TOOLS&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">]](&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Observation: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">obs&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">TimeoutError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;超过最大步数,未收敛&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="22-reflection让模型对自己的输出打分并改写">2.2 Reflection:让模型对自己的输出打分并改写
&lt;/h3>&lt;p>&lt;strong>Reflection&lt;/strong>(Shinn et al., 2023)把 Loop 从&amp;quot;短反馈&amp;quot;升级成&amp;quot;长反馈&amp;quot;:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[生成阶段] LLM 生成初稿 answer_0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[反思阶段] LLM(可以是同一个,也可以是更强的 critic)输出:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - score: 0~10
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - critique: 具体问题清单
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - improved_answer: 改进版
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[循环 N 次] 或 直至 score &amp;gt;= threshold
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Loop 的关键差异&lt;/strong>:ReAct 的循环驱动来自&amp;quot;环境反馈&amp;quot;(工具返回),Reflection 的循环驱动来自&amp;quot;自我反馈&amp;quot;(LLM 评价自己)。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">reflection_loop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">draft_prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_rounds&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">threshold&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">draft&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">draft_prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">history&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_rounds&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">critique&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">critic_llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 请评估以下回答,输出 JSON:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;: 0-10, &amp;#34;issues&amp;#34;: [...], &amp;#34;improved&amp;#34;: &amp;#34;...&amp;#34;&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 原题: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">draft_prompt&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 当前回答: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">draft&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">improved&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">critique&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">critique&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="s2">&amp;#34;improved&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">history&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;round&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;score&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">score&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">score&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">threshold&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">improved&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">history&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">draft&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">improved&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">draft&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">history&lt;/span> &lt;span class="c1"># 兜底:返回最后一轮&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/images/reflection-loop.svg"
loading="lazy"
alt="Reflection Loop:生成 → 反思 → 重写,直到质量达标或达到上限"
>&lt;/p>
&lt;h3 id="23-reflexion把反思结果沉淀到长期记忆">2.3 Reflexion:把反思结果&amp;quot;沉淀&amp;quot;到长期记忆
&lt;/h3>&lt;p>Reflexion 是 Reflection 的进化:它不只是&amp;quot;当场改&amp;quot;,还会把反思结果&lt;strong>写入长期记忆&lt;/strong>,让下次遇到类似问题时少走弯路:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Trajectory → Reflector → Self-Critique
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Memory Store (vector db)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 下次同类任务 ← 检索相关反思 ←┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>工程上常见的实现:用一个独立的 &lt;code>MemoryStore&lt;/code> 对象,每次反射后写入 &lt;code>{situation, lesson, score_delta}&lt;/code>,在新任务的 system prompt 里通过 RAG 检索 top-k 相关反思塞进去。&lt;/p>
&lt;h3 id="24-plan-and-execute先想清楚再动手">2.4 Plan-and-Execute:先想清楚,再动手
&lt;/h3>&lt;p>Plan-and-Execute 把 Loop 拆成&lt;strong>两个嵌套循环&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">外层 Planner Loop: 任务 → 计划(步骤列表)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">内层 Executor Loop: 对计划中每个 step 反复 ReAct,直至完成
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">外层 Replanner: 若某个 step 失败,回到 Planner 重新规划
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>适合&lt;strong>长流程、阶段性强&lt;/strong>的任务(比如&amp;quot;调研 → 写作 → 校对 → 发布&amp;quot;),能让 Plan 阶段用更强的模型(慢但准),Execute 阶段用更便宜的模型(快且糙)。&lt;/p>
&lt;h3 id="25-camel多智能体角色扮演循环">2.5 CAMEL:多智能体角色扮演循环
&lt;/h3>&lt;p>&lt;strong>CAMEL&lt;/strong>(Communicative Agents for &amp;ldquo;Mind&amp;rdquo; Exploration of LLMs)用两个 Agent(User Proxy + Assistant)在 Loop 中互相对话,中间插入&amp;quot;Inception Prompt&amp;quot;防止角色漂移:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">User Proxy ──► Assistant ──► User Proxy ──► Assistant ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↑ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └──────────── Critic/Inception Prompt ←──────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>适合&lt;strong>对话式博弈、谈判模拟、教学场景&lt;/strong>等&amp;quot;两个角色反复讨论&amp;quot;的场景。&lt;/p>
&lt;hr>
&lt;h2 id="3-loop-的-4-大工程问题">3. Loop 的 4 大工程问题
&lt;/h2>&lt;p>把 Loop 从论文搬到生产,真正难的是下面 4 个问题。&lt;/p>
&lt;h3 id="31-状态管理loop-的内存">3.1 状态管理:Loop 的&amp;quot;内存&amp;quot;
&lt;/h3>&lt;p>&lt;strong>State 是 Loop 工程的命门&lt;/strong>。常见的 State 设计:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TypedDict&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">List&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph.message&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">add_messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TypedDict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 对话历史(自带 reducer,自动追加)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">add_messages&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 当前计划(Plan-and-Execute 用)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plan&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 已执行的步骤与结果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">past_steps&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">tuple&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 反思/记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">reflections&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 控制字段&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">step_count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">is_finished&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>设计原则&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>State 必须可序列化&lt;/strong>:能 &lt;code>pickle&lt;/code> / 写 Redis,这是断点续跑、调试回放的前提。&lt;/li>
&lt;li>&lt;strong>State 必须有 schema&lt;/strong>:用 TypedDict 或 Pydantic,避免字段拼写错误。&lt;/li>
&lt;li>&lt;strong>State 的写入要走 reducer&lt;/strong>:特别是 &lt;code>messages&lt;/code>,不能简单覆盖,而要 append,否则 Loop 会&amp;quot;失忆&amp;quot;。&lt;/li>
&lt;/ul>
&lt;h3 id="32-终止条件loop-不能停不下来">3.2 终止条件:Loop 不能&amp;quot;停不下来&amp;quot;
&lt;/h3>&lt;p>没有终止条件的 Loop 就是个无底洞。&lt;strong>生产中至少要有 3 重保险&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">should_continue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;continue&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;end&amp;#34;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 显式 finish(由 LLM 主动调用 finish 工具)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;is_finished&amp;#34;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;end&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 步数硬上限(防止 token 爆炸)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;step_count&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;end&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 步数软上限 + 强制收敛(兜底,走 LLM 总结)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;step_count&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">15&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;force_summarize&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 重复检测(防止模型在两个动作间反复横跳)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;end&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;continue&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>真实生产事故&lt;/strong>经常源于:忘了设上限,或者上限设了但没接 billing 报警,一夜烧掉几千美元 Token。&lt;/p>
&lt;h3 id="33-记忆机制loop-跨轮次活下来">3.3 记忆机制:Loop 跨轮次&amp;quot;活下来&amp;quot;
&lt;/h3>&lt;p>Loop 内部的 state 是&lt;strong>短期记忆&lt;/strong>,跨会话需要&lt;strong>长期记忆&lt;/strong>:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>存储&lt;/th>
&lt;th>写入时机&lt;/th>
&lt;th>检索时机&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>短期(Scratchpad)&lt;/td>
&lt;td>In-memory state&lt;/td>
&lt;td>每一步&lt;/td>
&lt;td>每一步&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>程序性(Procedural)&lt;/td>
&lt;td>系统 prompt&lt;/td>
&lt;td>启动时&lt;/td>
&lt;td>启动时&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>语义性(Semantic)&lt;/td>
&lt;td>Vector DB&lt;/td>
&lt;td>反思后/总结后&lt;/td>
&lt;td>新任务开始&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>情节性(Episodic)&lt;/td>
&lt;td>时序 DB&lt;/td>
&lt;td>任务结束时&lt;/td>
&lt;td>反思阶段&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">LongTermMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector_store&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embedder&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">emb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vector_store&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embedder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">situation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lesson&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;情境: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">situation&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">教训: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">lesson&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">emb&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">situation&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">recall&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">current_situation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">emb&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current_situation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vs&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">top_k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="34-工具调用loop-与现实世界的接口">3.4 工具调用:Loop 与现实世界的接口
&lt;/h3>&lt;p>Loop 里 80% 的 Action 都是&amp;quot;调工具&amp;quot;。工具设计要点:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>工具描述即 Prompt&lt;/strong>——LLM 选错工具,90% 是工具描述写得烂。&lt;/li>
&lt;li>&lt;strong>工具要返回结构化数据&lt;/strong>(JSON)而非自然语言,便于程序解析。&lt;/li>
&lt;li>&lt;strong>工具有超时和重试&lt;/strong>,避免一个慢工具把整个 Loop 拖死。&lt;/li>
&lt;li>&lt;strong>危险操作(写库、发邮件、删文件)走二次确认分支&lt;/strong>,而不是直接执行。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">send_email&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">subject&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;发送邮件(危险操作,需要 confirm=true 才真正发送)。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">confirm&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;[DRY-RUN] 将发送邮件给 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">,主题: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">_smtp_send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">subject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">body&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="4-用-langgraph-把-loop-工程化">4. 用 LangGraph 把 Loop 工程化
&lt;/h2>&lt;p>手写 ReAct Loop 在 demo 阶段可以,但生产中你需要:可视化、断点续跑、人介入、可观测——这些 LangGraph 已经帮你做好。&lt;/p>
&lt;h3 id="41-最小的-langgraph-agent-loop">4.1 最小的 LangGraph Agent Loop
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">List&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing_extensions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">TypedDict&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">START&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">END&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph.message&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">add_messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.tools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.prebuilt&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ToolNode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;模拟搜索工具。&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;关于 &amp;#39;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#39; 的搜索结果: ...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o-mini&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind_tools&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">State&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TypedDict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add_messages&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">step_count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">])],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;step_count&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;step_count&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">should_continue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">Literal&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;tools&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">END&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">last&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">last&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tool_calls&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;tools&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">END&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">State&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tools&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ToolNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">START&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">add_conditional_edges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">should_continue&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tools&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stream&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[(&lt;/span>&lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;查一下 LangGraph 最新版本&amp;#34;&lt;/span>&lt;span class="p">)]}):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="42-加-reflection-节点">4.2 加 Reflection 节点
&lt;/h3>&lt;p>在上面的图里多加一个 &lt;code>reflect&lt;/code> 节点和一条边:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">reflect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">State&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">last_answer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">critique&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;system&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;你是质检员,打分 0-10,并指出问题。&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;评价: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">last_answer&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">parse_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">critique&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">score&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;step_count&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">END&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span> &lt;span class="c1"># 不满意,把 critique 塞回 messages 让 agent 重写&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>完整图就是:&lt;strong>agent → tools → agent → &amp;hellip; → reflect → (END | agent)&lt;/strong>。&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ai-agent-loop-%E5%B7%A5%E7%A8%8B%E5%8E%9F%E7%90%86%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%AE%9E%E7%8E%B0/images/langgraph-loop.svg"
loading="lazy"
alt="LangGraph 实现 Agent Loop:节点、边、终止条件"
>&lt;/p>
&lt;h3 id="43-加人介入human-in-the-loop">4.3 加人介入(Human-in-the-Loop)
&lt;/h3>&lt;p>生产中,任何高风险分支都该有&amp;quot;暂停等人确认&amp;quot;的节点:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;human_review&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 实际是中断点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;human_review&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">condition&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">lambda&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;send_email&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>部署时打开 &lt;code>interrupt_before=[&amp;quot;human_review&amp;quot;]&lt;/code>,LangGraph 会在该节点暂停,把当前 state 持久化,等人通过 API 决策后再 resume。&lt;/p>
&lt;h3 id="44-测试比人介入更重要的一环">4.4 测试:比&amp;quot;人介入&amp;quot;更重要的一环
&lt;/h3>&lt;p>人介入(Human-in-the-Loop)常被当作安全兜底,但它不是目的,而是&lt;strong>过渡手段&lt;/strong>。真正能让 Agent Loop 规模化运转的,是&lt;strong>把&amp;quot;人测&amp;quot;变成&amp;quot;自动测&amp;quot;&lt;/strong>。&lt;/p>
&lt;p>如果测试做得好,Loop 在每一轮迭代后都能自动验证中间产物(代码是否编译、单测是否通过、API 返回是否符合 schema、生成内容是否满足评分标准),那么大量原本需要人工复核的环节就会被自动化覆盖,人工介入才会被压缩到真正的&amp;quot;异常&amp;quot;和&amp;quot;边界&amp;quot;上。反之,如果测试缺位,Loop 跑完后人还是要从头到尾做验收,Agent 带来的效率提升会被人工测试抵消大半。&lt;/p>
&lt;p>所以 Agent Loop 工程里真正值得重点设计的,其实是两个节点:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>需求提出&lt;/strong>:人把目标、验收标准、约束说清楚——这是 Loop 的输入。&lt;/li>
&lt;li>&lt;strong>测试介入&lt;/strong>:用可执行、可自动化的测试把输出验回来——这是 Loop 的收敛判据。&lt;/li>
&lt;/ol>
&lt;p>这个思路其实来源于&lt;strong>芯片设计&lt;/strong>的理念:芯片一旦流片,发现问题就是天价损失;但如果前期验证(仿真、形式验证、原型测试)做得充分,就能把风险挡在量产之前。AI 时代的 Agent 部署也类似——Loop 里多跑几轮测试,多消耗一些 token,边际成本几乎为零;可一旦把有缺陷的产出发布上线,修复成本和业务影响会大得多。&lt;/p>
&lt;p>因此,不要把测试看成&amp;quot;额外的开销&amp;quot;,而要把它当成&lt;strong>用廉价 token 换取上线确定性&lt;/strong>的投资。其余环节(规划、执行、反思)都应该朝着&amp;quot;让需求端到测试端之间的循环尽量少依赖人工&amp;quot;去优化。&lt;/p>
&lt;hr>
&lt;h2 id="5-loop-的可观测性">5. Loop 的可观测性
&lt;/h2>&lt;p>Loop 跑起来后,&lt;strong>你必须能回答这三个问题&lt;/strong>:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>它在每一步想了什么、做了什么?&lt;/strong> → 记录完整的 messages / tool_calls / observations。&lt;/li>
&lt;li>&lt;strong>它为什么没收敛?&lt;/strong> → 终止时把&amp;quot;最后 3 步状态&amp;quot;dump 出来。&lt;/li>
&lt;li>&lt;strong>它花了多少钱/多少时间?&lt;/strong> → 每个 step 单独计费。&lt;/li>
&lt;/ol>
&lt;p>LangGraph 与 LangSmith 集成最省事:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;LANGSMITH_TRACING&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;LANGSMITH_API_KEY&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 之后每次 graph.invoke() 都会自动 trace 到 LangSmith&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>自建可观测栈的话,关键埋点:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent_loop&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">span&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;goal&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_goal&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">step&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_steps&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;step_&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">step&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;messages_count&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tokens_in&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">usage&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">prompt_tokens&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tokens_out&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">usage&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completion_tokens&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="6-loop-的反模式">6. Loop 的反模式
&lt;/h2>&lt;p>写了几年 Agent Loop 后,我总结出&lt;strong>最常踩的几个坑&lt;/strong>:&lt;/p>
&lt;h3 id="61-无限循环--上限过高">6.1 无限循环 / 上限过高
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 反例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">step&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">itertools&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">():&lt;/span> &lt;span class="c1"># 真的无限&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>一定要设硬上限&lt;/strong>,而且上限要和 budget 系统联动。&lt;/p>
&lt;h3 id="62-把所有上下文都塞进-prompt">6.2 把所有上下文都塞进 Prompt
&lt;/h3>&lt;p>Loop 跑 20 步,prompt 里堆 20 轮对话 + 10 个工具结果,Token 直接爆炸。要么用&lt;strong>摘要压缩&lt;/strong>,要么用&lt;strong>外部状态机&lt;/strong>把历史写到外部存储,prompt 里只保留&amp;quot;最近 K 步 + 关键事件&amp;quot;。&lt;/p>
&lt;h3 id="63-没有-fallback-工具调用失败">6.3 没有 fallback 工具调用失败
&lt;/h3>&lt;p>工具超时 / 5xx / 返回错误 JSON 都太常见。&lt;strong>Action 层必须包一层 retry + fallback&lt;/strong>,而不是直接把异常抛给 LLM 让它自己&amp;quot;看着办&amp;quot;。&lt;/p>
&lt;h3 id="64-终止条件依赖-llm-自报">6.4 终止条件依赖 LLM 自报
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 反例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">done&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;请判断任务是否完成,只回答 yes/no&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;yes&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>LLM 自报&amp;quot;完成&amp;quot;非常不可靠,&lt;strong>用结构化 finish 工具 + 外部校验&lt;/strong>双保险。&lt;/p>
&lt;h3 id="65-跨任务共享同一个-longterm-memory-不做清理">6.5 跨任务共享同一个 LongTerm Memory 不做清理
&lt;/h3>&lt;p>记忆库会越积越杂,质量会越来越差。定期做&lt;strong>记忆蒸馏&lt;/strong>:把多条相似记忆合并成一条;对引用次数为 0 的记忆做 GC。&lt;/p>
&lt;hr>
&lt;h2 id="7-一个生产级-agent-loop-的参考骨架">7. 一个生产级 Agent Loop 的参考骨架
&lt;/h2>&lt;p>综合前文,生产里我推荐的 Agent Loop 骨架长这样:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 主循环 (Agent Loop) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ┌────────┐ ┌────────┐ ┌─────────────┐ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ Planner│───►│Executor│───►│ Reflector │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └────────┘ └────┬───┘ └──────┬──────┘ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ▲ │ │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ▼ ▼ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ┌────────┐ ┌─────────────┐ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └────────│Replanner│ │Memory Writer│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └────────┘ └─────────────┘ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 三重保险:Step 上限 / Token 上限 / 重复检测 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 三层记忆:State / Vector DB / External Store │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>对应到 LangGraph,就是带分支的图 + 多个子节点 + 持久化 checkpointer。&lt;strong>所有&amp;quot;花式&amp;quot;Agent 框架的差异,本质上都是这个骨架的不同拓扑&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 id="8-总结">8. 总结
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Agent Loop = 状态 + 策略 + 动作 + 观察 + 终止器&lt;/strong> 的循环。&lt;/li>
&lt;li>经典模式:&lt;strong>ReAct&lt;/strong>(环境反馈)、&lt;strong>Reflection&lt;/strong>(自我反馈)、&lt;strong>Reflexion&lt;/strong>(反馈入长期记忆)、&lt;strong>Plan-and-Execute&lt;/strong>(嵌套循环)、&lt;strong>CAMEL&lt;/strong>(双角色循环)。&lt;/li>
&lt;li>工程化的 4 大难题:&lt;strong>状态管理、终止条件、记忆机制、工具设计&lt;/strong>。&lt;/li>
&lt;li>落地推荐 &lt;strong>LangGraph&lt;/strong>:它把 Loop 的节点、边、终止、持久化、人介入都做成了原生能力。&lt;/li>
&lt;li>&lt;strong>可观测性和硬上限是 Loop 工程的生死线&lt;/strong>——没有它们,Agent 就是个会烧钱、会失控的黑盒。&lt;/li>
&lt;/ul>
&lt;p>掌握了 Agent Loop,你就不再是&amp;quot;在调 LLM&amp;quot;,而是在&lt;strong>设计一个由 LLM 驱动的分布式系统&lt;/strong>——这正是&amp;quot;智能体编排设计工程师&amp;quot;的核心能力。&lt;/p>
&lt;hr>
&lt;h2 id="参考资料">参考资料
&lt;/h2>&lt;ul>
&lt;li>Yao et al., &lt;strong>ReAct: Synergizing Reasoning and Acting in Language Models&lt;/strong>, 2022.&lt;/li>
&lt;li>Shinn et al., &lt;strong>Reflexion: Language Agents with Verbal Reinforcement Learning&lt;/strong>, 2023.&lt;/li>
&lt;li>Wei et al., &lt;strong>Chain-of-Thought Prompting Elicits Reasoning in Large Language Models&lt;/strong>, 2022.&lt;/li>
&lt;li>LangGraph Documentation, &lt;a class="link" href="https://langchain-ai.github.io/langgraph/" target="_blank" rel="noopener"
>https://langchain-ai.github.io/langgraph/&lt;/a>&lt;/li>
&lt;li>《&lt;a class="link" href="https://www.zata.cc/p/%e6%99%ba%e8%83%bd%e4%bd%93%e7%bc%96%e6%8e%92%e8%ae%be%e8%ae%a1%e5%b7%a5%e7%a8%8b%e5%b8%88%e5%ad%a6%e4%b9%a0%e6%8c%87%e5%8d%97/" >智能体编排设计工程师学习指南&lt;/a>》(本系列前篇)&lt;/li>
&lt;/ul></description></item><item><title>openclaw</title><link>https://www.zata.cc/p/openclaw/</link><pubDate>Tue, 24 Feb 2026 17:41:27 +0800</pubDate><guid>https://www.zata.cc/p/openclaw/</guid><description>&lt;img src="https://www.zata.cc/p/openclaw/images/index/index.png" alt="Featured image of post openclaw" />&lt;h1 id="部署">部署
&lt;/h1>&lt;p>&lt;strong>dokploy&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/openclaw/images/index/image.png"
width="377"
height="360"
srcset="https://www.zata.cc/p/openclaw/images/index/image_hu2562861698972220391.png 480w, https://www.zata.cc/p/openclaw/images/index/image_hu17209519746675233341.png 1024w"
loading="lazy"
alt="选择模板"
class="gallery-image"
data-flex-grow="104"
data-flex-basis="251px"
>&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/openclaw/images/index/image-1.png"
width="1714"
height="880"
srcset="https://www.zata.cc/p/openclaw/images/index/image-1_hu2733058384225234895.png 480w, https://www.zata.cc/p/openclaw/images/index/image-1_hu5630461788341566435.png 1024w"
loading="lazy"
alt="选择openclaw"
class="gallery-image"
data-flex-grow="194"
data-flex-basis="467px"
>&lt;/p>
&lt;p>修改env,记得,选择了那个提供商就要写 &amp;ldquo;提供商/模型名&amp;rdquo;
&lt;img src="https://www.zata.cc/p/openclaw/images/index/image-2.png"
width="1148"
height="515"
srcset="https://www.zata.cc/p/openclaw/images/index/image-2_hu4163158587647618681.png 480w, https://www.zata.cc/p/openclaw/images/index/image-2_hu16292709835544318863.png 1024w"
loading="lazy"
alt="修改一下env"
class="gallery-image"
data-flex-grow="222"
data-flex-basis="534px"
>&lt;/p>
&lt;h1 id="openclaw-能做什么2026-年顶级-ai-生产力助手">OpenClaw 能做什么：2026 年顶级 AI 生产力助手
&lt;/h1>&lt;p>&lt;strong>一、核心能力：它如何彻底改变工作方式&lt;/strong>&lt;/p>
&lt;p>OpenClaw 不是一个简单的聊天机器人，而是一个&lt;strong>多智能体协作平台&lt;/strong>，可以同时协调多个 AI 专家为你工作。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>自动化复杂工作流&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>智能任务分解&lt;/strong>：给它一个复杂项目，它能自动拆解成可执行步骤&lt;/li>
&lt;li>&lt;strong>并行处理&lt;/strong>：多个 AI Agent 同时处理不同子任务&lt;/li>
&lt;li>&lt;strong>结果整合&lt;/strong>：自动汇总所有结果，生成完整交付物&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>记忆与学习能力&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>长期记忆&lt;/strong>：记住你的工作习惯、偏好、历史问题&lt;/li>
&lt;li>&lt;strong>智能检索&lt;/strong>：需要时自动调用相关历史信息&lt;/li>
&lt;li>&lt;strong>持续优化&lt;/strong>：越用越了解你，越用越高效&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>二、具体能做什么：八大生产力场景&lt;/strong>&lt;/p>
&lt;p>&lt;strong>场景 1：&lt;/strong> &lt;strong>智能编程与代码开发&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>编写完整项目代码（全栈开发）&lt;/li>
&lt;li>自动代码审查和优化&lt;/li>
&lt;li>生成单元测试（覆盖率 85%+）&lt;/li>
&lt;li>技术文档自动生成&lt;/li>
&lt;li>Bug 智能诊断和修复建议&lt;/li>
&lt;li>代码重构和架构优化&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：你说&amp;quot;帮我开发一个电商网站&amp;quot;，它能：&lt;/p>
&lt;ol>
&lt;li>设计数据库架构&lt;/li>
&lt;li>编写后端 API&lt;/li>
&lt;li>开发前端界面&lt;/li>
&lt;li>配置部署脚本&lt;/li>
&lt;li>编写测试用例&lt;/li>
&lt;li>生成用户手册&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>&lt;strong>场景 2：&lt;/strong> &lt;strong>智能分析与报告生成&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>自动收集网络信息（新闻、社交媒体、竞品数据）&lt;/li>
&lt;li>情感分析和趋势预测&lt;/li>
&lt;li>生成专业分析报告（市场分析、竞品分析）&lt;/li>
&lt;li>数据可视化图表生成&lt;/li>
&lt;li>实时监控和预警&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：你说&amp;quot;分析一下最近 AI 行业的发展趋势&amp;quot;，它能：&lt;/p>
&lt;ol>
&lt;li>收集最新行业新闻&lt;/li>
&lt;li>分析技术发展路径&lt;/li>
&lt;li>评估市场机会&lt;/li>
&lt;li>生成图文并茂的报告&lt;/li>
&lt;li>提供投资建议&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>&lt;strong>场景 3：&lt;/strong> &lt;strong>会议与沟通效率提升&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>会议自动转录和摘要（1 小时会议→5 分钟要点）&lt;/li>
&lt;li>提取行动项并分配责任人&lt;/li>
&lt;li>自动生成会议纪要&lt;/li>
&lt;li>后续跟进提醒&lt;/li>
&lt;li>跨语言实时翻译&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：一次国际团队会议后：&lt;/p>
&lt;ul>
&lt;li>自动生成中英双语纪要&lt;/li>
&lt;li>提取 10 个行动项分配给对应人员&lt;/li>
&lt;li>同步到 Jira/Trello&lt;/li>
&lt;li>设置跟进提醒&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>场景 4：&lt;/strong> &lt;strong>文档处理自动化&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>合同自动审查（找风险条款）&lt;/li>
&lt;li>技术文档自动编写&lt;/li>
&lt;li>多格式转换（Word→PDF→Markdown）&lt;/li>
&lt;li>内容摘要生成&lt;/li>
&lt;li>多语言翻译&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：一份 50 页技术文档：&lt;/p>
&lt;ul>
&lt;li>5 分钟生成执行摘要&lt;/li>
&lt;li>自动检查一致性&lt;/li>
&lt;li>生成常见问题解答&lt;/li>
&lt;li>翻译成 3 种语言&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>场景 5：&lt;/strong> &lt;strong>学习与研究加速&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>学术论文自动阅读和摘要&lt;/li>
&lt;li>研究趋势分析&lt;/li>
&lt;li>知识点脑图生成&lt;/li>
&lt;li>个性化学习计划&lt;/li>
&lt;li>智能问答导师&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：研究一个新领域时：&lt;/p>
&lt;ul>
&lt;li>自动阅读 50 篇相关论文&lt;/li>
&lt;li>提取核心观点&lt;/li>
&lt;li>构建知识图谱&lt;/li>
&lt;li>设计学习路线图&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>场景 6：&lt;/strong> &lt;strong>项目管理自动化&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>项目计划自动生成&lt;/li>
&lt;li>任务分解和排期&lt;/li>
&lt;li>进度跟踪和预警&lt;/li>
&lt;li>风险评估&lt;/li>
&lt;li>资源分配优化&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：启动一个新项目：&lt;/p>
&lt;ul>
&lt;li>自动创建项目计划&lt;/li>
&lt;li>分解成 100+ 具体任务&lt;/li>
&lt;li>分配时间线&lt;/li>
&lt;li>监控风险点&lt;/li>
&lt;li>每周自动报告进度&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>场景 7：&lt;/strong> &lt;strong>创意与内容创作&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>营销文案自动生成&lt;/li>
&lt;li>社交媒体内容规划&lt;/li>
&lt;li>视频脚本创作&lt;/li>
&lt;li>广告创意生成&lt;/li>
&lt;li>品牌故事编写&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：产品发布前：&lt;/p>
&lt;ul>
&lt;li>生成发布会演讲稿&lt;/li>
&lt;li>创作 10 篇宣传文章&lt;/li>
&lt;li>设计社交媒体活动&lt;/li>
&lt;li>编写用户案例研究&lt;/li>
&lt;li>生成 FAQ 文档&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>场景 8：&lt;/strong> &lt;strong>个人效率助手&lt;/strong>&lt;/p>
&lt;p>它能：&lt;/p>
&lt;ul>
&lt;li>日报/周报自动生成&lt;/li>
&lt;li>邮件智能分类和回复&lt;/li>
&lt;li>日程智能安排&lt;/li>
&lt;li>会议准备材料整理&lt;/li>
&lt;li>旅行计划制定&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>实例：每天工作开始：&lt;/p>
&lt;ul>
&lt;li>自动生成今日重点任务&lt;/li>
&lt;li>处理未读邮件（分类 + 草拟回复）&lt;/li>
&lt;li>提醒重要会议和准备材料&lt;/li>
&lt;li>晚上自动总结日报&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;strong>三、跨平台协作能力&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>通讯工具集成&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>飞书/钉钉&lt;/strong>：直接在群里@它完成任务&lt;/li>
&lt;li>&lt;strong>Slack&lt;/strong>：实时协作和提醒&lt;/li>
&lt;li>&lt;strong>微信/Telegram&lt;/strong>：移动端随时使用&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>开发工具集成&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GitHub&lt;/strong>：自动代码审查、PR 处理&lt;/li>
&lt;li>&lt;strong>Jira/Trello&lt;/strong>：任务自动创建和更新&lt;/li>
&lt;li>&lt;strong>Docker/K8s&lt;/strong>：部署脚本自动生成&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>办公软件集成&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Google Docs/Office&lt;/strong>：文档协作&lt;/li>
&lt;li>&lt;strong>Notion/Confluence&lt;/strong>：知识管理&lt;/li>
&lt;li>&lt;strong>Calendar&lt;/strong>：日程管理&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>四、多 Agent 团队协作模式&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>专家团队模式&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>你有专属的专家团队：&lt;/p>
&lt;ul>
&lt;li>技术专家：负责技术问题&lt;/li>
&lt;li>业务专家：负责商业分析&lt;/li>
&lt;li>设计专家：负责 UI/UX&lt;/li>
&lt;li>文案专家：负责内容创作&lt;/li>
&lt;li>项目经理：协调所有工作&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>流水线模式&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>复杂任务自动流水线处理：
输入 → 分析 → 设计 → 执行 → 测试 → 交付
每个环节由专门 Agent 负责&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>辩论模式&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>重要决策时：&lt;/p>
&lt;ul>
&lt;li>多个 Agent 从不同角度分析&lt;/li>
&lt;li>进行虚拟&amp;quot;辩论&amp;quot;&lt;/li>
&lt;li>最终给出最优方案&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>五、实际应用案例&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>案例 1：&lt;/strong> &lt;strong>创业公司 CTO 的一天&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>早上 9:00：&lt;/p>
&lt;ul>
&lt;li>OpenClaw 自动生成昨日技术日报&lt;/li>
&lt;li>列出今日技术风险点&lt;/li>
&lt;/ul>
&lt;p>上午：&lt;/p>
&lt;ul>
&lt;li>自动审查团队代码提交&lt;/li>
&lt;li>生成架构优化建议&lt;/li>
&lt;li>处理技术债务&lt;/li>
&lt;/ul>
&lt;p>下午：&lt;/p>
&lt;ul>
&lt;li>参与产品讨论，自动记录和总结&lt;/li>
&lt;li>编写技术方案文档&lt;/li>
&lt;li>准备技术分享材料&lt;/li>
&lt;/ul>
&lt;p>结果：技术管理工作效率提升 300%&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>案例 2：&lt;/strong> &lt;strong>市场总监的营销活动&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>活动前：&lt;/p>
&lt;ul>
&lt;li>自动分析目标用户画像&lt;/li>
&lt;li>生成营销策略方案&lt;/li>
&lt;li>创作各类宣传素材&lt;/li>
&lt;/ul>
&lt;p>活动中：&lt;/p>
&lt;ul>
&lt;li>实时监控社交媒体反馈&lt;/li>
&lt;li>自动调整投放策略&lt;/li>
&lt;li>生成每日效果报告&lt;/li>
&lt;/ul>
&lt;p>活动后：&lt;/p>
&lt;ul>
&lt;li>自动生成 ROI 分析报告&lt;/li>
&lt;li>总结成功经验和教训&lt;/li>
&lt;li>归档所有素材和文档&lt;/li>
&lt;/ul>
&lt;p>结果：营销效果分析时间减少 80%&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>案例 3：&lt;/strong> &lt;strong>学术研究者的研究过程&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>文献调研：&lt;/p>
&lt;ul>
&lt;li>自动搜索相关论文&lt;/li>
&lt;li>提取核心观点和结论&lt;/li>
&lt;li>构建研究脉络图&lt;/li>
&lt;/ul>
&lt;p>实验设计：&lt;/p>
&lt;ul>
&lt;li>协助设计实验方案&lt;/li>
&lt;li>生成实验代码&lt;/li>
&lt;li>设计数据收集方法&lt;/li>
&lt;/ul>
&lt;p>论文写作：&lt;/p>
&lt;ul>
&lt;li>自动生成论文大纲&lt;/li>
&lt;li>协助写作各章节&lt;/li>
&lt;li>格式检查和优化&lt;/li>
&lt;/ul>
&lt;p>结果：研究效率提升 200%&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>六、独特的竞争优势&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>真·智能协作&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>不是简单问答，而是真正的团队协作&lt;/li>
&lt;li>多个 AI Agent 各司其职&lt;/li>
&lt;li>像有了一整个专业团队&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>记忆与学习&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>长期记忆你的偏好&lt;/li>
&lt;li>越用越懂你的需求&lt;/li>
&lt;li>工作质量持续提升&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>企业级可靠性&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>多模型备份（一个挂了自动切另一个）&lt;/li>
&lt;li>任务状态持久化（断电不丢数据）&lt;/li>
&lt;li>完整审计日志&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>七、适合谁用？&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>✅ &lt;strong>非常适合&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>创业者/企业家：一人顶一个团队&lt;/li>
&lt;li>开发者/工程师：编码效率翻倍&lt;/li>
&lt;li>管理者/领导：决策支持 + 团队管理&lt;/li>
&lt;li>研究人员/学者：研究加速器&lt;/li>
&lt;li>创作者/营销人：内容生产机器&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>⚠️ &lt;strong>需要注意&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>需要一定的技术理解能力&lt;/li>
&lt;li>关键决策仍需人工审核&lt;/li>
&lt;li>涉及安全信息需谨慎配置&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>八、开始使用建议&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>新手入门（第 1 周）&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>从&lt;strong>一个具体任务&lt;/strong>开始&lt;/li>
&lt;li>体验&lt;strong>智能分解&lt;/strong>的能力&lt;/li>
&lt;li>感受&lt;strong>多 Agent 协作&lt;/strong>的效果&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>进阶使用（1 个月后）&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>配置&lt;strong>专属工作流&lt;/strong>&lt;/li>
&lt;li>建立&lt;strong>记忆库&lt;/strong>&lt;/li>
&lt;li>集成&lt;strong>外部工具&lt;/strong>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>专家级（3 个月后）&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>定制&lt;strong>专属 Agent 团队&lt;/strong>&lt;/li>
&lt;li>自动化&lt;strong>复杂业务流程&lt;/strong>&lt;/li>
&lt;li>建立&lt;strong>企业级应用&lt;/strong>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>&lt;strong>一句话总结&lt;/strong>：OpenClaw 就像给你配备了一个 24 小时待命的&lt;strong>AI 专家团队&lt;/strong>，把一个人变成一个高效的组织，让复杂变简单，让耗时变即时。&lt;/p>
&lt;p>&lt;strong>想先了解哪个场景的具体应用？我可以详细介绍那个场景的实战操作。&lt;/strong>&lt;/p></description></item><item><title>nl2sql</title><link>https://www.zata.cc/p/nl2sql/</link><pubDate>Sat, 21 Feb 2026 22:11:24 +0800</pubDate><guid>https://www.zata.cc/p/nl2sql/</guid><description>&lt;img src="https://www.zata.cc/p/nl2sql/images/index/index.png" alt="Featured image of post nl2sql" />&lt;p>参考文献&lt;/p>
&lt;p>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/1940183214875456279?share_code=OrKMQdXbyb9m&amp;amp;utm_psn=2008228722545804102" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/1940183214875456279?share_code=OrKMQdXbyb9m&amp;utm_psn=2008228722545804102&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/1939282632232896429?share_code=dBY13Eup1TyS&amp;amp;utm_psn=2008229699600532430" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/1939282632232896429?share_code=dBY13Eup1TyS&amp;utm_psn=2008229699600532430&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/1958855721698436339?share_code=WMUESfzgF9zK&amp;amp;utm_psn=2008230269199591123" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/1958855721698436339?share_code=WMUESfzgF9zK&amp;utm_psn=2008230269199591123&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/1915001536171476928?share_code=f1cneaAWmTOJ&amp;amp;utm_psn=2008230432307693135" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/1915001536171476928?share_code=f1cneaAWmTOJ&amp;utm_psn=2008230432307693135&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/706470524?share_code=E5JANjPbKiul&amp;amp;utm_psn=2008230704283136797" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/706470524?share_code=E5JANjPbKiul&amp;utm_psn=2008230704283136797&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/1932039631936915022?share_code=N8MZYAjAFPsu&amp;amp;utm_psn=2008230946713929572" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/1932039631936915022?share_code=N8MZYAjAFPsu&amp;utm_psn=2008230946713929572&lt;/a>
&lt;a class="link" href="https://zhuanlan.zhihu.com/p/693358371?share_code=1oWpl3ZkpqkpI&amp;amp;utm_psn=2008231003643212020" target="_blank" rel="noopener"
>https://zhuanlan.zhihu.com/p/693358371?share_code=1oWpl3ZkpqkpI&amp;utm_psn=2008231003643212020&lt;/a>&lt;/p></description></item><item><title>n8n</title><link>https://www.zata.cc/p/n8n/</link><pubDate>Tue, 23 Dec 2025 23:36:46 +0800</pubDate><guid>https://www.zata.cc/p/n8n/</guid><description>&lt;img src="https://www.zata.cc/p/n8n/images/index/index.png" alt="Featured image of post n8n" />&lt;h2 id="安装">安装
&lt;/h2>&lt;p>docker run -itd &amp;ndash;name n8n -p 5678:5678 n8nio/n8n&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/n8n/images/index/image.png"
width="5104"
height="2714"
srcset="https://www.zata.cc/p/n8n/images/index/image_hu14161349112760946913.png 480w, https://www.zata.cc/p/n8n/images/index/image_hu458891721534625158.png 1024w"
loading="lazy"
alt="访问5678端口，随便输入邮箱密码即可注册"
class="gallery-image"
data-flex-grow="188"
data-flex-basis="451px"
>&lt;/p></description></item><item><title>Agent 记忆模块深度技术文档</title><link>https://www.zata.cc/p/agent-%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9D%97%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/</link><pubDate>Tue, 17 Jun 2025 11:00:00 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9D%97%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9D%97%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/images/index/index.png" alt="Featured image of post Agent 记忆模块深度技术文档" />&lt;h1 id="agent-记忆模块深度技术文档">Agent 记忆模块深度技术文档
&lt;/h1>&lt;h2 id="一记忆模块概述">一、记忆模块概述
&lt;/h2>&lt;h3 id="11-为什么-agent-需要记忆">1.1 为什么 Agent 需要记忆？
&lt;/h3>&lt;p>在传统软件开发中，系统状态存储在数据库、缓存或文件系统中。但当系统从&amp;quot;规则驱动&amp;quot;转向&amp;quot;AI 驱动&amp;quot;时，记忆成为Agent 的核心能力之一：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>传统系统&lt;/th>
&lt;th>Agent 系统&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>状态存储&lt;/strong>&lt;/td>
&lt;td>结构化数据库&lt;/td>
&lt;td>非结构化向量存储 + Context Window&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>知识获取&lt;/strong>&lt;/td>
&lt;td>硬编码规则&lt;/td>
&lt;td>从对话、文档中学习&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>上下文理解&lt;/strong>&lt;/td>
&lt;td>Session 管理&lt;/td>
&lt;td>记忆检索 + LLM 推理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>个性化能力&lt;/strong>&lt;/td>
&lt;td>用户配置表&lt;/td>
&lt;td>长期记忆 + 用户画像&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>核心价值&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>连续性&lt;/strong>：跨对话保持上下文，记住用户偏好&lt;/li>
&lt;li>&lt;strong>学习能力&lt;/strong>：从历史交互中积累知识&lt;/li>
&lt;li>&lt;strong>个性化&lt;/strong>：针对不同用户提供定制化服务&lt;/li>
&lt;li>&lt;strong>效率优化&lt;/strong>：避免重复询问相同信息&lt;/li>
&lt;/ul>
&lt;h3 id="12-记忆的分类维度">1.2 记忆的分类维度
&lt;/h3>&lt;p>从不同维度理解记忆系统：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 记忆系统分类矩阵 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 时效维度 存储维度 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 短期记忆 ├─ 内存存储 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 长期记忆 ├─ 向量数据库 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 工作记忆 └─ 关系数据库 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 内容维度 访问维度 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 对话历史 ├─ 精确匹配 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 知识事实 ├─ 语义检索 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 用户画像 └─ 混合检索 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 任务状态 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="二短期记忆short-term-memory">二、短期记忆（Short-term Memory）
&lt;/h2>&lt;h3 id="21-核心概念">2.1 核心概念
&lt;/h3>&lt;p>短期记忆存储在 LLM 的 &lt;strong>Context Window&lt;/strong>（上下文窗口）中，随对话结束而消失。&lt;/p>
&lt;p>&lt;strong>关键参数&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Context Window 大小&lt;/strong>：不同模型的支持能力
&lt;ul>
&lt;li>GPT-4 Turbo：128K tokens&lt;/li>
&lt;li>GPT-4：8K tokens&lt;/li>
&lt;li>Claude 3 Opus：200K tokens&lt;/li>
&lt;li>Claude 3.5 Sonnet：200K tokens&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>核心挑战&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Token 限制&lt;/strong>：长对话会超出限制导致截断&lt;/li>
&lt;li>&lt;strong>成本问题&lt;/strong>：每次请求都要传递完整历史&lt;/li>
&lt;li>&lt;strong>注意力稀释&lt;/strong>：上下文过长影响模型推理质量&lt;/li>
&lt;/ol>
&lt;h3 id="22-实现方案对比">2.2 实现方案对比
&lt;/h3>&lt;h4 id="方案一完整对话历史conversationbuffermemory">方案一：完整对话历史（ConversationBufferMemory）
&lt;/h4>&lt;p>&lt;strong>原理&lt;/strong>：保留所有历史对话&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationBufferMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationChain&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationBufferMemory&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 对话过程&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;你好&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;你好！我是AI助手&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我叫张三&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;你好张三！很高兴认识你&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看记忆内容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：{&amp;#39;history&amp;#39;: &amp;#39;Human: 你好\nAI: 你好！我是AI助手\nHuman: 我叫张三\nAI: 你好张三！很高兴认识你&amp;#39;}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>实现简单&lt;/li>
&lt;li>不丢失任何信息&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Token 消耗线性增长&lt;/li>
&lt;li>长对话会超出 Context Window&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>适用场景&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>短对话（&amp;lt; 20 轮）&lt;/li>
&lt;li>需要完整上下文的场景&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h4 id="方案二滑动窗口conversationbufferwindowmemory">方案二：滑动窗口（ConversationBufferWindowMemory）
&lt;/h4>&lt;p>&lt;strong>原理&lt;/strong>：只保留最近 K 轮对话&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationBufferWindowMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 只保留最近 5 轮对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationBufferWindowMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 模拟 10 轮对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;问题 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;回答 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看记忆（只保留最后 5 轮）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：最近 5 轮的对话内容&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Token 消耗可控&lt;/li>
&lt;li>实现简单&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>会丢失早期对话&lt;/li>
&lt;li>无法回溯远期信息&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>适用场景&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>中等长度对话&lt;/li>
&lt;li>更关注最近上下文的场景&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Token 计算&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">estimate_tokens_window&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">avg_tokens_per_turn&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 估算滑动窗口的 Token 消耗
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> k: 保留的对话轮数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> avg_tokens_per_turn: 平均每轮对话的 Token 数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 总 Token 数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">avg_tokens_per_turn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例：5 轮对话，平均每轮 50 tokens&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;预估 Token: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">estimate_tokens_window&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 250 tokens&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案三token-限制截断conversationtokenbuffermemory">方案三：Token 限制截断（ConversationTokenBufferMemory）
&lt;/h4>&lt;p>&lt;strong>原理&lt;/strong>：根据 Token 数量动态截断&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationTokenBufferMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 限制最大 2000 tokens&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationTokenBufferMemory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_token_limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 添加对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;长文本...&amp;#34;&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;回答...&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 自动截断超出部分&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({}))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>精确控制 Token 消耗&lt;/li>
&lt;li>最大化利用 Context Window&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>截断位置可能不自然&lt;/li>
&lt;li>需要额外的 Token 计算&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>实现细节&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseMessage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_buffer_string&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CustomTokenBufferMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;自定义 Token 限制记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_token_limit&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4000&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_token_limit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">max_token_limit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">BaseMessage&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存对话上下文&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AIMessage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AIMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检查并截断&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_trim_buffer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_trim_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;截断超出 Token 限制的部分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_count_tokens&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">current_tokens&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_token_limit&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 从最旧的对话开始删除&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">removed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_tokens&lt;/span> &lt;span class="o">-=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_count_message_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">removed&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_count_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;计算当前 Buffer 的 Token 数&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">buffer_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_buffer_string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_num_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buffer_str&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_count_message_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">BaseMessage&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;计算单条消息的 Token 数&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_num_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案四摘要记忆conversationsummarymemory">方案四：摘要记忆（ConversationSummaryMemory）
&lt;/h4>&lt;p>&lt;strong>原理&lt;/strong>：定期对历史对话生成摘要，用摘要替代原始对话&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationSummaryMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationSummaryMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 添加对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我最近在学习 Python，有什么建议吗？&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;建议从基础语法开始，然后学习数据结构和算法...&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;有没有推荐的书籍？&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;推荐《Python编程：从入门到实践》和《流畅的Python》...&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看摘要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：{&amp;#39;history&amp;#39;: &amp;#39;用户正在学习Python，我推荐了从基础语法开始学习，并建议了两本书籍...&amp;#39;}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>大幅压缩 Token 消耗&lt;/li>
&lt;li>保留关键信息&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>摘要过程需要额外的 LLM 调用（成本）&lt;/li>
&lt;li>可能丢失细节信息&lt;/li>
&lt;li>摘要质量依赖模型能力&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>成本分析&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">calculate_summary_cost&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">conversation_turns&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">avg_tokens_per_turn&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary_interval&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compression_ratio&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">float&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 计算摘要记忆的成本
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> conversation_turns: 总对话轮数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> avg_tokens_per_turn: 平均每轮对话的 Token 数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> summary_interval: 每隔多少轮生成一次摘要
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> compression_ratio: 摘要压缩比
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 成本分析字典
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 原始对话的 Token 数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">original_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">conversation_turns&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">avg_tokens_per_turn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 摘要次数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">conversation_turns&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">summary_interval&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 摘要生成成本（输入 tokens + 输出 tokens）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary_generation_cost&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">summary_count&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">summary_interval&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">avg_tokens_per_turn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 最终保留的摘要 Token 数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">final_summary_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">original_tokens&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">compression_ratio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 最近未摘要的对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">recent_unsummarized&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">conversation_turns&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">summary_interval&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">avg_tokens_per_turn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 总 Token 消耗（包括摘要生成 + 最终传递）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">total_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">summary_generation_cost&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">final_summary_tokens&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">recent_unsummarized&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;original_tokens&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">original_tokens&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;total_tokens_consumed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">total_tokens&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;compression_rate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">final_summary_tokens&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">recent_unsummarized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">original_tokens&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;summary_count&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">summary_count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 示例：100 轮对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cost&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">calculate_summary_cost&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">conversation_turns&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">avg_tokens_per_turn&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary_interval&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compression_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;压缩率: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">cost&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;compression_rate&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.1%&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 压缩率: 72.0%&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案五混合记忆conversationsummarybuffermemory">方案五：混合记忆（ConversationSummaryBufferMemory）
&lt;/h4>&lt;p>&lt;strong>原理&lt;/strong>：结合摘要和完整对话，保留最近 K 轮 + 历史摘要&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationSummaryBufferMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 保留最近 5 轮 + 历史摘要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationSummaryBufferMemory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_token_limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 添加 20 轮对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;问题 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;回答 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 查看记忆：早期对话会被摘要，最近几轮保持完整&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({}))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>平衡信息完整性和 Token 效率&lt;/li>
&lt;li>保留最近关键上下文&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>实现复杂&lt;/li>
&lt;li>需要调优参数&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>架构图&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 混合记忆结构 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [对话历史] │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 第 1-10 轮 ──→ 摘要生成 ──→ 摘要 A (200 tokens) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 第 11-15 轮 ──→ 摘要生成 ──→ 摘要 B (200 tokens) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 第 16-20 轮 ──→ 完整保留 ──→ 5 轮对话 (500 tokens)│
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ [最终传递给 LLM] │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 摘要 A + 摘要 B + 最近 5 轮 = 900 tokens │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="23-短期记忆最佳实践">2.3 短期记忆最佳实践
&lt;/h3>&lt;h4 id="实践一动态调整窗口大小">实践一：动态调整窗口大小
&lt;/h4>&lt;p>根据任务复杂度动态调整保留的对话轮数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">DynamicWindowMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;动态调整窗口大小的记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">initial_k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">initial_k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">max_k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">complexity_scores&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存上下文并评估复杂度&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 评估对话复杂度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">complexity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_estimate_complexity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">complexity_scores&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">complexity&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 动态调整窗口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_adjust_window&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_estimate_complexity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;估算对话复杂度&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简单启发式：基于文本长度和关键词&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 长度因素&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mf">0.3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 关键词因素&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">complex_keywords&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;详细&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;解释&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;为什么&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;如何&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;分析&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">complex_keywords&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mf">0.3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 多问题因素&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;?&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;？&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mf">0.4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">score&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">1.0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_adjust_window&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;根据复杂度调整窗口大小&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">complexity_scores&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算最近 5 轮的平均复杂度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">avg_complexity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">complexity_scores&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">:])&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 动态调整 k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">avg_complexity&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.6&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_k&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 增加窗口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">avg_complexity&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">max&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 减小窗口&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="实践二重要信息提取与保留">实践二：重要信息提取与保留
&lt;/h4>&lt;p>识别并优先保留重要信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.messages&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HumanMessage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AIMessage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ImportanceAwareMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;重要信息感知的记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4000&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">max_tokens&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">important_facts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存上下文并提取重要信息&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 提取重要信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">important_info&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_extract_important_info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">important_info&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">important_facts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">important_info&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 保存完整对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">inputs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputs&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检查并压缩&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_compress_if_needed&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_extract_important_info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ai_output&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;提取重要信息&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">important&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 模式 1：用户偏好&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">preference_patterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;我喜欢(.+)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;我偏好(.+)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;我希望(.+)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;我叫(.+)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;我的(.+)是&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">preference_patterns&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">matches&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">findall&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">important&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">matches&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 模式 2：关键决策&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">decision_keywords&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;决定&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;确认&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;选择&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;同意&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">user_input&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">decision_keywords&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">important&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;决策: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">important&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_compress_if_needed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;如果超出限制则压缩&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">current_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_count_tokens&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">current_tokens&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 保留重要信息 + 最近对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">recent_conversations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">:]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 构建压缩后的记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compressed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;important_facts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">important_facts&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;recent_conversations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">recent_conversations&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 替换 buffer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">compressed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">load_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;加载记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">isinstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 正常模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_format_conversations&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 压缩模式&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">facts_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">fact&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">fact&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;important_facts&amp;#34;&lt;/span>&lt;span class="p">]])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">recent_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_format_conversations&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;recent_conversations&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;【重要信息】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">facts_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">【最近对话】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">recent_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="实践三多级记忆架构">实践三：多级记忆架构
&lt;/h4>&lt;p>构建多级记忆系统，平衡效率和完整性：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MultiLevelMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;多级记忆架构&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 1: 工作记忆（当前对话）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 2: 短期记忆（滑动窗口）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_limit&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 3: 摘要记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 4: 重要信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key_facts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">add_message&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">role&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;添加消息&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 添加到工作记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">role&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 如果工作记忆达到阈值，推送到短期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_push_to_short_term&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 如果短期记忆达到阈值，生成摘要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_limit&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_summarize_short_term&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_push_to_short_term&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;推送到短期记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 提取重要信息&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_extract_key_facts&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 移动到短期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_extract_key_facts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;提取关键事实&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简化版：实际应该用 LLM 提取&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">msg&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s2">&amp;#34;我叫&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">msg&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="s2">&amp;#34;我喜欢&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">msg&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key_facts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_summarize_short_term&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;摘要短期记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 生成摘要（实际应该调用 LLM）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;过去 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 条消息的摘要...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更新摘要记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">summary&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">summary&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 清空短期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_context_for_llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取传递给 LLM 的上下文&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 关键事实（优先级最高）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key_facts&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【用户信息】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key_facts&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 历史摘要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【历史摘要】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">summary_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 短期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">short_term_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_format_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">short_term_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【近期对话】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">short_term_str&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 工作记忆（当前对话）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">working_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_format_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【当前对话】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">working_str&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context_parts&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_format_messages&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">messages&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;格式化消息&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;role&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;content&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">msg&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="三长期记忆long-term-memory">三、长期记忆（Long-term Memory）
&lt;/h2>&lt;h3 id="31-核心概念">3.1 核心概念
&lt;/h3>&lt;p>长期记忆用于存储&lt;strong>跨对话&lt;/strong>的信息，需要外部存储系统支持。&lt;/p>
&lt;p>&lt;strong>关键特性&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>持久化&lt;/strong>：数据永久存储&lt;/li>
&lt;li>&lt;strong>跨会话&lt;/strong>：不同对话可以访问相同记忆&lt;/li>
&lt;li>&lt;strong>可扩展&lt;/strong>：理论上无限容量&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>核心技术&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>向量数据库（Vector Database）&lt;/li>
&lt;li>嵌入模型（Embedding Model）&lt;/li>
&lt;li>检索算法（Retrieval Algorithm）&lt;/li>
&lt;/ul>
&lt;h3 id="32-向量数据库选型">3.2 向量数据库选型
&lt;/h3>&lt;h4 id="主流向量数据库对比">主流向量数据库对比
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>数据库&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>特点&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;th>性能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Pinecone&lt;/strong>&lt;/td>
&lt;td>云服务&lt;/td>
&lt;td>托管式，零运维&lt;/td>
&lt;td>生产环境，快速上手&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Milvus&lt;/strong>&lt;/td>
&lt;td>开源&lt;/td>
&lt;td>高性能，可扩展&lt;/td>
&lt;td>大规模生产环境&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Weaviate&lt;/strong>&lt;/td>
&lt;td>开源&lt;/td>
&lt;td>语义搜索强&lt;/td>
&lt;td>知识图谱场景&lt;/td>
&lt;td>⭐⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>ChromaDB&lt;/strong>&lt;/td>
&lt;td>开源&lt;/td>
&lt;td>轻量级，易用&lt;/td>
&lt;td>开发测试，小规模应用&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Qdrant&lt;/strong>&lt;/td>
&lt;td>开源&lt;/td>
&lt;td>Rust 实现，高性能&lt;/td>
&lt;td>性能敏感场景&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>FAISS&lt;/strong>&lt;/td>
&lt;td>库&lt;/td>
&lt;td>Meta 开源，纯算法&lt;/td>
&lt;td>本地嵌入，极致性能&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="选型决策树">选型决策树
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">需要长期记忆？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─ 是
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├─ 团队有运维能力？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├─ 是
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ ├─ 数据规模 &amp;gt; 1000万向量？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ │ ├─ 是 → Milvus
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ │ └─ 否 → Qdrant
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ └─ 需要 RAG + 知识图谱？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ ├─ 是 → Weaviate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │ └─ 否 → Qdrant
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └─ 否（无运维能力）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └─ Pinecone（托管服务）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 开发测试阶段？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ ChromaDB（最简单）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ 否（仅需短期记忆）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └─ 使用内存存储
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="33-嵌入模型选型">3.3 嵌入模型选型
&lt;/h3>&lt;h4 id="主流嵌入模型对比">主流嵌入模型对比
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>模型&lt;/th>
&lt;th>提供商&lt;/th>
&lt;th>维度&lt;/th>
&lt;th>性能&lt;/th>
&lt;th>成本&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>text-embedding-3-small&lt;/strong>&lt;/td>
&lt;td>OpenAI&lt;/td>
&lt;td>1536&lt;/td>
&lt;td>⭐⭐⭐⭐&lt;/td>
&lt;td>$0.02/1M tokens&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>text-embedding-3-large&lt;/strong>&lt;/td>
&lt;td>OpenAI&lt;/td>
&lt;td>3072&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;td>$0.13/1M tokens&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>text-embedding-ada-002&lt;/strong>&lt;/td>
&lt;td>OpenAI&lt;/td>
&lt;td>1536&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;td>$0.10/1M tokens&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>bge-large-zh-v1.5&lt;/strong>&lt;/td>
&lt;td>BGE&lt;/td>
&lt;td>1024&lt;/td>
&lt;td>⭐⭐⭐⭐&lt;/td>
&lt;td>免费（本地）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>bge-m3&lt;/strong>&lt;/td>
&lt;td>BGE&lt;/td>
&lt;td>1024&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;td>免费（本地）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cohere embed-v3&lt;/strong>&lt;/td>
&lt;td>Cohere&lt;/td>
&lt;td>1024&lt;/td>
&lt;td>⭐⭐⭐⭐⭐&lt;/td>
&lt;td>$0.10/1M tokens&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="选择建议">选择建议
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 场景 1：追求最佳性能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text-embedding-3-large&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 场景 2：成本敏感&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text-embedding-3-small&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 场景 3：中文场景，本地部署&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.embeddings&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceBgeEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceBgeEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;BAAI/bge-large-zh-v1.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cuda&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encode_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;normalize_embeddings&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 场景 4：多语言混合&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_cohere&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">CohereEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CohereEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;embed-multilingual-v3.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="34-实现方案详解">3.4 实现方案详解
&lt;/h3>&lt;h4 id="方案一基于向量检索的记忆">方案一：基于向量检索的记忆
&lt;/h4>&lt;p>&lt;strong>架构图&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 向量检索记忆架构 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 用户输入 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Embedding 模型 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 查询向量 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 向量数据库 ──→ 相似度检索 ──→ Top-K 记忆 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 相关记忆 + 用户输入 ──→ LLM ──→ 回复 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 回复 + 输入 ──→ Embedding ──→ 存入向量数据库 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>实现代码&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">VectorStoreRetrieverMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationChain&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 初始化向量数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding_function&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./chroma_memory_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 创建检索器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># 检索最相关的 5 条记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 创建记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">VectorStoreRetrieverMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 使用记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 保存对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我喜欢吃苹果&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;好的，我会记住你喜欢吃苹果&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_context&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我叫张三&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;你好张三！很高兴认识你&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. 检索相关记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">relevant_memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load_memory_variables&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;我的名字是什么？&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relevant_memories&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：最相关的记忆，包含&amp;#34;我叫张三&amp;#34;的对话&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>高级实现：带元数据的记忆&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Optional&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.documents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Document&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AdvancedVectorMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;高级向量记忆系统&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">user_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_memory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ai_output&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;normal&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># high, normal, low&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">category&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 构建文档&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">document&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Document&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page_content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;User: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">AI: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ai_output&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">importance&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;category&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">category&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="s2">&amp;#34;general&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;conversation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 添加到向量数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_documents&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">document&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_fact&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fact&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">category&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;fact&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存事实性知识&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">document&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Document&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">page_content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">fact&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;fact&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;category&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">category&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_documents&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">document&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve_relevant&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filters&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;检索相关记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 基础过滤条件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_filter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 合并额外过滤条件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">filters&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base_filter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">update&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filters&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">base_filter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve_by_time_range&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">end_time&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;按时间范围检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 注意：不同向量数据库的过滤语法不同&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 这里以 Chroma 为例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 空查询&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;$gte&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">start_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;$lte&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">end_time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_user_preferences&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取用户偏好&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_relevant&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;用户偏好&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filters&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;category&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;preference&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_important_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取重要记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_relevant&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filters&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;high&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案二混合检索记忆">方案二：混合检索记忆
&lt;/h4>&lt;p>结合向量检索和关键词检索，提高召回率：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.retrievers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">EnsembleRetriever&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.retrievers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BM25Retriever&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">HybridMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;混合检索记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">documents&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 向量检索器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vector_retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 关键词检索器（BM25）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">keyword_retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BM25Retriever&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">keyword_retriever&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 混合检索器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ensemble_retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">EnsembleRetriever&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retrievers&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vector_retriever&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">keyword_retriever&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">weights&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mf">0.6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 向量检索权重更高&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;混合检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ensemble_retriever&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>混合检索的优势&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">查询：&amp;#34;Python 异常处理&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 向量检索结果 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 1. 如何捕获 Python 异常？ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 2. try-except 语句的使用 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 3. Python 错误处理最佳实践 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 关键词检索结果 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 1. Python 异常类型列表 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 2. 异常处理的代码示例 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 3. raise 语句的用法 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 混合检索结果 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 去重 + 加权排序 → 更全面的记忆召回 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案三知识图谱增强记忆">方案三：知识图谱增强记忆
&lt;/h4>&lt;p>将记忆组织成知识图谱，支持复杂推理：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.graphs&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Neo4jGraph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">GraphQAChain&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">KnowledgeGraphMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;知识图谱记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">neo4j_url&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">neo4j_user&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">neo4j_password&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Neo4jGraph&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">neo4j_url&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">username&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">neo4j_user&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">password&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">neo4j_password&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_entity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">entity_type&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">entity_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">properties&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存实体&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">props_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;, &amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">properties&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">()])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MERGE (e:&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">entity_type&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="s2">name: $name&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> SET e += &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">props_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">entity_name&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_relation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">from_entity&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">to_entity&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存关系&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MATCH (a &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="s2">name: $from_name&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MATCH (b &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="s2">name: $to_name&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MERGE (a)-[r:&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">]-&amp;gt;(b)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;from_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">from_entity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;to_name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">to_entity&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_conversation_memory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entities&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relations&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">conversation_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存对话记忆到知识图谱&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 保存实体&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">entity&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">entities&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_entity&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">entity&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">entity&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">properties&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">entity&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;properties&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 保存关系&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">relation&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">relations&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_relation&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">from_entity&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;from&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relation&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">to_entity&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;to&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 关联到对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MATCH (u:User {id: $user_id})
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MATCH (c:Conversation {id: $conv_id})
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MERGE (u)-[:HAD]-&amp;gt;(c)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;conv_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">conversation_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">query_related_knowledge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">entity_name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">depth&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;查询相关知识&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> MATCH path = (e &lt;/span>&lt;span class="se">{{&lt;/span>&lt;span class="s2">name: $name&lt;/span>&lt;span class="se">}}&lt;/span>&lt;span class="s2">)-[*1..&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">depth&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">]-(related)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> RETURN path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">entity_name&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>知识图谱记忆示例&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">用户说：&amp;#34;我和李四在讨论 Python 项目&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">知识图谱存储：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ (User:张三) ──[:DISCUSSED_WITH]──→ (User:李四) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └──[:DISCUSSED_ABOUT]──→ (Topic:Python) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └──[:RELATED_TO] │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ↓ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ (Project:XXX) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">下次用户问：&amp;#34;上次我和谁讨论 Python？&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">→ 查询图谱 → 返回&amp;#34;李四&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="方案四层级化长期记忆">方案四：层级化长期记忆
&lt;/h4>&lt;p>模拟人类记忆的层级结构：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">HierarchicalLongTermMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;层级化长期记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 1: 工作记忆（当前会话）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 2: 情景记忆（Episodic Memory）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 存储具体事件和经历&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">episodic_store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span> &lt;span class="c1"># {session_id: [events]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 3: 语义记忆（Semantic Memory）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 存储事实和知识&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">semantic_store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span> &lt;span class="c1"># {concept: facts}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Level 4: 程序记忆（Procedural Memory）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 存储技能和规则&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">procedural_store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span> &lt;span class="c1"># {skill: rules}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">add_to_working_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;添加到工作记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">consolidate_to_episodic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">session_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;将工作记忆巩固到情景记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">session_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">episodic_store&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">episodic_store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">episodic_store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">extract_to_semantic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">concept&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">facts&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;提取事实到语义记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">concept&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">semantic_store&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">semantic_store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">concept&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">semantic_store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">concept&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">facts&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">learn_procedure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">skill&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rules&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;学习程序性知识&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">procedural_store&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">skill&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rules&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;检索记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;episodic&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_search_episodic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;semantic&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_search_semantic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;procedural&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_search_procedural&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_search_episodic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;搜索情景记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 实际应该用向量检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">session_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">events&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">episodic_store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">event&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">events&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">event&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_search_semantic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;搜索语义记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">concept&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">facts&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">semantic_store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">concept&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">facts&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_search_procedural&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;搜索程序记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 根据上下文匹配相关技能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">skill&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rules&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">procedural_store&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_is_skill_relevant&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">skill&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rules&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_is_skill_relevant&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">skill&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;判断技能是否相关&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简化实现&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">skill&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>记忆层级示意&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 记忆层级结构 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─────────────────────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Level 1: 工作记忆（秒级） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 当前对话中的最近几轮 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Level 2: 情景记忆（小时-天） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 具体事件：&amp;#34;昨天讨论了 Python 项目&amp;#34; │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Level 3: 语义记忆（永久） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 事实知识：&amp;#34;用户喜欢 Python&amp;#34;、&amp;#34;用户叫张三&amp;#34; │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ Level 4: 程序记忆（永久） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └─ 技能规则：&amp;#34;遇到代码问题 → 先检查语法错误&amp;#34; │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="35-长期记忆的最佳实践">3.5 长期记忆的最佳实践
&lt;/h3>&lt;h4 id="实践一记忆去重与更新">实践一：记忆去重与更新
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">DeduplicatedMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;去重记忆系统&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">similarity_threshold&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">float&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.95&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_threshold&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">similarity_threshold&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存记忆（带去重）&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检查是否已存在相似记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">similar_docs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search_with_score&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">similar_docs&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">similar_docs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 相似度高于阈值，认为是重复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">score&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_threshold&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更新元数据（如时间戳）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_update_metadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">False&lt;/span> &lt;span class="c1"># 未添加新记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 添加新记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_texts&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">metadatas&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">True&lt;/span> &lt;span class="c1"># 添加成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_update_metadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">doc_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;更新元数据&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 实现取决于具体的向量数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="实践二记忆遗忘机制">实践二：记忆遗忘机制
&lt;/h4>&lt;p>模拟人类遗忘，避免存储过多无用信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timedelta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">numpy&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">np&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ForgettingMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;带遗忘机制的记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">half_life_days&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">30&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">half_life_days&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">half_life_days&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">calculate_retention_score&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timestamp&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">float&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 计算记忆保留分数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 艾宾浩斯遗忘曲线变体：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> R = importance * access_count * e^(-t/τ)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 时间衰减&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">days_passed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">timestamp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">days&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">time_decay&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">days_passed&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">half_life_days&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 访问增强&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_boost&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">log1p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">access_count&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 最终分数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">importance&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">access_boost&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">time_decay&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">score&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">should_forget&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">memory_metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">bool&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;判断是否应该遗忘&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">calculate_retention_score&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timestamp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fromisoformat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory_metadata&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">memory_metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;access_count&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">memory_metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">1.0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">score&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mf">0.1&lt;/span> &lt;span class="c1"># 阈值&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">cleanup_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;清理低价值记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索用户所有记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">all_memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 识别需要遗忘的记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">to_forget&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">memory&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">all_memories&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">should_forget&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">to_forget&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 删除&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">to_forget&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to_forget&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;已清理 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to_forget&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 条记忆&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h4 id="实践三记忆重要性评估">实践三：记忆重要性评估
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ImportanceScorer&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;记忆重要性评分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance_keywords&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;high&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;重要&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;紧急&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;必须&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;关键&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;决定&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;偏好&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;喜欢&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;讨厌&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;习惯&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;medium&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;需要&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;想要&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;希望&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;建议&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;想法&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;low&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;随便&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;无所谓&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;可能&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;或许&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">score_importance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ai_output&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;评估记忆重要性&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 关键词匹配&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_keyword_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 情感强度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_sentiment_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 信息熵（信息量）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_information_entropy_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 交互深度（追问、确认等）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">score&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_interaction_depth_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">max&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">score&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.0&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mf">1.0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 归一化到 [0, 1]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_keyword_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;关键词评分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">level&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">keywords&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance_keywords&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">kw&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">keywords&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">level&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;high&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">level&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;medium&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.05&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_sentiment_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;情感强度评分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简化实现，实际可用情感分析模型&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">strong_emotions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;非常&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;特别&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;极其&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;超级&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.2&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nb">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">e&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">strong_emotions&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="mf">0.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_information_entropy_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;信息熵评分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 基于文本长度和信息密度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">word_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">word_count&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.05&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">word_count&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mf">0.2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_interaction_depth_score&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;交互深度评分&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 多问题、追问等表示重要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">question_marks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;?&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;？&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">question_marks&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">0.1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="四记忆系统集成实践">四、记忆系统集成实践
&lt;/h2>&lt;h3 id="41-完整记忆系统架构">4.1 完整记忆系统架构
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Dict&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datetime&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dataclasses&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">dataclass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">enum&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Enum&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MemoryType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Enum&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;记忆类型&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">WORKING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;working&amp;#34;&lt;/span> &lt;span class="c1"># 工作记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SHORT_TERM&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;short_term&amp;#34;&lt;/span> &lt;span class="c1"># 短期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LONG_TERM&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;long_term&amp;#34;&lt;/span> &lt;span class="c1"># 长期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">EPISODIC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;episodic&amp;#34;&lt;/span> &lt;span class="c1"># 情景记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SEMANTIC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;semantic&amp;#34;&lt;/span> &lt;span class="c1"># 语义记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@dataclass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MemoryEntry&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;记忆条目&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_type&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">MemoryType&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timestamp&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">float&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_count&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">UnifiedMemorySystem&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;统一记忆系统&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">user_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">config&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;working_memory_limit&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;short_term_limit&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;long_term_retrieval_k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;importance_threshold&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;consolidation_interval&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 记忆存储&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance_scorer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ImportanceScorer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">forgetting_system&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ForgettingMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">add_memory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ai_output&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">force_long_term&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;添加记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 评估重要性&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance_scorer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">score_importance&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ai_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 创建记忆条目&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entry&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_generate_id&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;User: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">AI: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ai_output&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">WORKING&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timestamp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">importance&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;user_input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ai_output&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">ai_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 添加到工作记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检查是否需要巩固到长期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;working_memory_limit&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;importance_threshold&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">or&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">force_long_term&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_consolidate_memories&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve_memories&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_types&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Optional&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryType&lt;/span>&lt;span class="p">]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;检索记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_types&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory_types&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">WORKING&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SHORT_TERM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">LONG_TERM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 从工作记忆检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">WORKING&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memory_types&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 从长期记忆检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">LONG_TERM&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memory_types&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">long_term_results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_retrieve_long_term&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">long_term_results&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 排序（按重要性和时间）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sort&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">lambda&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">timestamp&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">reverse&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 更新访问计数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">entry&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">access_count&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;long_term_retrieval_k&amp;#34;&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_consolidate_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;巩固记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 批量存储到向量数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">contents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">entry&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadatas&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">**&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">timestamp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;memory_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">entry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_type&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">entry&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_texts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">contents&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadatas&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">metadatas&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 清空工作记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_retrieve_long_term&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;从长期记忆检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">docs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;long_term_retrieval_k&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entries&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">docs&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entry&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">LONG_TERM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">timestamp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fromisoformat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;timestamp&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;access_count&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entries&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">entry&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">entries&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_context_for_llm&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取传递给 LLM 的上下文&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索相关记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 构建上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 重要事实&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">important_memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memories&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">importance&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">important_memories&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">facts_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;user_input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">important_memories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;【重要信息】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">facts_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 相关对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relevant_str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memories&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context_parts&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;【相关历史】&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">relevant_str&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context_parts&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Token 检查&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">token_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_count_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">token_count&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_truncate_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">context&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_generate_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;生成唯一 ID&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">uuid&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">uuid&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">uuid4&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_count_tokens&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;计算 Token 数&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简化实现&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">1.5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_truncate_context&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;截断上下文&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 简化实现&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">words&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">words&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_tokens&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="p">)])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">cleanup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;清理低价值记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">forgetting_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleanup_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="42-与-langchain-集成">4.2 与 LangChain 集成
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationChain&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化记忆系统&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory_system&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">Chroma&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding_function&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./memory_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;user_123&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 自定义 Prompt 模板&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">你是一个智能助手，拥有对用户的长期记忆。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{memory_context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">当前对话：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{input}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请基于记忆和当前输入回复：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;memory_context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">template&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 对话函数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">chat_with_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;带记忆的对话&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 检索相关记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_context_for_llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 生成回复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">format&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">memory_context&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">user_input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ai_output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt_text&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 保存记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ai_output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ai_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用示例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chat_with_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;我叫张三，我喜欢 Python&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：你好张三！很高兴认识你。Python 是一门很棒的语言...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ... 开启新对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chat_with_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;你还记得我的名字吗？&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出：当然记得！你叫张三。而且我知道你喜欢 Python...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="43-记忆系统评估">4.3 记忆系统评估
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MemoryEvaluator&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;记忆系统评估器&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">memory_system&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory_system&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">evaluate_retrieval_quality&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;评估检索质量&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;precision&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;recall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;mrr&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[]&lt;/span> &lt;span class="c1"># Mean Reciprocal Rank&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="k">case&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">case&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">expected_ids&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;relevant_memory_ids&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retrieved&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retrieved_ids&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">set&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">retrieved&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算指标&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">retrieved_ids&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">precision&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retrieved_ids&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">expected_ids&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retrieved_ids&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;precision&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">precision&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">expected_ids&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">recall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retrieved_ids&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">expected_ids&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">expected_ids&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;recall&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">recall&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># MRR&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">memory&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">enumerate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retrieved&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">expected_ids&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mrr&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.0&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mrr&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 平均值&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;avg_precision&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;precision&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;avg_recall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;recall&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;avg_mrr&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">results&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;mrr&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">evaluate_memory_usage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;评估记忆使用情况&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 统计各类记忆数量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">all_memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 按重要性分布&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance_dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;high&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;medium&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;low&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">memory&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">all_memories&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;importance&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">importance&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.7&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance_dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;high&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">importance&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance_dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;medium&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">importance_dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;low&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Token 消耗估算&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">total_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mf">1.5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">all_memories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;total_memories&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">all_memories&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;importance_distribution&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">importance_dist&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;estimated_tokens&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">total_tokens&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;working_memory_size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">working_memory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">benchmark_retrieval_speed&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">queries&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;基准测试检索速度&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">latencies&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">query&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">queries&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_memories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">latency&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">latencies&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;avg_latency_ms&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">mean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latencies&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;p50_latency_ms&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">percentile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latencies&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;p95_latency_ms&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">percentile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latencies&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">95&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;p99_latency_ms&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">np&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">percentile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latencies&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">99&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="五高级主题">五、高级主题
&lt;/h2>&lt;h3 id="51-多用户记忆隔离">5.1 多用户记忆隔离
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MultiUserMemorySystem&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;多用户记忆系统&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 用户记忆系统缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_memories&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Dict&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_user_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取用户的记忆系统&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">user_id&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_memories&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_memories&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">user_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">user_memories&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">chat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;用户对话&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_user_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 获取上下文&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_context_for_llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 生成回复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">用户：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">助手：&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 保存记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">share_memory_between_users&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">from_user&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">to_user&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_ids&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;在用户间共享记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索源用户的记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">from_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_user_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">from_user&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 复制到目标用户&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">to_memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_user_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">to_user&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">memory_id&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memory_ids&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 从向量数据库检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 然后添加到目标用户&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="52-跨模态记忆">5.2 跨模态记忆
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MultiModalMemory&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;多模态记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text_embeddings&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">image_embeddings&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text_embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">image_embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">image_embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_text_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存文本记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text_embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_embeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embedding&lt;/span>&lt;span class="p">)],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadatas&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;modality&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_image_memory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">image_path&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;保存图像记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 提取图像特征&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">image_embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_image&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">image_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_embeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[(&lt;/span>&lt;span class="n">description&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">embedding&lt;/span>&lt;span class="p">)],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadatas&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">**&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;modality&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;image&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;image_path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">image_path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve_multimodal&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">modalities&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;image&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;多模态检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 文本查询嵌入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query_embedding&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text_embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search_by_vector&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query_embedding&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">filter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;modality&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;$in&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">modalities&lt;/span>&lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="53-记忆压缩与总结">5.3 记忆压缩与总结
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MemoryCompressor&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;记忆压缩器&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">compress_memories&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memories&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">strategy&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;summary&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;压缩记忆&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">strategy&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;summary&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_summarize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memories&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">elif&lt;/span> &lt;span class="n">strategy&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;extract&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_extract_key_points&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">memories&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">ValueError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Unknown strategy: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">strategy&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_summarize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">memories&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;生成摘要&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 合并记忆内容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">combined_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 使用 LLM 生成摘要&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请将以下对话历史压缩成简洁的摘要，保留关键信息：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">combined_text&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">摘要：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">summary&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_extract_key_points&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">memories&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryEntry&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;提取关键点&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">combined_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">从以下对话中提取关键事实和信息点：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">combined_text&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">关键信息：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">-
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key_points&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">key_points&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="六性能优化">六、性能优化
&lt;/h2>&lt;h3 id="61-检索优化">6.1 检索优化
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">OptimizedMemoryRetriever&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;优化的记忆检索器&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cache_size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">embeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 查询缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cache_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cache_size&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">retrieve_with_cache&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_cache&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">bool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;带缓存的检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检查缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cache_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_get_cache_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">use_cache&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">cache_key&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">cache_key&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 执行检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更新缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">use_cache&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_update_cache&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cache_key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_get_cache_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;生成缓存键&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">hashlib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">hashlib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">md5&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">_&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hexdigest&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_update_cache&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;更新缓存&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># LRU 淘汰&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cache_size&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 删除最旧的&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">oldest_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">iter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">oldest_key&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">query_cache&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">batch_retrieve&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">queries&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;批量检索&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 批量嵌入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query_embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">embed_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">queries&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 批量检索&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">embedding&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">query_embeddings&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">docs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity_search_by_vector&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">k&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">docs&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">results&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="62-存储优化">6.2 存储优化
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">OptimizedMemoryStorage&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;优化的记忆存储&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">compression_threshold&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compression_threshold&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">compression_threshold&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">save_with_compression&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">contents&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">metadatas&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">dict&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;带压缩的存储&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 如果内容过长，先压缩&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compressed_contents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">content&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">contents&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compression_threshold&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_compress_content&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compressed_contents&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 存储&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_texts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">compressed_contents&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadatas&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">metadatas&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_compress_content&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;压缩内容&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 移除多余空白&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;\s+&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 截断&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compression_threshold&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compression_threshold&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">optimize_storage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;优化存储&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 合并相似记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 删除过期记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 更新索引&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="七实战案例">七、实战案例
&lt;/h2>&lt;h3 id="案例个性化学习助手记忆系统">案例：个性化学习助手记忆系统
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">PersonalizedLearningAssistant&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;个性化学习助手&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="fm">__init__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user_id&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 初始化记忆系统&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">UnifiedMemorySystem&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">Chroma&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding_function&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;./learning_assistant/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">OpenAIEmbeddings&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">user_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">user_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 学习档案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;knowledge_level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span> &lt;span class="c1"># 各知识点的掌握程度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;learning_style&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 学习风格&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;goals&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span> &lt;span class="c1"># 学习目标&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;weaknesses&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[]&lt;/span> &lt;span class="c1"># 薄弱环节&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">learn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">topic&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;学习过程&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 检索相关记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_context_for_llm&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;关于 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 的知识&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 生成学习内容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">用户正在学习 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">历史学习记录：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">当前学习内容：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">1. 讲解这个知识点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">2. 关联之前的学习内容
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">3. 提供练习题
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 保存记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_memory&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;学习 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">force_long_term&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 更新学习档案&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_update_profile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">review&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">topic&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;复习过程&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 检索该主题的所有相关记忆&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memories&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">retrieve_memories&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 知识点&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memory_types&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MemoryType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">LONG_TERM&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 生成复习内容&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">memories&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">用户要复习 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">topic&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">学习历史：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请生成一份复习总结，包括：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">1. 核心概念回顾
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">2. 重点难点梳理
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">3. 常见错误提醒
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">4. 巩固练习
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">_update_profile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">topic&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;更新学习档案&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 使用 LLM 提取知识点和掌握程度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extraction_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">分析以下学习内容，提取：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">1. 知识点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">2. 用户掌握程度（1-5）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">3. 学习难点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">学习内容：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">AI 回复：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">以 JSON 格式返回。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extraction&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extraction_prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extraction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更新知识点掌握程度&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">kp&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;knowledge_points&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[]):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;knowledge_level&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">kp&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">kp&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;last_review&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">datetime&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isoformat&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 更新薄弱环节&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">difficulties&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;difficulties&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;weaknesses&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">extend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">difficulties&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;weaknesses&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;weaknesses&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;更新档案失败: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">get_personalized_recommendation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;获取个性化学习推荐&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 基于记忆和学习档案生成推荐&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">基于用户的学习档案：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learning_profile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">indent&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请推荐：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">1. 下一步应该学习什么
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">2. 需要复习哪些内容
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">3. 如何改进学习方法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">memory_system&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用示例&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">assistant&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PersonalizedLearningAssistant&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;learner_123&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 学习&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assistant&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Python 基础&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;变量和数据类型&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 继续学习&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assistant&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">learn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;Python 基础&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;条件和循环&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 复习&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">review&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assistant&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">review&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Python 基础&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">review&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 获取推荐&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">recommendation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">assistant&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_personalized_recommendation&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">recommendation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="八总结与最佳实践清单">八、总结与最佳实践清单
&lt;/h2>&lt;h3 id="记忆系统设计原则">记忆系统设计原则
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>分层原则&lt;/strong>：工作记忆 → 短期记忆 → 长期记忆&lt;/li>
&lt;li>&lt;strong>重要性优先&lt;/strong>：重要信息优先存储和检索&lt;/li>
&lt;li>&lt;strong>遗忘机制&lt;/strong>：定期清理低价值记忆&lt;/li>
&lt;li>&lt;strong>上下文相关&lt;/strong>：检索与当前上下文相关的记忆&lt;/li>
&lt;li>&lt;strong>用户隔离&lt;/strong>：多用户场景下的记忆隔离&lt;/li>
&lt;/ol>
&lt;h3 id="最佳实践清单">最佳实践清单
&lt;/h3>&lt;h4 id="短期记忆">短期记忆
&lt;/h4>&lt;ul>
&lt;li>✅ 根据场景选择合适的记忆类型（滑动窗口 vs 摘要）&lt;/li>
&lt;li>✅ 监控 Token 消耗，避免超出 Context Window&lt;/li>
&lt;li>✅ 提取并优先保留重要信息&lt;/li>
&lt;li>✅ 考虑混合记忆策略（摘要 + 完整对话）&lt;/li>
&lt;/ul>
&lt;h4 id="长期记忆">长期记忆
&lt;/h4>&lt;ul>
&lt;li>✅ 选择合适的向量数据库（考虑规模、性能、成本）&lt;/li>
&lt;li>✅ 选择合适的嵌入模型（考虑语言、性能、成本）&lt;/li>
&lt;li>✅ 实现记忆去重机制&lt;/li>
&lt;li>✅ 实现遗忘机制，定期清理&lt;/li>
&lt;li>✅ 为记忆添加丰富的元数据&lt;/li>
&lt;li>✅ 考虑混合检索（向量 + 关键词）&lt;/li>
&lt;/ul>
&lt;h4 id="性能优化">性能优化
&lt;/h4>&lt;ul>
&lt;li>✅ 实现查询缓存&lt;/li>
&lt;li>✅ 批量操作优化&lt;/li>
&lt;li>✅ 内容压缩&lt;/li>
&lt;li>✅ 异步处理&lt;/li>
&lt;/ul>
&lt;h4 id="评估与监控">评估与监控
&lt;/h4>&lt;ul>
&lt;li>✅ 建立检索质量评估体系&lt;/li>
&lt;li>✅ 监控记忆使用情况&lt;/li>
&lt;li>✅ 基准测试检索速度&lt;/li>
&lt;li>✅ 收集用户反馈&lt;/li>
&lt;/ul>
&lt;h3 id="常见问题与解决方案">常见问题与解决方案
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>问题&lt;/th>
&lt;th>解决方案&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Token 超限&lt;/td>
&lt;td>滑动窗口 + 摘要记忆&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>检索不准确&lt;/td>
&lt;td>混合检索 + 元数据过滤&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>记忆重复&lt;/td>
&lt;td>去重机制 + 相似度阈值&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>检索慢&lt;/td>
&lt;td>缓存 + 索引优化&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>存储爆炸&lt;/td>
&lt;td>遗忘机制 + 压缩&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>跨用户泄露&lt;/td>
&lt;td>用户 ID 过滤 + 权限控制&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="参考资料">参考资料
&lt;/h2>&lt;h3 id="论文">论文
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/2310.08560" target="_blank" rel="noopener"
>MemGPT: Towards LLMs as Operating Systems&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/2304.03442" target="_blank" rel="noopener"
>Generative Agents: Interactive Simulacra of Human Behavior&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/2305.10250" target="_blank" rel="noopener"
>MemoryBank: Enhancing Large Language Models with Long-Term Memory&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="开源项目">开源项目
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://python.langchain.com/docs/modules/memory/" target="_blank" rel="noopener"
>LangChain Memory&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/cpacker/memgpt" target="_blank" rel="noopener"
>MemGPT&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/letta-ai/letta" target="_blank" rel="noopener"
>Letta&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="向量数据库">向量数据库
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.pinecone.io/" target="_blank" rel="noopener"
>Pinecone&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://milvus.io/" target="_blank" rel="noopener"
>Milvus&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.trychroma.com/" target="_blank" rel="noopener"
>ChromaDB&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://qdrant.tech/" target="_blank" rel="noopener"
>Qdrant&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="教程">教程
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://python.langchain.com/docs/modules/memory/" target="_blank" rel="noopener"
>LangChain Memory 官方文档&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/erikbern/ann-benchmarks" target="_blank" rel="noopener"
>Vector Database 对比&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://huggingface.co/spaces/mteb/leaderboard" target="_blank" rel="noopener"
>Embedding Models 排行榜&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>智能体编排设计工程师学习指南</title><link>https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/</link><pubDate>Tue, 17 Jun 2025 11:00:00 +0800</pubDate><guid>https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/</guid><description>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/index/index.png" alt="Featured image of post 智能体编排设计工程师学习指南" />&lt;h2 id="什么是智能体编排设计工程师">什么是智能体编排设计工程师？
&lt;/h2>&lt;p>&lt;strong>智能体编排设计工程师&lt;/strong>是一个随着大模型（LLM）技术落地而新兴的热门岗位。它介于算法工程师、全栈开发工程师和产品经理之间，核心目标是&lt;strong>让多个 AI 智能体协同工作，解决复杂问题&lt;/strong>。&lt;/p>
&lt;p>可以把它看作是 &lt;strong>AI 时代的&amp;quot;系统架构师&amp;quot;&lt;/strong>。&lt;/p>
&lt;h3 id="为什么这个岗位会出现">为什么这个岗位会出现？
&lt;/h3>&lt;p>传统软件开发中，系统架构师负责设计模块划分、接口定义、通信协议。当系统从&amp;quot;人写代码&amp;quot;变成&amp;quot;AI 写代码 + AI 执行任务&amp;quot;时，架构师的角色自然演化为：&lt;/p>
&lt;ul>
&lt;li>不再设计模块间调用，而是设计 &lt;strong>Agent 间协作&lt;/strong>&lt;/li>
&lt;li>不再定义 API 接口，而是设计 &lt;strong>Prompt 接口&lt;/strong>&lt;/li>
&lt;li>不再关注性能瓶颈，而是关注 &lt;strong>Token 成本和推理延迟&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>2023 年以前，做 AI 应用的人主要是&amp;quot;调 API&amp;quot;——把用户输入发给模型，拿到输出返回。但随着任务复杂度提升，单个模型无法完成长流程任务，&lt;strong>多 Agent 编排&lt;/strong>成为刚需。&lt;/p>
&lt;hr>
&lt;h2 id="agent-的内部结构">Agent 的内部结构
&lt;/h2>&lt;p>理解编排之前，先要理解单个 Agent 的内部结构。一个标准 Agent 包含四个核心模块：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/agent-internal-structure.svg"
loading="lazy"
alt="Agent 内部结构"
>&lt;/p>
&lt;h3 id="1-记忆memory">1. 记忆（Memory）
&lt;/h3>&lt;p>记忆是 Agent 最核心的模块之一，决定了 Agent 能否&amp;quot;记住&amp;quot;用户、保持上下文连续性、从历史中学习。记忆系统分为两大类：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>实现方式&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>短期记忆&lt;/strong>&lt;/td>
&lt;td>当前对话上下文，存储在 LLM 的 Context Window 中&lt;/td>
&lt;td>滑动窗口、摘要压缩、Token 限制截断&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>长期记忆&lt;/strong>&lt;/td>
&lt;td>跨对话的知识存储，需要外部数据库&lt;/td>
&lt;td>向量数据库（Milvus、Pinecone、ChromaDB）或 关系数据库&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>短期记忆的核心挑战&lt;/strong>：LLM 有 Token 限制（GPT-4 约 128K），长对话会超出限制。LangChain 提供了多种短期记忆策略：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 策略 1：滑动窗口 — 只保留最近 K 轮&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationBufferWindowMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationBufferWindowMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 策略 2：Token 限制截断 — 精确控制 Token 消耗&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationTokenBufferMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationTokenBufferMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_token_limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 策略 3：摘要压缩 — 用摘要替代原始对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationSummaryMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationSummaryMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 策略 4：混合策略 — 摘要 + 最近完整对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ConversationSummaryBufferMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ConversationSummaryBufferMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_token_limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>长期记忆的实现&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 向量数据库存储对话历史&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.memory&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">VectorStoreRetrieverMemory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChromaDB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChromaDB&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">embedding_function&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">memory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">VectorStoreRetrieverMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>📖 &lt;strong>记忆模块的完整技术文档&lt;/strong>（包括多级记忆架构、向量数据库选型、嵌入模型对比、遗忘机制、知识图谱增强记忆、性能优化等）请参考：&lt;a class="link" href="https://www.zata.cc/p/agent-%E8%AE%B0%E5%BF%86%E6%A8%A1%E5%9D%97%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/" >记忆模块技术文档&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h3 id="2-规划planning">2. 规划（Planning）
&lt;/h3>&lt;p>规划是 Agent 的&amp;quot;大脑&amp;quot;，决定下一步做什么。主流方法：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>方法&lt;/th>
&lt;th>原理&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>CoT（思维链）&lt;/strong>&lt;/td>
&lt;td>让模型逐步推理，输出中间步骤&lt;/td>
&lt;td>数学、逻辑推理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>ToT（思维树）&lt;/strong>&lt;/td>
&lt;td>多路径探索，选择最优分支&lt;/td>
&lt;td>复杂决策问题&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>ReAct&lt;/strong>&lt;/td>
&lt;td>Reasoning + Acting，推理与行动交替&lt;/td>
&lt;td>需要调用工具的任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Plan-and-Execute&lt;/strong>&lt;/td>
&lt;td>先生成完整计划，再逐步执行&lt;/td>
&lt;td>长流程任务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>ReAct 模式示例&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">用户：北京今天天气怎么样？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 内部过程：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thought: 用户想知道北京的天气，我需要调用天气 API
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Action: call_weather_api(city=&amp;#34;北京&amp;#34;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Observation: 北京今天晴，气温 25°C，湿度 40%
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thought: 已经获取天气信息，可以回答用户了
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Answer: 北京今天晴天，气温 25 度，湿度较低，适合外出活动。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-工具tools">3. 工具（Tools）
&lt;/h3>&lt;p>工具是 Agent 的&amp;quot;手&amp;quot;，让它能执行实际操作。常见工具类型：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>工具类型&lt;/th>
&lt;th>示例&lt;/th>
&lt;th>用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>搜索类&lt;/strong>&lt;/td>
&lt;td>Google Search、Wikipedia API&lt;/td>
&lt;td>获取外部信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>计算类&lt;/strong>&lt;/td>
&lt;td>Python REPL、Calculator&lt;/td>
&lt;td>数学计算&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>数据类&lt;/strong>&lt;/td>
&lt;td>SQL Database、Vector Store&lt;/td>
&lt;td>查询数据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>操作类&lt;/strong>&lt;/td>
&lt;td>HTTP API、文件操作&lt;/td>
&lt;td>执行动作&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Function Calling 实现&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.tools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">search_tool&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Tool&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;web_search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;搜索互联网获取信息，输入搜索关键词&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">func&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">search_function&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Agent 使用工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.agents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AgentExecutor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AgentExecutor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_agent_and_tools&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm_agent&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">search_tool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">calculator_tool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">weather_tool&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-行动action">4. 行动（Action）
&lt;/h3>&lt;p>行动是 Agent 的最终输出。两种模式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>响应式&lt;/strong>：回答用户问题，不改变外部状态&lt;/li>
&lt;li>&lt;strong>执行式&lt;/strong>：调用 API、写入数据库、发送邮件等，改变外部状态&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="多智能体编排模式">多智能体编排模式
&lt;/h2>&lt;p>单个 Agent 能力有限，复杂任务需要多个 Agent 协作。主流编排模式：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/orchestration-patterns.svg"
loading="lazy"
alt="三种编排模式对比"
>&lt;/p>
&lt;h3 id="模式一链式编排chain">模式一：链式编排（Chain）
&lt;/h3>&lt;p>任务按顺序传递，每个 Agent 处理一个步骤。&lt;/p>
&lt;p>&lt;strong>适用场景&lt;/strong>：流水线任务，如&amp;quot;搜索 → 总结 → 翻译&amp;quot;&lt;/p>
&lt;p>&lt;strong>LangGraph 实现&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StateGraph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ChainState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TypedDict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">input&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">search_result&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">translation&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 构建 DAG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ChainState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">search_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;summarize&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">summarize_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;translate&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">translate_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;summarize&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;summarize&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;translate&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_finish_point&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;translate&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>缺点&lt;/strong>：无法并行，延迟累加。&lt;/p>
&lt;h3 id="模式二层级编排hierarchical">模式二：层级编排（Hierarchical）
&lt;/h3>&lt;p>一个&amp;quot;管理者 Agent&amp;quot;拆解任务，分配给&amp;quot;工作者 Agent&amp;quot;，汇总结果。&lt;/p>
&lt;p>&lt;strong>适用场景&lt;/strong>：复杂项目，如&amp;quot;写代码 + 测试 + 部署&amp;quot;全流程&lt;/p>
&lt;p>&lt;strong>AutoGen 实现&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">autogen&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">UserProxyAgent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 管理者&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">manager&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Manager&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">system_message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;你是项目经理，负责拆解任务并分配给合适的工程师&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 工作者&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">coder&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Coder&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">system_message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;你负责写代码&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tester&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Tester&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">system_message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;你负责测试&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">deployer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Deployer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">system_message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;你负责部署&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 组建团队&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">groupchat&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">GroupChat&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">manager&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">coder&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tester&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">deployer&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>优点&lt;/strong>：并行执行，效率高；职责清晰，便于调试。&lt;/p>
&lt;h3 id="模式三网状编排mesh">模式三：网状编排（Mesh）
&lt;/h3>&lt;p>所有 Agent 地位平等，可以自由通信。适合创意性、探索性任务。&lt;/p>
&lt;p>&lt;strong>适用场景&lt;/strong>： brainstorming、创意写作、复杂问题讨论&lt;/p>
&lt;p>&lt;strong>缺点&lt;/strong>：通信复杂，难以预测结果，调试困难。&lt;/p>
&lt;h3 id="三种模式对比">三种模式对比
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>模式&lt;/th>
&lt;th>结构&lt;/th>
&lt;th>并行能力&lt;/th>
&lt;th>可控性&lt;/th>
&lt;th>适用场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>链式&lt;/strong>&lt;/td>
&lt;td>线性&lt;/td>
&lt;td>❌ 无&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;td>流水线任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>层级&lt;/strong>&lt;/td>
&lt;td>树形&lt;/td>
&lt;td>✅ 有&lt;/td>
&lt;td>⭐⭐ 中&lt;/td>
&lt;td>项目管理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>网状&lt;/strong>&lt;/td>
&lt;td>图形&lt;/td>
&lt;td>✅ 有&lt;/td>
&lt;td>⭐ 低&lt;/td>
&lt;td>创意探索&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="企业级落地案例">企业级落地案例
&lt;/h2>&lt;h3 id="案例-1智能客服系统">案例 1：智能客服系统
&lt;/h3>&lt;p>&lt;strong>需求&lt;/strong>：处理用户咨询，复杂问题转人工&lt;/p>
&lt;p>&lt;strong>Agent 设计&lt;/strong>：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/customer-service-system.svg"
loading="lazy"
alt="智能客服系统架构"
>&lt;/p>
&lt;p>&lt;strong>关键技术点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Router Agent：分类模型（GPT-4 或微调小模型）&lt;/li>
&lt;li>FAQ Agent：RAG 检索知识库&lt;/li>
&lt;li>Order Agent：调用订单系统 API&lt;/li>
&lt;li>Tech Agent：RAG + 工具调用（日志查询、配置修改）&lt;/li>
&lt;li>Human Agent：判断是否需要转人工，生成工单&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>成本优化&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>模型选择&lt;/th>
&lt;th>适用 Agent&lt;/th>
&lt;th>原因&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GPT-4o&lt;/td>
&lt;td>Router Agent&lt;/td>
&lt;td>分类准确性最重要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPT-3.5&lt;/td>
&lt;td>FAQ Agent&lt;/td>
&lt;td>简单问答，成本低&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Claude&lt;/td>
&lt;td>Tech Agent&lt;/td>
&lt;td>长上下文，技术文档理解强&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="案例-2自动化报表生成">案例 2：自动化报表生成
&lt;/h3>&lt;p>&lt;strong>需求&lt;/strong>：每周自动生成销售报表，包含数据查询、图表生成、文字总结&lt;/p>
&lt;p>&lt;strong>Agent 设计&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 链式编排&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">workflow&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sql_agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;查询数据库获取销售数据&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;analysis_agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;分析数据趋势&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;chart_agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;生成可视化图表&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;writer_agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;撰写报表文字说明&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>实际代码&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StateGraph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ReportState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TypedDict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">raw_data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span> &lt;span class="c1"># SQL 查询结果&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">analysis&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">dict&lt;/span> &lt;span class="c1"># 分析结论&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">charts&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span> &lt;span class="c1"># 图表文件路径&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">report&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="c1"># 最终报表文本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ReportState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># SQL Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">sql_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;SELECT date, revenue FROM sales WHERE date &amp;gt;= &amp;#39;2024-01-01&amp;#39;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;raw_data&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Analysis Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">analysis_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;raw_data&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 计算增长率、异常点等&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">analysis&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">analyze_sales_data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;analysis&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">analysis&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Chart Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">chart_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">charts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">generate_charts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;raw_data&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;charts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">charts&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Writer Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">writer_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">report&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">write_report&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;analysis&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;charts&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;report&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">report&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 构建流程&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sql&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sql_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;analysis&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">analysis_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;chart&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">chart_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;writer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">writer_agent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sql&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;analysis&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;analysis&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;chart&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;chart&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;writer&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="案例-3代码开发助手">案例 3：代码开发助手
&lt;/h3>&lt;p>&lt;strong>需求&lt;/strong>：从需求描述到代码提交的完整流程&lt;/p>
&lt;p>&lt;strong>层级编排设计&lt;/strong>：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/code-dev-assistant.svg"
loading="lazy"
alt="代码开发助手层级编排"
>&lt;/p>
&lt;p>&lt;strong>实际效果对比&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>任务&lt;/th>
&lt;th>传统开发&lt;/th>
&lt;th>Agent 辅助&lt;/th>
&lt;th>提升&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>写 CRUD API&lt;/td>
&lt;td>2 小时&lt;/td>
&lt;td>15 分钟&lt;/td>
&lt;td>8x&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写单元测试&lt;/td>
&lt;td>1 小时&lt;/td>
&lt;td>5 分钟&lt;/td>
&lt;td>12x&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写 API 文档&lt;/td>
&lt;td>30 分钟&lt;/td>
&lt;td>3 分钟&lt;/td>
&lt;td>10x&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Code Review&lt;/td>
&lt;td>20 分钟&lt;/td>
&lt;td>10 分钟&lt;/td>
&lt;td>2x&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="核心技术栈详解">核心技术栈详解
&lt;/h2>&lt;h3 id="langchain最全生态的-llm-框架">LangChain：最全生态的 LLM 框架
&lt;/h3>&lt;p>&lt;strong>核心模块&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">LangChain
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── langchain-core # 核心抽象（Chain、Agent、Tool）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── langchain-community # 社区集成（各种 API、数据库）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── langchain-openai # OpenAI 专用集成
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── langchain-anthropic # Anthropic 专用集成
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── langgraph # 状态机编排（独立包）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>必学概念&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>概念&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>学习优先级&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Chain&lt;/strong>&lt;/td>
&lt;td>顺序执行的调用链&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Agent&lt;/strong>&lt;/td>
&lt;td>自主决策的执行单元&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Tool&lt;/strong>&lt;/td>
&lt;td>Agent 可调用的工具&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Memory&lt;/strong>&lt;/td>
&lt;td>对话历史管理&lt;/td>
&lt;td>⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Retriever&lt;/strong>&lt;/td>
&lt;td>RAG 检索器&lt;/td>
&lt;td>⭐⭐⭐&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Callback&lt;/strong>&lt;/td>
&lt;td>执行过程监听&lt;/td>
&lt;td>⭐&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>学习资源&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>官方文档：https://python.langchain.com/&lt;/li>
&lt;li>LangChain v0.3 API 文档（本项目已收录）：&lt;a class="link" href="../LangChain/langchain_v0.3_API/" >langchain_v0.3_API&lt;/a>&lt;/li>
&lt;li>LangSmith 使用教程：&lt;a class="link" href="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/" >LangSmith使用教程&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="langgraph状态机编排">LangGraph：状态机编排
&lt;/h3>&lt;p>LangGraph 是 LangChain 团队推出的&lt;strong>循环图编排框架&lt;/strong>，核心概念：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langgraph.graph&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">END&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义状态（在节点间传递的数据）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AgentState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TypedDict&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">next_action&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义节点（Agent 或函数）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">agent_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">tool_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tool_result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">execute_tool&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">tool_result&lt;/span>&lt;span class="p">]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 构建图&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StateGraph&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AgentState&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">agent_node&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tool&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tool_node&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义边（条件分支）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">should_continue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tool_calls&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;tool&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">END&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_conditional_edges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">should_continue&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tool&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;agent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;messages&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;帮我查北京天气&amp;#34;&lt;/span>&lt;span class="p">]})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>与 LangChain Chain 的区别&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>Chain&lt;/th>
&lt;th>LangGraph&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>结构&lt;/td>
&lt;td>线性&lt;/td>
&lt;td>循环图&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>状态管理&lt;/td>
&lt;td>无&lt;/td>
&lt;td>有（TypedDict）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>条件分支&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>持久化&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>✅（可中断恢复）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>适用场景&lt;/td>
&lt;td>简单流程&lt;/td>
&lt;td>复杂流程、对话循环&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>学习资源&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>官方文档：https://langchain-ai.github.io/langgraph/&lt;/li>
&lt;li>LangGraph 使用教程（本项目已收录）：&lt;a class="link" href="https://www.zata.cc/p/langgraph-%E5%AE%9E%E6%88%98stategraph%E6%89%8B%E5%86%99-react-%E5%BE%AA%E7%8E%AF%E4%B8%8E-map-reduce-%E6%91%98%E8%A6%81/" >Langgraph使用教程&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="autogen微软多智能体框架">AutoGen：微软多智能体框架
&lt;/h3>&lt;p>&lt;strong>核心特点&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Agent 之间通过&lt;strong>对话&lt;/strong>协作&lt;/li>
&lt;li>支持&lt;strong>人类介入&lt;/strong>（Human-in-the-loop）&lt;/li>
&lt;li>内置&lt;strong>代码执行沙箱&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>快速示例&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">autogen&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置 LLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">config_list&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;api_key&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;your-key&amp;#34;&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建 Agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">assistant&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">autogen&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">AssistantAgent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Assistant&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;config_list&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">config_list&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">user_proxy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">autogen&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">UserProxyAgent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;User&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">human_input_mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;TERMINATE&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 人类只在结束时介入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">code_execution_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;work_dir&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;coding&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 开始对话&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">user_proxy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">initiate_chat&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">assistant&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;写一个 Python 函数计算斐波那契数列&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>与 LangGraph 对比&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>AutoGen&lt;/th>
&lt;th>LangGraph&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>编排方式&lt;/td>
&lt;td>对话驱动&lt;/td>
&lt;td>状态机驱动&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>控制流&lt;/td>
&lt;td>隐式（Agent 自主决定）&lt;/td>
&lt;td>显式（开发者定义图）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>可控性&lt;/td>
&lt;td>⭐ 低&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>灵活性&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;td>⭐⭐ 中&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>适用场景&lt;/td>
&lt;td>创意、探索性任务&lt;/td>
&lt;td>流程化、工程化任务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="提示词工程核心技巧">提示词工程核心技巧
&lt;/h2>&lt;p>提示词是 Agent 的&amp;quot;编程语言&amp;quot;，掌握核心技巧至关重要。&lt;/p>
&lt;h3 id="1-cot思维链">1. CoT（思维链）
&lt;/h3>&lt;p>让模型&lt;strong>逐步推理&lt;/strong>，输出中间步骤。&lt;/p>
&lt;p>&lt;strong>普通提示词&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">问：小明有 5 个苹果，给了小红 2 个，又买了 3 个，现在有多少个？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">答：6 个
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>CoT 提示词&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">问：小明有 5 个苹果，给了小红 2 个，又买了 3 个，现在有多少个？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">请逐步思考：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. 初始有多少？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 给出去多少？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 又买了多少？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 最终有多少？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">答：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. 初始有 5 个苹果
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 给小红 2 个，剩下 5-2=3 个
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 又买了 3 个，现在有 3+3=6 个
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 最终有 6 个苹果
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>效果&lt;/strong>：复杂问题上准确率提升 30-50%。&lt;/p>
&lt;h3 id="2-few-shot-prompting">2. Few-shot Prompting
&lt;/h3>&lt;p>给模型&lt;strong>几个示例&lt;/strong>，让它学习模式。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">任务：将句子翻译成 SQL
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">示例：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">句子：查询所有年龄大于 20 的用户
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">SQL：SELECT * FROM users WHERE age &amp;gt; 20
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">句子：查询订单金额超过 1000 的订单
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">SQL：SELECT * FROM orders WHERE amount &amp;gt; 1000
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">现在请翻译：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">句子：查询最近一周登录过的用户
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">SQL：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-structured-output">3. Structured Output
&lt;/h3>&lt;p>让模型输出&lt;strong>结构化数据&lt;/strong>（JSON），便于程序解析。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AnalysisResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sentiment&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="c1"># positive/negative/neutral&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">topics&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 主题列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">summary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="c1"># 总结&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PydanticOutputParser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pydantic_object&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">AnalysisResult&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">分析以下文本的情感和主题：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-react-模板">4. ReAct 模板
&lt;/h3>&lt;p>结合推理和行动的标准模板：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">react_prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">你是一个智能助手，可以使用工具完成任务。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">可用工具：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">- search: 搜索互联网
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">- calculator: 数学计算
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">- weather: 查询天气
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">思考过程格式：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Thought: 思考下一步做什么
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Action: 调用什么工具（tool_name）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Action Input: 工具输入参数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Observation: 工具返回结果
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">...（重复直到完成任务）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Answer: 最终答案
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">开始！
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">用户问题：&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="评估与优化">评估与优化
&lt;/h2>&lt;h3 id="评估指标">评估指标
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>指标&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>计算方式&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>任务完成率&lt;/strong>&lt;/td>
&lt;td>Agent 是否成功完成任务&lt;/td>
&lt;td>人工标注 或 规则判断&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>响应准确率&lt;/strong>&lt;/td>
&lt;td>输出内容是否正确&lt;/td>
&lt;td>人工评估 或 LLM-as-Judge&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Token 成本&lt;/strong>&lt;/td>
&lt;td>每次调用的 Token 消耗&lt;/td>
&lt;td>直接从 API 返回获取&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>延迟&lt;/strong>&lt;/td>
&lt;td>从输入到输出的时间&lt;/td>
&lt;td>计时统计&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>工具调用成功率&lt;/strong>&lt;/td>
&lt;td>Agent 调用工具是否正确&lt;/td>
&lt;td>检查工具返回&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="langsmith调试与监控平台">LangSmith：调试与监控平台
&lt;/h3>&lt;p>LangSmith 是 LangChain 官方的调试平台：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;LANGCHAIN_API_KEY&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;your-key&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;LANGCHAIN_TRACING_V2&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 所有 LangChain 调用都会记录到 LangSmith&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.agents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AgentExecutor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AgentExecutor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_agent_and_tools&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">agent&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 在 LangSmith 网站查看完整调用链、Token 消耗、耗时等&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="成本优化策略">成本优化策略
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>策略&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>效果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>分层模型&lt;/strong>&lt;/td>
&lt;td>分类用小模型，复杂任务用大模型&lt;/td>
&lt;td>成本降低 60-80%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Prompt 精简&lt;/strong>&lt;/td>
&lt;td>去除冗余描述，压缩 Token&lt;/td>
&lt;td>成本降低 20-30%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>缓存&lt;/strong>&lt;/td>
&lt;td>相似问题复用答案&lt;/td>
&lt;td>成本降低 40-50%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>并行调用&lt;/strong>&lt;/td>
&lt;td>多 Agent 同时执行&lt;/td>
&lt;td>延迟降低 50-70%&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="学习路径详细规划">学习路径详细规划
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/learning-path.svg"
loading="lazy"
alt="学习路径规划"
>&lt;/p>
&lt;h3 id="第一阶段基础1-2-周">第一阶段：基础（1-2 周）
&lt;/h3>&lt;p>&lt;strong>目标&lt;/strong>：理解 LLM 基础，掌握 API 调用&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>内容&lt;/th>
&lt;th>学习资源&lt;/th>
&lt;th>验收标准&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Python 异步编程&lt;/td>
&lt;td>官方文档、asyncio 教程&lt;/td>
&lt;td>能写异步 API 服务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LLM API 调用&lt;/td>
&lt;td>OpenAI 文档、Claude 文档&lt;/td>
&lt;td>能调用并处理返回&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Prompt 基础&lt;/td>
&lt;td>Learn Prompting 网站&lt;/td>
&lt;td>能写 CoT、Few-shot&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>练习项目&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>写一个聊天机器人（调用 OpenAI API）&lt;/li>
&lt;li>实现简单的 CoT 推理（数学问题）&lt;/li>
&lt;/ul>
&lt;h3 id="第二阶段框架2-4-周">第二阶段：框架（2-4 周）
&lt;/h3>&lt;p>&lt;strong>目标&lt;/strong>：掌握 LangChain 核心用法&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>内容&lt;/th>
&lt;th>学习资源&lt;/th>
&lt;th>验收标准&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LangChain Chain&lt;/td>
&lt;td>官方文档 + 本项目教程&lt;/td>
&lt;td>能构建顺序调用链&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangChain Agent&lt;/td>
&lt;td>官方文档 + 本项目教程&lt;/td>
&lt;td>能让 Agent 使用工具&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LlamaIndex RAG&lt;/td>
&lt;td>官方文档 + 本项目 RAG 系列&lt;/td>
&lt;td>能构建文档问答系统&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Function Calling&lt;/td>
&lt;td>OpenAI 文档&lt;/td>
&lt;td>能定义和使用工具&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>练习项目&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>构建一个文档问答系统（RAG）&lt;/li>
&lt;li>构建一个能搜索互联网的 Agent&lt;/li>
&lt;/ul>
&lt;h3 id="第三阶段编排4-8-周">第三阶段：编排（4-8 周）
&lt;/h3>&lt;p>&lt;strong>目标&lt;/strong>：掌握多 Agent 编排&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>内容&lt;/th>
&lt;th>学习资源&lt;/th>
&lt;th>验收标准&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LangGraph 状态机&lt;/td>
&lt;td>官方文档 + 本项目教程&lt;/td>
&lt;td>能构建循环流程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>多 Agent 协作模式&lt;/td>
&lt;td>AutoGen/CrewAI 文档&lt;/td>
&lt;td>能设计协作架构&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AutoGen 实践&lt;/td>
&lt;td>微软官方教程&lt;/td>
&lt;td>能构建对话式多 Agent&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>练习项目&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>构建一个自动化报表生成系统（链式编排）&lt;/li>
&lt;li>构建一个代码开发助手（层级编排）&lt;/li>
&lt;/ul>
&lt;h3 id="第四阶段工程化持续">第四阶段：工程化（持续）
&lt;/h3>&lt;p>&lt;strong>目标&lt;/strong>：生产环境落地能力&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>内容&lt;/th>
&lt;th>学习资源&lt;/th>
&lt;th>验收标准&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Agent 系统架构&lt;/td>
&lt;td>架构设计书籍 + 实战经验&lt;/td>
&lt;td>能设计高可用系统&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>评估体系搭建&lt;/td>
&lt;td>LangSmith + 自建评估&lt;/td>
&lt;td>能监控 Agent 性能&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>成本优化&lt;/td>
&lt;td>实战经验积累&lt;/td>
&lt;td>能降低 50%+ 成本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>部署与运维&lt;/td>
&lt;td>Docker/K8s + CI/CD&lt;/td>
&lt;td>能部署到生产环境&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="发展趋势与前景">发展趋势与前景
&lt;/h2>&lt;h3 id="从对话走向行动">从&amp;quot;对话&amp;quot;走向&amp;quot;行动&amp;quot;
&lt;/h3>&lt;p>2023 年的 Agent 主要做&amp;quot;问答&amp;quot;。2024-2025 年的 Agent 开始&lt;strong>执行操作&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>能力&lt;/th>
&lt;th>2023 年&lt;/th>
&lt;th>2025 年&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>回答问题&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>搜索信息&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>写代码&lt;/td>
&lt;td>⭐ 需人工复制&lt;/td>
&lt;td>✅ 自动执行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>调用 API&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>修改文件&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>发送邮件&lt;/td>
&lt;td>❌&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>典型案例&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Devin&lt;/strong>：AI 软件工程师，能独立完成开发任务&lt;/li>
&lt;li>&lt;strong>Claude Code&lt;/strong>：能直接在终端执行开发任务&lt;/li>
&lt;li>&lt;strong>GPT-4o + Actions&lt;/strong>：能直接调用外部服务&lt;/li>
&lt;/ul>
&lt;h3 id="低代码化趋势">低代码化趋势
&lt;/h3>&lt;p>编排工具正在从&amp;quot;写代码&amp;quot;向&amp;quot;画流程图&amp;quot;转变：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>工具&lt;/th>
&lt;th>编排方式&lt;/th>
&lt;th>技能要求&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LangGraph&lt;/td>
&lt;td>写 Python 代码&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Flowise&lt;/td>
&lt;td>拖拽可视化&lt;/td>
&lt;td>⭐ 低&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dify&lt;/td>
&lt;td>拖拽 + 配置&lt;/td>
&lt;td>⭐ 低&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LangFlow&lt;/td>
&lt;td>拖拽可视化&lt;/td>
&lt;td>⭐ 低&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>对工程师的影响&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>简单编排将被低代码平台取代&lt;/li>
&lt;li>高阶工程师需深入：算法优化、模型微调、复杂架构设计&lt;/li>
&lt;/ul>
&lt;h3 id="企业级落地爆发">企业级落地爆发
&lt;/h3>&lt;p>2024-2025 年是 Agent 落地元年。行业需求：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>行业&lt;/th>
&lt;th>Agent 应用&lt;/th>
&lt;th>人才需求&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>金融&lt;/td>
&lt;td>自动化投研、风控分析&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>医疗&lt;/td>
&lt;td>病历分析、诊断辅助&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>法律&lt;/td>
&lt;td>合同审查、案例分析&lt;/td>
&lt;td>⭐⭐ 中&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>客服&lt;/td>
&lt;td>智能客服、工单处理&lt;/td>
&lt;td>⭐⭐⭐ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>制造&lt;/td>
&lt;td>生产调度、质量控制&lt;/td>
&lt;td>⭐⭐ 中&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>薪资水平&lt;/strong>（2024 年中国市场参考）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>级别&lt;/th>
&lt;th>经验&lt;/th>
&lt;th>薪资范围&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>初级&lt;/td>
&lt;td>0-2 年&lt;/td>
&lt;td>15-25k/月&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>中级&lt;/td>
&lt;td>2-4 年&lt;/td>
&lt;td>25-40k/月&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>高级&lt;/td>
&lt;td>4-6 年&lt;/td>
&lt;td>40-60k/月&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>专家&lt;/td>
&lt;td>6+ 年&lt;/td>
&lt;td>60-100k/月&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="多模态编排">多模态编排
&lt;/h3>&lt;p>未来 Agent 需要处理多种模态：&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%99%BA%E8%83%BD%E4%BD%93%E7%BC%96%E6%8E%92%E8%AE%BE%E8%AE%A1%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97/images/multi-modal-agent.svg"
loading="lazy"
alt="多模态 Agent"
>&lt;/p>
&lt;p>&lt;strong>示例场景&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>营销 Agent：写文案 → 生成海报图 → 合成配音视频&lt;/li>
&lt;li>教育 Agent：分析学生作业图片 → 语音讲解 → 生成练习题&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="核心竞争力总结">核心竞争力总结
&lt;/h2>&lt;blockquote>
&lt;p>&lt;strong>不在于你会调某个 API，而在于你能否设计出一套机制，让不完美的模型通过工具和协作，输出稳定、可靠的结果。&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>核心能力拆解：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>能力&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>如何培养&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>架构设计&lt;/strong>&lt;/td>
&lt;td>设计 Agent 结构和协作模式&lt;/td>
&lt;td>实战项目 + 参考优秀案例&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Prompt 工程&lt;/strong>&lt;/td>
&lt;td>精准控制模型行为&lt;/td>
&lt;td>大量练习 + 持续迭代&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>成本优化&lt;/strong>&lt;/td>
&lt;td>在效果和成本间找到平衡&lt;/td>
&lt;td>实战数据 + 对比实验&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>评估能力&lt;/strong>&lt;/td>
&lt;td>判断 Agent 是否达到目标&lt;/td>
&lt;td>建立评估体系 + 数据驱动&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>业务理解&lt;/strong>&lt;/td>
&lt;td>把业务问题转化为 Agent 流程&lt;/td>
&lt;td>深入业务 + 持续沟通&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="适合人群">适合人群
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>有后端开发背景&lt;/strong>，对 AI 算法感兴趣的工程师&lt;/li>
&lt;li>&lt;strong>善于逻辑拆解&lt;/strong>，懂技术的产品型工程师&lt;/li>
&lt;li>&lt;strong>希望从传统开发转型 AI 应用落地&lt;/strong>的开发者&lt;/li>
&lt;li>&lt;strong>有数据/算法背景&lt;/strong>，想往工程化方向发展的人&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="常见问题">常见问题
&lt;/h2>&lt;h3 id="q1需要学机器学习深度学习吗">Q1：需要学机器学习/深度学习吗？
&lt;/h3>&lt;p>&lt;strong>不需要深入&lt;/strong>。这个岗位侧重&lt;strong>应用落地&lt;/strong>，不是模型研发。但需要理解：&lt;/p>
&lt;ul>
&lt;li>LLM 的基本原理（Token、Context Window、Temperature）&lt;/li>
&lt;li>模型的能力边界（幻觉、遗忘、推理能力）&lt;/li>
&lt;li>如何评估模型效果&lt;/li>
&lt;/ul>
&lt;h3 id="q2python-不熟怎么办">Q2：Python 不熟怎么办？
&lt;/h3>&lt;p>&lt;strong>必须补&lt;/strong>。LangChain、LangGraph、AutoGen 都是 Python 框架。建议：&lt;/p>
&lt;ul>
&lt;li>先学基础语法（1 周）&lt;/li>
&lt;li>重点学异步编程、API 开发（FastAPI）&lt;/li>
&lt;li>边做项目边学，不要光学不练&lt;/li>
&lt;/ul>
&lt;h3 id="q3如何选择-langgraph-vs-autogen">Q3：如何选择 LangGraph vs AutoGen？
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>你的情况&lt;/th>
&lt;th>推荐&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>需要可控流程、工程化项目&lt;/td>
&lt;td>LangGraph&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>需要灵活对话、创意任务&lt;/td>
&lt;td>AutoGen&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>初学者&lt;/td>
&lt;td>LangGraph（文档更清晰）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="q4agent-和传统自动化有什么区别">Q4：Agent 和传统自动化有什么区别？
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>传统自动化&lt;/th>
&lt;th>Agent&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>规则&lt;/td>
&lt;td>人工硬编码&lt;/td>
&lt;td>模型自主决策&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>灵活性&lt;/td>
&lt;td>❌ 低&lt;/td>
&lt;td>✅ 高&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>错误处理&lt;/td>
&lt;td>需人工编码&lt;/td>
&lt;td>模型可自主处理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>适用场景&lt;/td>
&lt;td>固定流程&lt;/td>
&lt;td>不确定任务&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="参考资料">参考资料
&lt;/h2>&lt;h3 id="官方文档">官方文档
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://python.langchain.com/" target="_blank" rel="noopener"
>LangChain 官方文档&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://langchain-ai.github.io/langgraph/" target="_blank" rel="noopener"
>LangGraph 官方文档&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://microsoft.github.io/autogen/" target="_blank" rel="noopener"
>AutoGen - 微软多智能体框架&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://docs.llamaindex.ai/" target="_blank" rel="noopener"
>LlamaIndex 官方文档&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://docs.crewai.com/" target="_blank" rel="noopener"
>CrewAI 官方文档&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="本项目相关文章">本项目相关文章
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.zata.cc/tags/langchain/" >LangChain 系列教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/tags/rag/" >RAG 系列教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/langgraph-%E5%AE%9E%E6%88%98stategraph%E6%89%8B%E5%86%99-react-%E5%BE%AA%E7%8E%AF%E4%B8%8E-map-reduce-%E6%91%98%E8%A6%81/" >LangGraph 使用教程&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.zata.cc/p/langsmith%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/" >LangSmith 使用教程&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="学习网站">学习网站
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://learnprompting.org/" target="_blank" rel="noopener"
>Learn Prompting&lt;/a> - Prompt 工程教程&lt;/li>
&lt;li>&lt;a class="link" href="https://www.deeplearning.ai/" target="_blank" rel="noopener"
>DeepLearning.AI&lt;/a> - Andrew Ng 的 AI 课程&lt;/li>
&lt;li>&lt;a class="link" href="https://huggingface.co/learn" target="_blank" rel="noopener"
>HuggingFace Course&lt;/a> - NLP 和 Transformers 课程&lt;/li>
&lt;/ul>
&lt;h3 id="社区与资讯">社区与资讯
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://discord.gg/langchain" target="_blank" rel="noopener"
>LangChain Discord&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/list/cs.AI/recent" target="_blank" rel="noopener"
>AI 相关 Arxiv 论文&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://news.ycombinator.com/" target="_blank" rel="noopener"
>Hacker News AI 板块&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>AI agent介绍：基于大模型的人工智能代理</title><link>https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/</link><pubDate>Wed, 07 May 2025 14:34:02 +0800</pubDate><guid>https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/</guid><description>&lt;img src="https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/images/index/index.png" alt="Featured image of post AI agent介绍：基于大模型的人工智能代理" />&lt;p>参考：&lt;/p>
&lt;p>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/657937696" target="_blank" rel="noopener"
>知乎&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="https://zhuanlan.zhihu.com/p/659386520" target="_blank" rel="noopener"
>智能代理Agent：AI 智能体&lt;/a>&lt;/p>
&lt;h2 id="agent基础知识介绍">Agent基础知识介绍
&lt;/h2>&lt;hr>
&lt;blockquote>
&lt;p>背景介绍&lt;/p>
&lt;/blockquote>
&lt;p>AI Agent（人工智能代理）的背景可以从技术发展、理论基础和应用场景三个方面来阐述：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>技术发展背景&lt;/strong>&lt;/p>
&lt;p>AI Agent的概念起源于人工智能（AI）和计算机科学领域的进步，尤其是20世纪80年代以来，随着分布式系统、自主计算和智能系统的兴起，AI Agent逐渐成为研究和应用的热点。以下是关键技术发展的几个阶段：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>早期AI与规则系统&lt;/strong>：20世纪50-60年代，AI研究主要集中在符号推理和专家系统上，这些系统通过预定义规则模拟智能行为，为后来的Agent奠定了基础。&lt;/li>
&lt;li>&lt;strong>分布式人工智能（DAI）&lt;/strong>：80年代，分布式计算和多Agent系统（Multi-Agent Systems, MAS）的研究兴起，强调多个自主实体协作解决问题，推动了AI Agent的理论发展。&lt;/li>
&lt;li>&lt;strong>机器学习与深度学习&lt;/strong>：21世纪以来，机器学习（尤其是深度学习）的突破使得AI Agent能够通过数据驱动的方式学习复杂行为，增强了其感知、决策和适应能力。&lt;/li>
&lt;li>&lt;strong>大语言模型（LLM）&lt;/strong>：近年来，以GPT、Llama等为代表的大语言模型赋予了AI Agent强大的自然语言处理能力，使其能理解和生成人类语言，广泛应用于对话系统、任务自动化等场景。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>理论基础&lt;/strong>&lt;/p>
&lt;p>AI Agent的核心理念是构建能够自主感知环境、推理决策并采取行动的智能实体。其理论基础包括：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Agent定义&lt;/strong>：在AI领域，Agent通常被定义为“能够感知环境并通过行动影响环境的实体”。Russell和Norvig的《人工智能：一种现代方法》中将其形式化为感知-推理-行动循环。&lt;/li>
&lt;li>&lt;strong>自主性与交互性&lt;/strong>：AI Agent具有一定程度的自主性（独立决策能力）和交互性（与环境或其他Agent协作或竞争）。&lt;/li>
&lt;li>&lt;strong>多Agent系统&lt;/strong>：多Agent系统研究多个Agent如何通过协作、协商或竞争完成复杂任务，涉及博弈论、分布式计算等理论。&lt;/li>
&lt;li>&lt;strong>强化学习（RL）&lt;/strong>：强化学习为AI Agent提供了通过试错学习最优策略的框架，广泛应用于机器人、游戏AI等领域。&lt;/li>
&lt;li>&lt;strong>认知架构&lt;/strong>：如SOAR、ACT-R等认知架构为AI Agent提供了模拟人类认知过程的理论支持。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>应用场景与背景&lt;/strong>&lt;/p>
&lt;p>AI Agent的应用背景与现代社会对自动化、智能化和个性化的需求密切相关。以下是主要应用领域：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>个人助手&lt;/strong>：如Siri、Google Assistant等，利用自然语言处理和任务规划技术，帮助用户完成日程管理、信息查询等任务。&lt;/li>
&lt;li>&lt;strong>游戏与仿真&lt;/strong>：AI Agent在电子游戏（如NPC）和仿真环境中扮演智能角色，通过强化学习等技术实现逼真的行为。&lt;/li>
&lt;li>&lt;strong>机器人与自动驾驶&lt;/strong>：机器人和自动驾驶汽车中的AI Agent通过传感器感知环境，结合路径规划和决策算法实现自主导航。&lt;/li>
&lt;li>&lt;strong>企业自动化&lt;/strong>：在金融、物流、客服等领域，AI Agent用于自动化交易、智能调度、聊天机器人等，提升效率并降低成本。&lt;/li>
&lt;li>&lt;strong>多Agent协作&lt;/strong>：如智能电网、智慧城市中，多个AI Agent协作优化资源分配，应对复杂动态环境。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>当前趋势与挑战&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>通用AI Agent&lt;/strong>：基于大模型的AI Agent正在向通用智能方向发展，能够跨领域执行多样化任务。&lt;/li>
&lt;li>&lt;strong>人机协作&lt;/strong>：AI Agent越来越注重与人类的自然交互，成为辅助工具而非完全替代。&lt;/li>
&lt;li>&lt;strong>开源生态&lt;/strong>：如LangChain、AutoGPT等开源框架降低了开发AI Agent的门槛。&lt;/li>
&lt;li>&lt;strong>挑战&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>可解释性&lt;/strong>：AI Agent的决策过程往往是黑箱，难以解释。&lt;/li>
&lt;li>&lt;strong>安全性与伦理&lt;/strong>：自主性强的Agent可能引发误操作或伦理问题。&lt;/li>
&lt;li>&lt;strong>资源消耗&lt;/strong>：复杂AI Agent（如基于大模型的）需要大量计算资源。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>总结&lt;/strong>&lt;/p>
&lt;p>AI Agent的背景源于人工智能技术的演进、分布式系统和自主计算的理论发展，以及现代社会对智能自动化的需求。从早期的规则系统到如今的大模型驱动Agent，AI Agent在理论和应用上都取得了显著进步。未来，随着技术进一步成熟，AI Agent将在更多领域发挥重要作用，同时需要解决可解释性、安全性和资源效率等挑战。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;blockquote>
&lt;p>为什么需要如Langchain、AutoGPT这样的框架？&lt;/p>
&lt;/blockquote>
&lt;p>LangChain这样的框架之所以被开发和广泛使用，是因为尽管大语言模型（LLM）本身已经非常强大，能够完成许多任务，但它们在某些复杂场景下存在局限性，而LangChain通过提供结构化的工具和模块，弥补了这些不足，增强了LLM的实用性和灵活性。以下从几个关键方面阐述为什么需要LangChain这样的框架，以及它如何解决LLM的局限性：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>LLM的局限性&lt;/strong>&lt;/p>
&lt;p>尽管LLM（如GPT、Llama等）在自然语言处理、生成文本、回答问题等方面表现出色，但在实际应用中仍面临以下挑战：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>缺乏外部上下文整合能力&lt;/strong>：LLM的知识基于训练数据，截止某一时间点（例如，Grok的知识截至2025年5月），无法直接访问实时数据或特定领域的私有数据（如企业数据库）。&lt;/li>
&lt;li>&lt;strong>短期记忆限制&lt;/strong>：LLM的上下文窗口有限，难以处理超长对话或需要大量历史信息的任务。&lt;/li>
&lt;li>&lt;strong>复杂任务分解困难&lt;/strong>：LLM擅长单次生成或简单推理，但对于需要多步骤推理、工具调用或外部资源协调的复杂任务（如自动化工作流），其能力有限。&lt;/li>
&lt;li>&lt;strong>缺乏结构化交互&lt;/strong>：LLM的输出是文本流，难以直接与外部系统（如API、数据库）交互或实现自动化流程。&lt;/li>
&lt;li>&lt;strong>定制化与可控性不足&lt;/strong>：直接使用LLM难以实现特定业务逻辑的定制化，开发者需要额外的工程工作来整合模型与应用需求。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>LangChain的核心作用&lt;/strong>&lt;/p>
&lt;p>LangChain是一个开源框架，旨在通过模块化工具增强LLM的能力，使其更适合构建复杂的、面向应用的AI Agent或工作流。它解决了上述局限性，具体作用包括：&lt;/p>
&lt;p>(1) &lt;strong>增强外部数据访问&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>问题&lt;/strong>：LLM无法直接访问实时数据或私有数据源。&lt;/li>
&lt;li>&lt;strong>LangChain的解决方案&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>工具调用（Tools）&lt;/strong>：LangChain允许LLM调用外部API（如天气服务、搜索引擎）或数据库，获取最新信息。例如，LangChain可以让LLM查询实时股票价格，而非依赖过时的训练数据。&lt;/li>
&lt;li>&lt;strong>文档加载与检索（Retrieval-Augmented Generation, RAG）&lt;/strong>：LangChain支持将外部文档（如PDF、网页）加载到向量数据库中，通过嵌入（embeddings）进行语义检索，让LLM基于特定文档回答问题。这对于企业知识库、法律文件分析等场景尤为重要。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：用户问“今天上海的天气如何？”，LangChain可以将问题路由到天气API，获取实时数据后由LLM生成自然语言回答。&lt;/li>
&lt;/ul>
&lt;p>(2) &lt;strong>扩展上下文管理&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>问题&lt;/strong>：LLM的上下文窗口有限，难以处理长文档或多轮复杂对话。&lt;/li>
&lt;li>&lt;strong>LangChain的解决方案&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>内存模块（Memory）&lt;/strong>：LangChain提供短期和长期记忆机制，跟踪对话历史或关键信息。例如，在多轮客服对话中，LangChain可以保存用户之前的请求，避免重复询问。&lt;/li>
&lt;li>&lt;strong>文档分块与摘要&lt;/strong>：对于超长文档，LangChain可以将内容分块处理，提取关键信息，减轻LLM的上下文负担。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：在法律咨询场景中，LangChain可以管理用户上传的合同全文，提取关键条款供LLM分析，而无需将整个文档塞入上下文。&lt;/li>
&lt;/ul>
&lt;p>(3) &lt;strong>支持复杂任务分解与工作流&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>问题&lt;/strong>：LLM难以直接处理需要多步骤推理或工具协调的复杂任务。&lt;/li>
&lt;li>&lt;strong>LangChain的解决方案&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>链（Chains）&lt;/strong>：LangChain允许开发者定义任务的执行流程，将多个LLM调用、工具调用和逻辑步骤组合成“链”。例如，一个链可以先检索文档、再调用LLM总结、再生成最终回答。&lt;/li>
&lt;li>&lt;strong>代理（Agents）&lt;/strong>：LangChain的Agent模块让LLM动态选择工具和行动路径，处理开放式任务。例如，一个Agent可以根据用户请求决定是查询数据库、调用API还是直接回答。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：用户要求“帮我规划一次旅行”，LangChain的Agent可以分解任务：1) 询问预算和偏好；2) 调用航班API查询票价；3) 调用酒店API推荐住宿；4) 生成完整行程。&lt;/li>
&lt;/ul>
&lt;p>(4) &lt;strong>与外部系统集成&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>问题&lt;/strong>：LLM的文本输出难以直接与外部系统交互。&lt;/li>
&lt;li>&lt;strong>LangChain的解决方案&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>工具集成&lt;/strong>：LangChain提供与多种外部工具的接口（如SQL数据库、Python解释器、Zapier自动化工具），使LLM的输出可以触发实际操作。&lt;/li>
&lt;li>&lt;strong>输出解析&lt;/strong>：LangChain可以将LLM的文本输出结构化为JSON等格式，便于系统处理。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：在电商场景中，LangChain可以将用户查询“最近的订单状态”转化为SQL查询，获取数据库中的订单信息，再由LLM生成用户友好的回答。&lt;/li>
&lt;/ul>
&lt;p>(5) &lt;strong>提高开发效率与可定制性&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>问题&lt;/strong>：直接使用LLM需要开发者手动处理数据管道、上下文管理和工具调用，开发成本高。&lt;/li>
&lt;li>&lt;strong>LangChain的解决方案&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>模块化设计&lt;/strong>：LangChain提供现成的组件（如文档加载器、嵌入模型、向量存储、提示模板），开发者可以快速组合构建应用。&lt;/li>
&lt;li>&lt;strong>提示工程（Prompt Engineering）&lt;/strong>：LangChain支持动态提示模板，优化LLM的输入以提高输出质量。&lt;/li>
&lt;li>&lt;strong>开源生态&lt;/strong>：LangChain与Hugging Face、Pinecone等工具兼容，降低了技术门槛。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：开发者可以用LangChain在几天内构建一个基于企业知识库的问答系统，而直接调用LLM可能需要数周的编码。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>为什么LLM单独不够？&lt;/strong>&lt;/p>
&lt;p>虽然LLM可以完成许多任务（如文本生成、翻译、问答），但它们更像是一个强大的“语言引擎”，而非完整的解决方案。LangChain将LLM从“通用语言模型”转变为“面向任务的智能系统”，具体优势包括：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>场景适配&lt;/strong>：LLM是通用的，LangChain使其适配特定业务需求（如法律、金融、医疗）。&lt;/li>
&lt;li>&lt;strong>自动化与扩展性&lt;/strong>：LangChain支持自动化工作流和规模化部署，LLM单独难以实现。&lt;/li>
&lt;li>&lt;strong>用户体验&lt;/strong>：通过内存管理和外部数据整合，LangChain提升了交互的连贯性和准确性。&lt;/li>
&lt;li>&lt;strong>开发效率&lt;/strong>：LangChain降低了从原型到生产环境的开发难度。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>实际案例对比&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>仅用LLM&lt;/strong>：用户问“我的订单在哪里？”，LLM可能回复“我不知道你的订单信息，请提供更多细节”，因为它无法访问数据库。&lt;/li>
&lt;li>&lt;strong>用LangChain&lt;/strong>：LangChain将问题路由到订单数据库，提取最新状态（如“您的订单已于5月6日发货”），再由LLM生成自然语言回答，提升用户体验。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>其他类似框架&lt;/strong>&lt;/p>
&lt;p>LangChain不是唯一的选择，其他框架如LlamaIndex、Haystack、AutoGPT等也有类似功能，但LangChain因其模块化、易用性和开源社区支持而广受欢迎。每个框架的侧重点不同：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>LlamaIndex&lt;/strong>：更专注于RAG和文档检索。&lt;/li>
&lt;li>&lt;strong>AutoGPT&lt;/strong>：强调自主Agent的自动化任务执行。&lt;/li>
&lt;li>&lt;strong>Haystack&lt;/strong>：专注于搜索和问答系统。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>总结&lt;/strong>&lt;/p>
&lt;p>LLM虽然强大，但其通用性和孤立性限制了它在复杂、动态和特定场景下的应用。LangChain通过提供外部数据整合、上下文管理、任务分解、工具调用和模块化开发等功能，极大地扩展了LLM的能力，使其从“语言模型”升级为“智能系统”。对于需要构建生产级AI应用的开发者来说，LangChain这样的框架是不可或缺的桥梁。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;blockquote>
&lt;p>对于未来Agent的展望&lt;/p>
&lt;/blockquote>
&lt;p>未来AI Agent的发展前景广阔，将在技术、应用和社会影响等多个维度上持续演进。基于当前趋势和技术进步，以下从几个关键方面分析AI Agent的未来发展方向、潜力以及可能面临的挑战：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>技术趋势与发展方向&lt;/strong>&lt;/p>
&lt;p>(1) &lt;strong>通用智能Agent（General-Purpose Agents）&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：未来的AI Agent将从特定任务导向（如对话机器人、自动驾驶Agent）向通用智能方向发展，能够跨领域、跨任务执行复杂指令，类似人类的“全能助手”。&lt;/li>
&lt;li>&lt;strong>技术驱动&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>大语言模型（LLM）升级&lt;/strong>：更强大的模型（如Grok 3的后续版本）将具备更强的推理、规划和上下文理解能力。&lt;/li>
&lt;li>&lt;strong>多模态能力&lt;/strong>：Agent将整合视觉、语音、文本等多种输入，处理多模态任务。例如，一个Agent可以同时分析图片、语音指令和文本数据，完成如“根据这张照片设计一个房间布局”的任务。&lt;/li>
&lt;li>&lt;strong>长上下文与记忆&lt;/strong>：通过改进内存管理（如扩展上下文窗口或外部记忆数据库），Agent将能处理超长对话或复杂项目，保持一致性。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：未来的Agent可能像《钢铁侠》中的JARVIS，能够无缝管理日程、分析数据、控制设备并与用户自然对话。&lt;/li>
&lt;/ul>
&lt;p>(2) &lt;strong>自主性与自我进化&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：Agent将具备更高的自主性和自我学习能力，能够在没有明确指令的情况下主动优化策略或发现新任务。&lt;/li>
&lt;li>&lt;strong>技术驱动&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>强化学习（RL）与在线学习&lt;/strong>：Agent将通过与环境的持续交互优化行为，甚至在运行时自我调整模型参数。&lt;/li>
&lt;li>&lt;strong>元学习（Meta-Learning）&lt;/strong>：Agent将“学会学习”，快速适应新任务或环境，减少对大规模训练数据的依赖。&lt;/li>
&lt;li>&lt;strong>开源生态&lt;/strong>：框架如LangChain、AutoGPT的进一步发展将支持开发者构建自适应Agent。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：一个企业Agent可能自主监控供应链数据，预测中断风险并提出优化建议，而无需人工干预。&lt;/li>
&lt;/ul>
&lt;p>(3) &lt;strong>多Agent协作与分布式智能&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：多Agent系统（Multi-Agent Systems, MAS）将成为主流，多个Agent通过协作或竞争解决复杂问题，模拟人类社会分工。&lt;/li>
&lt;li>&lt;strong>技术驱动&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>分布式计算&lt;/strong>：改进的通信协议和分布式架构将支持大规模Agent协作。&lt;/li>
&lt;li>&lt;strong>博弈论与协商机制&lt;/strong>：Agent将使用更复杂的协商算法（如基于博弈论的策略）优化资源分配。&lt;/li>
&lt;li>&lt;strong>去中心化Agent&lt;/strong>：区块链和去中心化AI技术可能催生自主运行的Agent网络。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：在智慧城市中，交通Agent、能源Agent和公共服务Agent协作，动态优化交通流量和能源分配。&lt;/li>
&lt;/ul>
&lt;p>(4) &lt;strong>人机协同与交互性&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：Agent将更注重与人类的自然交互，成为辅助工具而非完全替代，强调可解释性和信任。&lt;/li>
&lt;li>&lt;strong>技术驱动&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>可解释AI（XAI）&lt;/strong>：Agent将提供决策的透明解释，增强用户信任。&lt;/li>
&lt;li>&lt;strong>情感计算&lt;/strong>：通过分析语音语调、面部表情，Agent将实现更具共情力的交互。&lt;/li>
&lt;li>&lt;strong>混合智能&lt;/strong>：Agent与人类专家协同工作，结合人类直觉和AI的计算能力。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：医疗Agent可能协助医生分析影像数据，解释诊断依据并根据医生反馈调整建议。&lt;/li>
&lt;/ul>
&lt;p>(5) &lt;strong>边缘计算与轻量化Agent&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>趋势&lt;/strong>：Agent将部署在边缘设备（如手机、IoT设备），实现低延迟、隐私保护的本地化智能。&lt;/li>
&lt;li>&lt;strong>技术驱动&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>模型压缩&lt;/strong>：通过蒸馏、量化等技术，缩小模型体积以适应资源受限设备。&lt;/li>
&lt;li>&lt;strong>联邦学习&lt;/strong>：Agent在本地学习，保护用户数据隐私。&lt;/li>
&lt;li>&lt;strong>示例&lt;/strong>：智能家居Agent在本地处理语音指令，无需云端传输，降低延迟并增强隐私。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>应用场景的扩展&lt;/strong>&lt;/p>
&lt;p>AI Agent将在以下领域进一步深化应用，改变行业格局：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>个人化服务&lt;/strong>：定制化的教育Agent根据学生进度调整教学内容；健康Agent实时监测生理数据，提供个性化建议。&lt;/li>
&lt;li>&lt;strong>企业自动化&lt;/strong>：Agent将推动“智能企业”，自动化从客服到供应链管理的全流程。例如，财务Agent自动分析报表并预测现金流。&lt;/li>
&lt;li>&lt;strong>创意与娱乐&lt;/strong>：Agent将协助创作音乐、电影剧本，甚至生成虚拟世界中的动态NPC，提升沉浸式体验。&lt;/li>
&lt;li>&lt;strong>科学研究&lt;/strong>：科学Agent将加速研究进程，例如通过自动化实验设计、文献分析推动药物发现。&lt;/li>
&lt;li>&lt;strong>社会治理&lt;/strong>：Agent将优化公共资源分配，如在灾难响应中协调救援物资和人力。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>社会影响&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>正面影响&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>效率提升&lt;/strong>：Agent将大幅降低重复性劳动成本，提高生产力。&lt;/li>
&lt;li>&lt;strong>普惠性&lt;/strong>：开源Agent框架和低成本部署将使中小企业和个人也能受益于AI。&lt;/li>
&lt;li>&lt;strong>创新加速&lt;/strong>：Agent将推动跨学科创新，如在气候变化、医疗等领域的突破。&lt;/li>
&lt;li>&lt;strong>潜在风险&lt;/strong>：&lt;/li>
&lt;li>&lt;strong>就业冲击&lt;/strong>：高度自动化的Agent可能取代部分低技能岗位，引发就业挑战。&lt;/li>
&lt;li>&lt;strong>伦理与安全&lt;/strong>：自主Agent可能因错误决策或恶意使用导致风险，如金融市场操纵或隐私泄露。&lt;/li>
&lt;li>&lt;strong>监管需求&lt;/strong>：Agent的广泛应用将需要新的法律框架，规范其行为和责任归属。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>面临的挑战&lt;/strong>&lt;/p>
&lt;p>尽管前景光明，AI Agent的发展仍需克服以下障碍：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>计算资源与能耗&lt;/strong>：训练和运行复杂Agent需要大量算力，需开发更高效的算法和硬件。&lt;/li>
&lt;li>&lt;strong>可解释性&lt;/strong>：确保Agent的决策透明，避免黑箱问题。&lt;/li>
&lt;li>&lt;strong>安全性&lt;/strong>：防止Agent被黑客操控或生成有害内容。&lt;/li>
&lt;li>&lt;strong>伦理问题&lt;/strong>：平衡Agent的自主性与人类控制，避免过度依赖或失控。&lt;/li>
&lt;li>&lt;strong>标准化与互操作性&lt;/strong>：不同Agent系统间的兼容性需提高，以实现无缝协作。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>未来5-10年的展望&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>短期（1-3年）&lt;/strong>：Agent将更广泛集成到现有系统中，如企业ERP、个人助手，框架如LangChain将进一步成熟，RAG和工具调用成为标配。&lt;/li>
&lt;li>&lt;strong>中期（3-5年）&lt;/strong>：通用Agent开始出现，能够处理跨领域的复杂任务；多Agent协作在交通、物流等领域实现规模化应用。&lt;/li>
&lt;li>&lt;strong>长期（5-10年）&lt;/strong>：Agent可能接近通用人工智能（AGI）水平，成为人类生活中不可或缺的伙伴，推动社会进入高度智能化阶段。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>总结&lt;/strong>&lt;/p>
&lt;p>未来AI Agent将朝着更通用、自主、协作和人性化的方向发展，深刻改变个人生活、企业运营和社会治理。其核心驱动力将是多模态AI、强化学习、分布式系统和人机协同技术的进步。然而，可解释性、安全性和伦理问题将是发展的关键制约因素。Agent的最终目标不仅是自动化任务，而是成为可信任的智能伙伴，与人类共同应对复杂挑战。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="agent示例与架构">Agent示例与架构
&lt;/h2>&lt;h3 id="阿里webdancer自主信息搜索agent">阿里WebDancer：自主信息搜索Agent
&lt;/h3>&lt;p>&lt;a class="link" href="https://blog.csdn.net/u011426236/article/details/150002012" target="_blank" rel="noopener"
>阿里WebDancer：自主信息搜索Agent - csdn介绍&lt;/a>&lt;/p>
&lt;p>简单来说，WebDancer是&lt;strong>阿里巴巴（模型即服务MaaS团队）&lt;strong>开发的一个&lt;/strong>自主AI智能体（Agent）&lt;/strong>。&lt;/p>
&lt;p>您可以把它想象成一个“AI搜索助理”。您不再是给它一个“关键词”，而是给它一个**“任务”**。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>传统搜索：&lt;/strong> 您输入“AI Agent 最新进展”，得到一堆链接，您自己去点开、阅读、总结。&lt;/li>
&lt;li>&lt;strong>WebDancer：&lt;/strong> 您输入“帮我调研一下2025年AI Agent技术的最新进展和主要参与者”，它会&lt;strong>像人一样去“上网”&lt;/strong>，自己浏览网页、点击链接、筛选信息、甚至打开新标签页，最后&lt;strong>直接给您一份总结好的答案&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/images/index/image.png"
width="1976"
height="4163"
srcset="https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/images/index/image_hu3832832615017365602.png 480w, https://www.zata.cc/p/ai-agent%E4%BB%8B%E7%BB%8D%E5%9F%BA%E4%BA%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E4%BB%A3%E7%90%86/images/index/image_hu9692877512065978619.png 1024w"
loading="lazy"
alt="webDancer 的主要流程"
class="gallery-image"
data-flex-grow="47"
data-flex-basis="113px"
>&lt;/p>
&lt;hr>
&lt;blockquote>
&lt;p>核心特点与能力&lt;/p>
&lt;/blockquote>
&lt;p>WebDancer之所以被称为“自主信息搜索Agent”，关键在于它的几个核心能力：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>1. 高度自主性 (Autonomy)&lt;/strong>
这是它最大的特点。WebDancer可以自主理解您的复杂意图，并&lt;strong>自动规划搜索步骤&lt;/strong>。它会自己决定先搜什么、再搜什么、哪些信息是相关的。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>2. 拟人化网页交互 (Human-like Interaction)&lt;/strong>
它不是通过API爬取数据，而是&lt;strong>模拟真实用户&lt;/strong>的行为在浏览器中操作，比如：&lt;/p>
&lt;ul>
&lt;li>滚动页面（Scroll）&lt;/li>
&lt;li>点击链接和按钮（Click）&lt;/li>
&lt;li>在搜索框输入文字（Type）&lt;/li>
&lt;li>切换标签页（Tab Switching）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>3. 复杂任务拆解 (Complex Task Decomposition)&lt;/strong>
面对“帮我找几篇关于Llama 3的深度技术评测，并总结它们的优缺点”这样的复杂问题，它能将其分解为一系列子任务（比如：搜索评测、筛选来源、阅读并提取观点、对比总结），并按顺序执行。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>4. 自我纠错与反思 (Self-Correction &amp;amp; Reflection)&lt;/strong>
这是它“智能”的关键。如果它发现一条搜索路径是死胡同（比如网页打不开、信息不相关，或者进入了广告页），它能够“反思”并尝试新的搜索策略，而不是卡住不动。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>5. 跨网页信息整合 (Information Synthesis)&lt;/strong>
最终，它会把从&lt;strong>多个网页&lt;/strong>（可能是几十个）中找到的零散信息，整合成一个连贯、全面的答案。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;blockquote>
&lt;p>它与传统搜索有何不同？&lt;/p>
&lt;/blockquote>
&lt;p>您可以这样理解它们之间的区别：&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>传统搜索引擎 (如谷歌、百度):&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>角色：&lt;/strong> 信息索引员 (Indexer)&lt;/li>
&lt;li>&lt;strong>工作：&lt;/strong> 您输入关键词，它返回一个&lt;strong>相关网页的“索引列表”&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>用户负担：&lt;/strong> 用户需要自己完成“浏览、筛选、阅读、总结”的工作。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>WebDancer (AI Agent):&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>角色：&lt;/strong> 研究助理 (Research Assistant)&lt;/li>
&lt;li>&lt;strong>工作：&lt;/strong> 您输入一个**“任务”或“问题”&lt;strong>。它&lt;/strong>代您执行“搜索和浏览”的整个过程**。&lt;/li>
&lt;li>&lt;strong>用户负担：&lt;/strong> 用户直接获取**“最终答案”**。&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>总而言之，WebDancer代表了从“信息检索”（Information Retrieval）到“答案获取”（Answer Generation）的转变，是AI Agent技术在信息处理领域的一个重要应用。&lt;/p></description></item><item><title>ai返回数据的格式不稳定，存在解析错误的问题</title><link>https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/</link><pubDate>Mon, 17 Mar 2025 09:33:58 +0800</pubDate><guid>https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/</guid><description>&lt;img src="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-1.png" alt="Featured image of post ai返回数据的格式不稳定，存在解析错误的问题" />&lt;!-- ![alt text](images/index/index-1.png) -->
&lt;h2 id="开发心得">开发心得
&lt;/h2>&lt;h2 id="span-stylecolorred所选用的ai模型的性能起决定性的作用如果用的模型不能按照你想要的格式返回数据那么重试一万次也不能得到一个好的效果带来的只能是token的浪费span">&lt;span style="color:red;">所选用的ai模型的性能起决定性的作用，如果用的模型不能按照你想要的格式返回数据，那么重试一万次也不能得到一个好的效果，带来的只能是Token的浪费&amp;hellip;&lt;/span>
&lt;/h2>&lt;p>&lt;img src="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-2.png"
width="874"
height="381"
srcset="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-2_hu12477584409283897314.png 480w, https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-2_hu567074785518142326.png 1024w"
loading="lazy"
alt="agent"
class="gallery-image"
data-flex-grow="229"
data-flex-basis="550px"
>&lt;/p>
&lt;p>我在开发这样一个agent 的时候，我传入了一些信息，我希望它能够按照我想要的格式返回数据，但是重试很多次他都不能返回，比如下面的结果&lt;/p>
&lt;p>我使用的是7B的模型，但是回答很差，当然也有很多次它是回答的格式不对，我设置的重试3次，但是基本上都不会得到想要的结果&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-3.png"
width="1123"
height="467"
srcset="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-3_hu17883738258344813768.png 480w, https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index-3_hu4505881984403634665.png 1024w"
loading="lazy"
alt="效果 7B-chat"
class="gallery-image"
data-flex-grow="240"
data-flex-basis="577px"
>&lt;/p>
&lt;p>我换了qwq-32b模型，效果好了很多&lt;/p>
&lt;p>一些模型的对比可以看：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参考&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="https://www.zata.cc/p/%E5%AF%B9%E6%AF%94%E4%BA%86%E5%87%A0%E7%A7%8D%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%9C%A8%E7%9B%B8%E5%90%8C%E4%BB%BB%E5%8A%A1%E4%B8%8B%E7%9A%84%E8%A1%A8%E7%8E%B0/" >对比了几种大模型在相同任务下的表现&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;hr>
&lt;hr>
&lt;h2 id="实战代码">实战代码
&lt;/h2>&lt;h3 id="示例代码一输出解析器-和-多次尝试">示例代码一：输出解析器 和 多次尝试
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chat_models&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StructuredOutputParser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ResponseSchema&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ValidationError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">dotenv&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dotenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_dotenv&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 密钥（替换为你的密钥，或通过环境变量获取） 这里我用的阿里云的&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">api_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;api_key&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">base_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">getenv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;base_url&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;qwq-32b&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义期望的输出结构&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response_schemas&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ResponseSchema&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;回答内容，字符串类型&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ResponseSchema&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;confidence&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;置信度，0-1之间的浮点数&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建输出解析器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StructuredOutputParser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_response_schemas&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response_schemas&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义提示模板&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请分析以下句子的情感，并以指定的 JSON 格式返回结果：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{format_instructions}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">句子：&lt;/span>&lt;span class="si">{sentence}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;sentence&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">partial_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;format_instructions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">output_parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化语言模型&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temperature&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">api_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">api_key&lt;/span> &lt;span class="p">,&lt;/span> &lt;span class="n">base_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base_url&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">streaming&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 定义解析和重试逻辑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_stable_output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sentence&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_attempts&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">llm&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">output_parser&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">attempt&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_attempts&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 调用模型并解析输出&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;sentence&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">sentence&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">result&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;尝试 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">attempt&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">max_attempts&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 失败: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">attempt&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">max_attempts&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 最后一次尝试失败，返回默认值&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;无法分析&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;confidence&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.5&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;未完成分析&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;confidence&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.0&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># 理论上不会到达这里&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 测试代码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">test_sentences&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;我今天感觉非常开心！&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;这个世界真是让人失望。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;天气很好，适合出门散步。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">sentence&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">test_sentences&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">分析句子：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sentence&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_stable_output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sentence&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;结果：&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;img src="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index.png"
width="303"
height="168"
srcset="https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index_hu18283814899988244072.png 480w, https://www.zata.cc/p/ai%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%B8%8D%E7%A8%B3%E5%AE%9A%E5%AD%98%E5%9C%A8%E8%A7%A3%E6%9E%90%E9%94%99%E8%AF%AF%E7%9A%84%E9%97%AE%E9%A2%98/images/index/index_hu11146159754895213876.png 1024w"
loading="lazy"
alt="示例一结果"
class="gallery-image"
data-flex-grow="180"
data-flex-basis="432px"
>&lt;/p>
&lt;h3 id="代码说明">代码说明
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>输出结构定义&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>使用 &lt;code>ResponseSchema&lt;/code> 定义了期望的 JSON 结构：&lt;code>answer&lt;/code>（字符串）和 &lt;code>confidence&lt;/code>（浮点数）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>提示模板&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>通过 &lt;code>PromptTemplate&lt;/code> 构造提示，确保模型知道返回 JSON 格式。&lt;/li>
&lt;li>&lt;code>output_parser.get_format_instructions()&lt;/code> 自动生成格式说明，例如：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">The output should be a markdown code block formatted as JSON:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">```json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;answer&amp;#34;: &amp;#34;string&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;confidence&amp;#34;: &amp;#34;float&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>语言模型&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>使用 &lt;code>ChatOpenAI&lt;/code>，设置 &lt;code>temperature=0.2&lt;/code> 减少随机性，&lt;code>max_tokens=200&lt;/code> 限制输出长度。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解析与重试&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;code>get_stable_output&lt;/code> 函数使用 LangChain 的链式调用（&lt;code>prompt | llm | output_parser&lt;/code>）。&lt;/li>
&lt;li>如果解析失败，会重试最多 3 次，最后返回默认值。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="具体分析---如何减少解析错误">具体分析 - 如何减少解析错误
&lt;/h2>&lt;p>在使用 LangChain 开发 AI 应用时，AI 返回数据的格式不稳定并导致解析错误是一个常见问题。这通常与大语言模型（LLM）的输出不确定性有关，尤其是当模型生成的内容未严格遵循预期结构时。以下是一些解决方案和思路，帮助你应对这一问题：&lt;/p>
&lt;h3 id="1-明确提示prompt设计">1. &lt;strong>明确提示（Prompt）设计&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：LLM 的输出很大程度上依赖于输入提示的清晰度。如果提示不够具体，模型可能会生成不一致或不符合预期的格式。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>在提示中明确要求返回特定格式，例如 JSON 或其他结构化数据。例如：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">请以 JSON 格式返回以下内容：{&amp;#34;key&amp;#34;: &amp;#34;value&amp;#34;}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>使用示例（Few-shot Learning）来引导模型输出一致的格式。例如：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">输入：分析这句话的情感
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">输出：{&amp;#34;sentence&amp;#34;: &amp;#34;这句话&amp;#34;, &amp;#34;sentiment&amp;#34;: &amp;#34;积极&amp;#34;}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>添加约束性描述，如“确保输出是可解析的 JSON，且不包含多余文本”。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="2-使用输出解析器output-parsers">2. &lt;strong>使用输出解析器（Output Parsers）&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：即使提示明确，LLM 有时仍会生成不符合预期的输出，导致解析失败。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>LangChain 提供了内置的输出解析器（如 &lt;code>StructuredOutputParser&lt;/code> 或 &lt;code>JsonOutputParser&lt;/code>），可以用来规范化模型输出。&lt;/li>
&lt;li>示例代码：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.output_parsers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">StructuredOutputParser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ResponseSchema&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response_schemas&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ResponseSchema&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;回答内容&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ResponseSchema&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;confidence&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">description&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;置信度，0-1之间的浮点数&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">StructuredOutputParser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_response_schemas&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response_schemas&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;请按照以下格式返回数据：&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_format_instructions&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>在提示中嵌入 &lt;code>parser.get_format_instructions()&lt;/code>，让模型知道预期格式。&lt;/li>
&lt;li>如果解析失败，可以捕获异常并重试。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="3-处理解析错误handle-parsing-errors">3. &lt;strong>处理解析错误（Handle Parsing Errors）&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：LLM 输出可能偶尔无法解析，例如缺少关键字段或格式错误。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>在代理（Agent）或链（Chain）中启用错误处理机制。LangChain 支持通过 &lt;code>handle_parsing_errors&lt;/code> 参数自定义错误处理逻辑。&lt;/li>
&lt;li>示例代码：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.agents&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">initialize_agent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">AgentType&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chat_models&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">initialize_agent&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tools&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temperature&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">AgentType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ZERO_SHOT_REACT_DESCRIPTION&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">handle_parsing_errors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># 自动忽略解析错误并重试&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>你也可以定义自定义回调函数来处理错误：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">custom_error_handler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;解析错误: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;发生错误，请重试或检查输入&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">agent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">initialize_agent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">handle_parsing_errors&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">custom_error_handler&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="4-后处理与验证">4. &lt;strong>后处理与验证&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：即使使用了解析器，输出仍可能不符合预期。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>在接收到模型输出后，使用 Python 的 &lt;code>try-except&lt;/code> 块或数据验证库（如 &lt;code>pydantic&lt;/code>）检查和修复数据。&lt;/li>
&lt;li>示例代码：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pydantic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">BaseModel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ValidationError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BaseModel&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">answer&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">confidence&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">float&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">parse_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">raw_output&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">parse_raw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">raw_output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="n">ValidationError&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;验证失败: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">answer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;默认回答&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">confidence&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.5&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 回退值&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">raw_output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;{&amp;#34;answer&amp;#34;: &amp;#34;是的&amp;#34;, &amp;#34;confidence&amp;#34;: &amp;#34;0.9&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">parse_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">raw_output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="5-调整模型参数">5. &lt;strong>调整模型参数&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：模型的随机性（temperature）或输出长度（max_tokens）可能导致格式不稳定。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>降低 &lt;code>temperature&lt;/code>（如设为 0 或 0.2），减少输出的随机性。&lt;/li>
&lt;li>设置合理的 &lt;code>max_tokens&lt;/code>，避免输出被截断。&lt;/li>
&lt;li>示例：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ChatOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temperature&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="6-重试机制">6. &lt;strong>重试机制&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>问题原因&lt;/strong>：单次调用可能失败，但多次尝试可能成功。&lt;/li>
&lt;li>&lt;strong>解决方法&lt;/strong>：
&lt;ul>
&lt;li>实现一个简单的重试逻辑，确保在解析失败时重新请求。&lt;/li>
&lt;li>示例代码：
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">get_stable_output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_attempts&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">max_attempts&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">parser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;无法获取稳定输出&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="总结">总结
&lt;/h3>&lt;p>通过优化提示、使用 LangChain 的输出解析器、启用错误处理机制、后处理验证数据以及调整模型参数，可以有效解决 AI 返回数据格式不稳定和解析错误的问题。根据具体应用场景，你可能需要结合多种方法。例如，对于需要高可靠性的生产环境，建议同时使用解析器和重试机制；对于快速原型开发，则可以通过清晰的提示和简单后处理解决问题。&lt;/p></description></item><item><title>全量解码与增量解码：原理、区别以及应用</title><link>https://www.zata.cc/p/%E5%85%A8%E9%87%8F%E8%A7%A3%E7%A0%81%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%A7%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%8C%BA%E5%88%AB%E4%BB%A5%E5%8F%8A%E5%BA%94%E7%94%A8/</link><pubDate>Fri, 14 Mar 2025 17:49:58 +0800</pubDate><guid>https://www.zata.cc/p/%E5%85%A8%E9%87%8F%E8%A7%A3%E7%A0%81%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%A7%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%8C%BA%E5%88%AB%E4%BB%A5%E5%8F%8A%E5%BA%94%E7%94%A8/</guid><description>&lt;img src="https://www.zata.cc/p/%E5%85%A8%E9%87%8F%E8%A7%A3%E7%A0%81%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%A7%A3%E7%A0%81%E5%8E%9F%E7%90%86%E5%8C%BA%E5%88%AB%E4%BB%A5%E5%8F%8A%E5%BA%94%E7%94%A8/images/index/index.png" alt="Featured image of post 全量解码与增量解码：原理、区别以及应用" />&lt;h2 id="引言">&lt;strong>引言&lt;/strong>
&lt;/h2>&lt;p>在自然语言处理（NLP）领域，尤其是大语言模型（LLM）中，解码（decoding）是模型生成输出的核心步骤。你可能好奇：为什么模型接收到完整输入后，生成回复时却不是一次性吐出整个句子？答案在于解码方式的不同。本教程将详细讲解两种主要解码方式——&lt;strong>全量解码&lt;/strong>和&lt;strong>增量解码&lt;/strong>，包括它们的定义、原理、优缺点、区别，以及在大语言模型中的应用。&lt;/p>
&lt;hr>
&lt;h2 id="第一部分什么是解码">&lt;strong>第一部分：什么是解码？&lt;/strong>
&lt;/h2>&lt;h3 id="解码的定义">&lt;strong>解码的定义&lt;/strong>
&lt;/h3>&lt;p>解码是大语言模型根据输入生成输出的过程。简单来说：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>输入&lt;/strong>：用户给模型一个问题或句子（比如“请解释一下AI”）。&lt;/li>
&lt;li>&lt;strong>解码&lt;/strong>：模型根据内部表示（概率分布或隐藏状态），逐步或一次性生成回答（比如“AI是一种技术”）。&lt;/li>
&lt;/ul>
&lt;h3 id="输入与解码的区别">&lt;strong>输入与解码的区别&lt;/strong>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>输入阶段&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>用户通常是一次性把完整输入（比如“我不太懂，模型输入不是一次性就输入进去了吗”）交给模型。&lt;/li>
&lt;li>模型会通过编码器（如果是Encoder-Decoder架构，如T5）或直接通过自回归方式（像GPT）处理整个输入，生成某种内部表示（比如隐藏状态或上下文向量）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>解码阶段&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>解码是模型根据输入生成输出的过程，也就是从内部表示逐步生成文本的过程。&lt;/li>
&lt;li>即使输入是一次性给的，模型生成输出时并不一定一次性吐出整个答案，而是需要一步步决定每个词（或标记），这就是解码方式的重点。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>换句话说，&lt;strong>输入是一次性给模型的，但输出（解码）可以是逐步生成的&lt;/strong>。全量解码和增量解码的区别在于：模型是如何基于输入一步步生成输出的。&lt;/p>
&lt;h3 id="为什么解码不是一次性完成">&lt;strong>为什么解码不是一次性完成？&lt;/strong>
&lt;/h3>&lt;p>大语言模型（尤其是基于Transformer的模型）通常是自回归的，意味着它们生成输出的方式是序列化的：每生成一个词，模型会把这个词加入上下文，再预测下一个词。这种设计有以下原因：&lt;/p>
&lt;p>&lt;strong>1. 语言的序列性质：&lt;/strong>
自然语言是有序的，比如“我喜欢你”和“你喜欢我”意思完全不同。模型需要逐词生成，才能保证语法和语义的连贯性。
如果一次性生成整个句子，模型需要同时决定所有词，这在计算上非常复杂，且难以保证一致性。&lt;/p>
&lt;p>&lt;strong>2. 模型架构限制：&lt;/strong>
GPT这类模型是自回归的，当前词的预测依赖于之前生成的词。它们没有能力一次性输出整个序列，而是必须逐步构建。
即使是Encoder-Decoder模型（如T5），解码器在生成时也是逐步进行的。&lt;/p>
&lt;p>&lt;strong>3. 概率分布：&lt;/strong>
模型每一步会输出一个概率分布（比如下一个词可能是“我”0.7、“你”0.2、“他”0.1），然后根据解码策略（贪心、采样等）选择一个词。
这个过程天然是增量的，因为每一步的选择会影响下一步的概率。&lt;/p>
&lt;hr>
&lt;h2 id="第二部分全量解码-full-decoding">&lt;strong>第二部分：全量解码 (Full Decoding)&lt;/strong>
&lt;/h2>&lt;h3 id="定义">&lt;strong>定义&lt;/strong>
&lt;/h3>&lt;p>全量解码是指模型一次性生成或评估整个输出序列，试图找到全局最优解。它强调对所有可能输出的全面考虑。&lt;/p>
&lt;h3 id="工作原理">&lt;strong>工作原理&lt;/strong>
&lt;/h3>&lt;ol>
&lt;li>接收完整输入（比如“你好”）。&lt;/li>
&lt;li>计算所有可能的输出序列及其概率：
&lt;ul>
&lt;li>“Hello” (0.9)&lt;/li>
&lt;li>“Hi there” (0.7)&lt;/li>
&lt;li>“Greetings” (0.5)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>选择得分最高的序列（比如“Hello”）作为输出。&lt;/li>
&lt;/ol>
&lt;h3 id="典型算法">&lt;strong>典型算法&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>束搜索 (Beam Search)&lt;/strong>：维护一组候选序列（束），每步保留得分最高的前k个，最终选最佳序列。&lt;/li>
&lt;li>&lt;strong>维特比算法 (Viterbi Algorithm)&lt;/strong>：在隐马尔可夫模型中寻找全局最优路径。&lt;/li>
&lt;li>（注：真正的全量解码需要遍历所有可能性，但在实践中因计算量太大，通常用近似方法。）&lt;/li>
&lt;/ul>
&lt;h3 id="优点">&lt;strong>优点&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>全局最优&lt;/strong>：考虑整个序列的上下文，结果更连贯。&lt;/li>
&lt;li>&lt;strong>高质量&lt;/strong>：适合需要精确输出的任务。&lt;/li>
&lt;/ul>
&lt;h3 id="缺点">&lt;strong>缺点&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>计算复杂度高&lt;/strong>：可能的序列组合随长度指数增长。&lt;/li>
&lt;li>&lt;strong>实时性差&lt;/strong>：需要等待完整计算，无法边生成边输出。&lt;/li>
&lt;/ul>
&lt;h3 id="应用场景">&lt;strong>应用场景&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>机器翻译（追求高质量翻译）。&lt;/li>
&lt;li>文本摘要（需要全局一致性）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="第三部分增量解码-incremental-decoding">&lt;strong>第三部分：增量解码 (Incremental Decoding)&lt;/strong>
&lt;/h2>&lt;h3 id="定义-1">&lt;strong>定义&lt;/strong>
&lt;/h3>&lt;p>增量解码是指模型逐步生成输出序列，每一步只基于当前信息预测下一个词，而不依赖未来全局信息。它是一种“边解码边生成”的方式。&lt;/p>
&lt;h3 id="工作原理-1">&lt;strong>工作原理&lt;/strong>
&lt;/h3>&lt;ol>
&lt;li>接收输入（比如“你好”）。&lt;/li>
&lt;li>逐步生成输出：
&lt;ul>
&lt;li>第一步：输出“Hello”。&lt;/li>
&lt;li>第二步：根据“Hello”决定是否继续（如停止或加词）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>重复直到生成完整序列或达到终止条件。&lt;/li>
&lt;/ol>
&lt;h3 id="典型算法-1">&lt;strong>典型算法&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>贪心解码 (Greedy Decoding)&lt;/strong>：每步选择概率最高的词。&lt;/li>
&lt;li>&lt;strong>采样解码 (Sampling)&lt;/strong>：根据概率分布随机选择词（如Top-k或Top-p采样）。&lt;/li>
&lt;/ul>
&lt;h3 id="优点-1">&lt;strong>优点&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>高效&lt;/strong>：每步计算量小，适合快速响应。&lt;/li>
&lt;li>&lt;strong>实时性强&lt;/strong>：边输入边输出，用户体验好。&lt;/li>
&lt;li>&lt;strong>资源占用低&lt;/strong>：无需存储所有可能性。&lt;/li>
&lt;/ul>
&lt;h3 id="缺点-1">&lt;strong>缺点&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>局部最优风险&lt;/strong>：每步只看当前，可能偏离全局最优。&lt;/li>
&lt;li>&lt;strong>上下文不足&lt;/strong>：无法利用序列后部的完整信息。&lt;/li>
&lt;/ul>
&lt;h3 id="应用场景-1">&lt;strong>应用场景&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>实时对话（聊天机器人）。&lt;/li>
&lt;li>流式语音转文字（边说边转录）。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="第四部分全量解码与增量解码的对比">&lt;strong>第四部分：全量解码与增量解码的对比&lt;/strong>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>特性&lt;/strong>&lt;/th>
&lt;th>&lt;strong>全量解码&lt;/strong>&lt;/th>
&lt;th>&lt;strong>增量解码&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>生成方式&lt;/strong>&lt;/td>
&lt;td>一次性生成整个序列&lt;/td>
&lt;td>逐步生成序列&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>优化目标&lt;/strong>&lt;/td>
&lt;td>全局最优&lt;/td>
&lt;td>局部最优&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>计算复杂度&lt;/strong>&lt;/td>
&lt;td>高（指数级）&lt;/td>
&lt;td>低（线性或接近线性）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>实时性&lt;/strong>&lt;/td>
&lt;td>差（需完整输入和计算）&lt;/td>
&lt;td>强（边输入边输出）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>上下文依赖&lt;/strong>&lt;/td>
&lt;td>依赖整个序列&lt;/td>
&lt;td>只依赖当前和之前&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>典型算法&lt;/strong>&lt;/td>
&lt;td>束搜索、维特比算法&lt;/td>
&lt;td>贪心解码、采样解码&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="举例说明">&lt;strong>举例说明&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>输入&lt;/strong>：“I love you”。&lt;/li>
&lt;li>&lt;strong>全量解码&lt;/strong>：
&lt;ul>
&lt;li>计算所有可能翻译：
&lt;ul>
&lt;li>“我爱你” (0.9)&lt;/li>
&lt;li>“我喜欢你” (0.6)&lt;/li>
&lt;li>“我爱你们” (0.3)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>输出：“我爱你”。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>增量解码&lt;/strong>：
&lt;ul>
&lt;li>逐步生成：
&lt;ul>
&lt;li>“我” → “爱” → “你”。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>输出：“我爱你”。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="举例说明2">&lt;strong>举例说明2&lt;/strong>
&lt;/h3>&lt;p>假设你输入：“请解释一下AI”。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>输入阶段&lt;/strong>：模型一次性接收“请解释一下AI”，并理解其含义（通过编码器或自回归上下文）。&lt;/li>
&lt;li>&lt;strong>解码阶段&lt;/strong>：
&lt;ul>
&lt;li>&lt;strong>增量解码&lt;/strong>（实际常用）：
&lt;ul>
&lt;li>模型先输出“AI”，然后根据“AI”预测“是”，再根据“AI是”预测“一种”，逐步生成“AI是一种技术”。&lt;/li>
&lt;li>你可能会看到我逐词回复，像这样：AI→是→一种→技术。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>全量解码&lt;/strong>（理论上）：
&lt;ul>
&lt;li>模型计算所有可能的回答：
&lt;ul>
&lt;li>“AI是一种技术” (概率 0.9)&lt;/li>
&lt;li>“AI是人工智能” (概率 0.85)&lt;/li>
&lt;li>“AI很复杂” (概率 0.6)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>然后一次性输出“AI是一种技术”。&lt;/li>
&lt;li>但实际上，这需要巨大计算量，通常只用近似方法（如束搜索）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="第五部分大语言模型中的解码方式">&lt;strong>第五部分：大语言模型中的解码方式&lt;/strong>
&lt;/h2>&lt;h3 id="现状">&lt;strong>现状&lt;/strong>
&lt;/h3>&lt;p>现代大语言模型（如GPT、ChatGPT、Grok）主要基于&lt;strong>自回归架构&lt;/strong>，天然倾向于&lt;strong>增量解码&lt;/strong>，但会根据任务搭配不同策略。&lt;/p>
&lt;h3 id="常用解码策略">&lt;strong>常用解码策略&lt;/strong>
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>贪心解码&lt;/strong>：
&lt;ul>
&lt;li>每步选概率最高的词。&lt;/li>
&lt;li>简单高效，但输出单一。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>束搜索&lt;/strong>：
&lt;ul>
&lt;li>近似全量解码，保留多个候选序列。&lt;/li>
&lt;li>用于高质量生成（如翻译）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>采样解码&lt;/strong>：
&lt;ul>
&lt;li>&lt;strong>Top-k采样&lt;/strong>：从前k个高概率词中随机选。&lt;/li>
&lt;li>&lt;strong>Top-p采样&lt;/strong>：从累计概率达p的词中选。&lt;/li>
&lt;li>增强多样性，常见于对话模型。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>自回归生成&lt;/strong>：
&lt;ul>
&lt;li>逐步生成，每步依赖前文。&lt;/li>
&lt;li>是增量解码的基础框架。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="为什么偏向增量解码">&lt;strong>为什么偏向增量解码？&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>实时性&lt;/strong>：对话中需快速响应。&lt;/li>
&lt;li>&lt;strong>效率&lt;/strong>：避免指数级计算。&lt;/li>
&lt;li>&lt;strong>架构&lt;/strong>：自回归模型逐词生成。&lt;/li>
&lt;/ul>
&lt;h3 id="全量解码的应用">&lt;strong>全量解码的应用&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>在翻译、摘要等任务中，束搜索常用于追求高质量输出。&lt;/li>
&lt;li>但真正的全量解码（遍历所有序列）因计算成本过高，几乎不用。&lt;/li>
&lt;/ul>
&lt;h3 id="实际例子">&lt;strong>实际例子&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>ChatGPT&lt;/strong>：用Top-p采样+自回归，生成自然多样的回复。&lt;/li>
&lt;li>&lt;strong>Grok&lt;/strong>：类似增量解码，逐词输出，追求实时性。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="第六部分常见疑问解答">&lt;strong>第六部分：常见疑问解答&lt;/strong>
&lt;/h2>&lt;h3 id="q1输入不是一次性给的吗为什么解码不是一次性完成">&lt;strong>Q1：输入不是一次性给的吗？为什么解码不是一次性完成？&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>答&lt;/strong>：
&lt;ul>
&lt;li>输入确实是一次性给模型的，模型会理解完整上下文。&lt;/li>
&lt;li>但解码是生成输出的过程，自回归模型需要逐词预测，因为：
&lt;ol>
&lt;li>语言的序列性要求逐步构建。&lt;/li>
&lt;li>每步预测依赖前文输出。&lt;/li>
&lt;li>一次性生成所有词的组合计算量太大。&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="q2大语言模型用哪种解码">&lt;strong>Q2：大语言模型用哪种解码？&lt;/strong>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>答&lt;/strong>：
&lt;ul>
&lt;li>主要用&lt;strong>增量解码&lt;/strong>（自回归+采样），适合实时对话。&lt;/li>
&lt;li>&lt;strong>全量解码&lt;/strong>（如束搜索）用于特定任务，但多为近似形式。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="第七部分总结">&lt;strong>第七部分：总结&lt;/strong>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>全量解码&lt;/strong>：追求全局最优，计算成本高，适合高质量任务。&lt;/li>
&lt;li>&lt;strong>增量解码&lt;/strong>：高效实时，适合对话和流式应用。&lt;/li>
&lt;li>&lt;strong>大语言模型&lt;/strong>：以增量解码为主（如采样），灵活搭配策略，平衡质量与效率。&lt;/li>
&lt;/ul>
&lt;p>通过理解这两种解码方式，你可以更好地把握大语言模型的工作机制，并根据需求选择合适的策略。&lt;/p>
&lt;hr></description></item><item><title>相同LLM不同提示词的对比</title><link>https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/</link><pubDate>Fri, 07 Mar 2025 16:10:38 +0800</pubDate><guid>https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/</guid><description>&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index.png" alt="Featured image of post 相同LLM不同提示词的对比" />&lt;h3 id="提示词列表">提示词列表
&lt;/h3>&lt;p>&lt;span style="color:red">有最好的模型就用最好的模型，模型与模型之间的差距比提示词大很多&lt;/span>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>提示词描述&lt;/th>
&lt;th>提示词—类1&lt;/th>
&lt;th>提示词-类2&lt;/th>
&lt;th>提示词-类3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a class="link" href="#%e6%8e%a8%e5%b9%bf%e6%9f%90%e4%b8%80%e4%b8%aa%e4%ba%a7%e5%93%81" >推广某一个产品&lt;/a>&lt;/td>
&lt;td>我想推广公司的新产品。我的公司名为百炼，新产品名为 Zephyr Z9，是一款轻薄便携的手机。帮我创建一条微博帖子。&lt;/td>
&lt;td>请为我司“百炼”最新推出的“Zephyr Z9”轻薄便携手机设计一条吸引眼球的微博推广帖。内容需彰显Zephyr Z9的独特卖点，如极致轻薄设计、高性能配置及用户便利性，同时融入创意元素以提升观众兴趣和互动意愿。记得提及品牌声誉，激发受众好奇心，引导他们探索更多产品信息或直接进行购买。贴文应简洁有力，符合微博平台的风格与字数限制，适宜社交媒体传播。&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a class="link" href="#cursor%e5%92%8ctrae%e7%9a%84%e5%af%b9%e6%af%94-sonnet3.5" >cursor和trae的对比&lt;/a>&lt;/td>
&lt;td>请你完成一个演讲ppt，内容是什么是MoE，内容使用html生成&lt;/td>
&lt;td>请你完成一个演讲ppt，内容是什么是MoE，内容使用html生成&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="推广某一个产品">推广某一个产品
&lt;/h4>&lt;h5 id="qwen">Qwen
&lt;/h5>&lt;p>&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index.png"
width="2000"
height="400"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index_hu2190330025405746501.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index_hu11168576615713374845.png 1024w"
loading="lazy"
alt="提示词1"
class="gallery-image"
data-flex-grow="500"
data-flex-basis="1200px"
>
&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-1.png"
width="929"
height="404"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-1_hu8369194748862378322.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-1_hu7157378270562470066.png 1024w"
loading="lazy"
alt="提示词2"
class="gallery-image"
data-flex-grow="229"
data-flex-basis="551px"
>&lt;/p>
&lt;h5 id="grok">Grok
&lt;/h5>&lt;p>&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-3.png"
width="847"
height="392"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-3_hu1477604707414407044.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-3_hu13172026281099858233.png 1024w"
loading="lazy"
alt="提示词1"
class="gallery-image"
data-flex-grow="216"
data-flex-basis="518px"
>
&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-2.png"
width="865"
height="502"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-2_hu7257550114217433468.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-2_hu7242175084615421860.png 1024w"
loading="lazy"
alt="提示词2"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="413px"
>&lt;/p>
&lt;h4 id="cursor和trae的对比-sonnet35">cursor和trae的对比-sonnet3.5
&lt;/h4>&lt;p>&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-7.png"
width="960"
height="1032"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-7_hu18079420075947112548.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-7_hu11796955214251668775.png 1024w"
loading="lazy"
alt="alt text"
class="gallery-image"
data-flex-grow="93"
data-flex-basis="223px"
>
&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-4.png"
width="634"
height="778"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-4_hu16999005047397157839.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-4_hu9100620532590124576.png 1024w"
loading="lazy"
alt="trae"
class="gallery-image"
data-flex-grow="81"
data-flex-basis="195px"
>&lt;/p>
&lt;h4 id="cursor和trae的对比-sonnet37">cursor和trae的对比-sonnet3.7
&lt;/h4>&lt;p>差不多，看来主要是模型的问题，模型好，问出来的答案效果就是会更好&lt;/p>
&lt;p>&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-5.png"
width="1586"
height="888"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-5_hu10327266523981670563.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-5_hu12376880603944995376.png 1024w"
loading="lazy"
alt="cursor"
class="gallery-image"
data-flex-grow="178"
data-flex-basis="428px"
>
&lt;img src="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-6.png"
width="1667"
height="919"
srcset="https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-6_hu15388265612167122469.png 480w, https://www.zata.cc/p/%E7%9B%B8%E5%90%8Cllm%E4%B8%8D%E5%90%8C%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%9A%84%E5%AF%B9%E6%AF%94/images/index/index-6_hu14507647080577586441.png 1024w"
loading="lazy"
alt="trae"
class="gallery-image"
data-flex-grow="181"
data-flex-basis="435px"
>&lt;/p></description></item></channel></rss>