<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>开源 on MyBrew</title>
    <link>https://aibrew.ai/zh/tags/%E5%BC%80%E6%BA%90/</link>
    <description>Recent content in 开源 on MyBrew</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 27 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://aibrew.ai/zh/tags/%E5%BC%80%E6%BA%90/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>n8n vs Dify：吸收一个，跳过一个</title>
      <link>https://aibrew.ai/zh/2026/05/n8n-vs-dify%E5%90%B8%E6%94%B6%E4%B8%80%E4%B8%AA%E8%B7%B3%E8%BF%87%E4%B8%80%E4%B8%AA/</link>
      <pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate>
      <guid>https://aibrew.ai/zh/2026/05/n8n-vs-dify%E5%90%B8%E6%94%B6%E4%B8%80%E4%B8%AA%E8%B7%B3%E8%BF%87%E4%B8%80%E4%B8%AA/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话&lt;/strong&gt; — n8n 和 Dify 在自托管 AI 评测里经常并列出现，但它们想占据栈里完全不同的层。对照一套自建 AI 系统认真评估之后，我们吸收了 n8n，跳过了 Dify。决策归到同一个问题——&lt;em&gt;&amp;ldquo;它想占据哪一层，而我是不是已经拥有那一层？&amp;quot;&lt;/em&gt;——两个平台的答案正好相反。这篇把决策框架完整摊开，你可以对着自己的栈跑一遍同样的评估。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;为什么这个对比在-2026-年还值得写&#34;&gt;为什么这个对比在 2026 年还值得写&lt;/h2&gt;
&lt;p&gt;半年前问&amp;quot;哪个 OSS AI 平台应该自己跑&amp;rdquo;，认真的答案大概只有三个。今天有几十个，而且彼此功能严重重叠。Dify 和 n8n 在评估清单里几乎总是并列出现——都用 TypeScript 写、都能 Docker 自托管、都有可视化编辑器、都能调 LLM。&lt;/p&gt;
&lt;p&gt;这种表面的相似很有误导性。&lt;strong&gt;它们想占据的栈层完全不同。&lt;/strong&gt; 把它们当替代品评估是一个范畴错误，代价就是一周的部署+返工。&lt;/p&gt;
&lt;p&gt;对照已有的自托管栈认真评估后，我们得到的结论是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dify&lt;/strong&gt; 想当 orchestrator。如果你已经有一个 orchestrator，Dify 一无是处。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;n8n&lt;/strong&gt; 想当执行层（execution layer）。如果你还没有执行层，n8n 是市面上最好的开箱选项之一。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;dify-是什么&#34;&gt;Dify 是什么&lt;/h2&gt;
&lt;p&gt;Dify 是开源 LLM 应用开发平台（Apache 2.0，GitHub 55k+ ⭐）。它的卖点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可视化工作流编辑器&lt;/strong&gt; — 拖节点构建 AI 流水线&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内置 RAG&lt;/strong&gt; — 上传文档，得到一个可查询的知识库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent 构建器&lt;/strong&gt; — 预打包的 prompt 模板 + tool calling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型网关&lt;/strong&gt; — 在 OpenAI / Anthropic / DeepSeek / 本地模型之上抽象一层&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可观测性 dashboard&lt;/strong&gt; — 请求日志、延迟、成本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2025–2026 年 Dify 用自研的 &amp;ldquo;Beehive Runtime&amp;rdquo; 替换了底层 LangChain，工程实现确实扎实。这个产品本身是认真做的。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>一句话</strong> — n8n 和 Dify 在自托管 AI 评测里经常并列出现，但它们想占据栈里完全不同的层。对照一套自建 AI 系统认真评估之后，我们吸收了 n8n，跳过了 Dify。决策归到同一个问题——<em>&ldquo;它想占据哪一层，而我是不是已经拥有那一层？&quot;</em>——两个平台的答案正好相反。这篇把决策框架完整摊开，你可以对着自己的栈跑一遍同样的评估。</p>
</blockquote>
<hr>
<h2 id="为什么这个对比在-2026-年还值得写">为什么这个对比在 2026 年还值得写</h2>
<p>半年前问&quot;哪个 OSS AI 平台应该自己跑&rdquo;，认真的答案大概只有三个。今天有几十个，而且彼此功能严重重叠。Dify 和 n8n 在评估清单里几乎总是并列出现——都用 TypeScript 写、都能 Docker 自托管、都有可视化编辑器、都能调 LLM。</p>
<p>这种表面的相似很有误导性。<strong>它们想占据的栈层完全不同。</strong> 把它们当替代品评估是一个范畴错误，代价就是一周的部署+返工。</p>
<p>对照已有的自托管栈认真评估后，我们得到的结论是：</p>
<ul>
<li><strong>Dify</strong> 想当 orchestrator。如果你已经有一个 orchestrator，Dify 一无是处。</li>
<li><strong>n8n</strong> 想当执行层（execution layer）。如果你还没有执行层，n8n 是市面上最好的开箱选项之一。</li>
</ul>
<hr>
<h2 id="dify-是什么">Dify 是什么</h2>
<p>Dify 是开源 LLM 应用开发平台（Apache 2.0，GitHub 55k+ ⭐）。它的卖点：</p>
<ul>
<li><strong>可视化工作流编辑器</strong> — 拖节点构建 AI 流水线</li>
<li><strong>内置 RAG</strong> — 上传文档，得到一个可查询的知识库</li>
<li><strong>Agent 构建器</strong> — 预打包的 prompt 模板 + tool calling</li>
<li><strong>模型网关</strong> — 在 OpenAI / Anthropic / DeepSeek / 本地模型之上抽象一层</li>
<li><strong>可观测性 dashboard</strong> — 请求日志、延迟、成本</li>
</ul>
<p>2025–2026 年 Dify 用自研的 &ldquo;Beehive Runtime&rdquo; 替换了底层 LangChain，工程实现确实扎实。这个产品本身是认真做的。</p>
<p>它的目标用户：想发布一个 AI 应用、但<strong>不想写代码</strong>、也不想自己维护各个基础设施组件的人。</p>
<p>同族产品：Flowise、Langflow、FastGPT。这些都是&quot;平台优先&quot;的 AI 构建工具。</p>
<hr>
<h2 id="n8n-是什么">n8n 是什么</h2>
<p>n8n 是开源工作流自动化（GitHub 162k+ ⭐）。可以理解成自托管版 Zapier，但有写代码的逃生口。</p>
<ul>
<li><strong>400+ SaaS 连接器</strong> — Notion、Slack、Stripe、Telegram、GitHub 等等</li>
<li><strong>Trigger → Action → Condition</strong> 可视化工作流编辑器</li>
<li><strong>Webhook</strong> — 接收外部事件并路由到动作</li>
<li><strong>轮询触发器</strong> — RSS、定时任务、文件监听</li>
<li><strong>原生重试 / 错误处理</strong> — 每个节点都有重试策略</li>
</ul>
<p>n8n <strong>不</strong>想成为 LLM 平台。它有调用 LLM 的节点，但核心定位是&quot;连接任意 SaaS 系统、对事件作出反应&quot;。</p>
<p>这个区分很关键。n8n 是 <strong>plumbing-first</strong>，AI 节点是可选项。Dify 是 <strong>AI-first</strong>，其他所有东西都折叠进来。</p>
<hr>
<h2 id="关键判断题替换还是吸收">关键判断题：替换还是吸收？</h2>
<p>评估任何平台时，正确的问题<strong>不是</strong>&ldquo;它好不好？&quot;。正确的问题是 <em>&ldquo;它想占据我栈里哪一层，而我是不是已经拥有那一层？&rdquo;</em></p>
<p>答案只有两种：</p>
<ul>
<li><strong>替换（Replace）</strong>：平台想占据你已有的层。引入它意味着推掉能跑的代码，换成一个更不灵活的黑盒等价物。</li>
<li><strong>吸收（Absorb）</strong>：平台想占据你还没有的层。引入它只是填补一个空白，不和任何东西竞争。</li>
</ul>
<p>这个框架把原本会拖好几天的模糊辩论，压成了几个清晰快速的决定。文章剩下的部分把这个框架对照两个平台各跑一遍。</p>
<hr>
<h2 id="dify-在栈里想占据的位置">Dify 在栈里想占据的位置</h2>
<p>Dify 想一口气占据五层。下面把每一层对照&quot;一套已经有 code-based orchestrator 的栈&rdquo;（任何 agent harness——Claude Code、LangGraph、或自研）：</p>
<table>
  <thead>
      <tr>
          <th>Dify 占据的层</th>
          <th>你还没有这一层</th>
          <th>你已经有这一层</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>可视化工作流编排</td>
          <td>Dify 几天给你一个精致 UI</td>
          <td>逼你把能跑的代码迁进拖拽节点</td>
      </tr>
      <tr>
          <td>RAG 流水线</td>
          <td>内置，开箱即用的知识库</td>
          <td>通常没有自定义 RAG 灵活；chunking、embedding、混合搜索都更难调</td>
      </tr>
      <tr>
          <td>Agent 构建器</td>
          <td>预打包的模板 + tool slot</td>
          <td>真正的 agent loop + 多步推理，比 prompt 模板包装强</td>
      </tr>
      <tr>
          <td>模型网关</td>
          <td>一层抽象切换 provider</td>
          <td>code-based orchestrator 里一个环境变量就行</td>
      </tr>
      <tr>
          <td>可观测性 dashboard</td>
          <td>一等公民的请求日志和成本追踪</td>
          <td>现有 telemetry 栈（Prometheus、OpenTelemetry、自建日志）通常更深</td>
      </tr>
  </tbody>
</table>
<p>更深一层的洞察：<strong>Dify 是为&quot;不写代码但想发布 AI 应用&quot;的人造的。</strong> 这是一个真实存在的市场，Dify 服务得也不错。但只要你已经有一个 code-based orchestrator 在跑，引入 Dify 就意味着推掉能跑的组件，换成不灵活的等价物，只是为了塞进一个可视化 UI。净成本：一周迁移、灵活性全损、零新增能力。</p>
<p><strong>我们的结论：跳过。</strong> 不是因为 Dify 差，而是没有空白给它填。</p>
<hr>
<h2 id="n8n-在栈里想占据的位置">n8n 在栈里想占据的位置</h2>
<p>n8n 的定位结构性地不同。它不想当大脑，它想当电线。</p>
<p>n8n 的四个核心能力，对照一套典型的自建 AI 栈：</p>
<table>
  <thead>
      <tr>
          <th>n8n 提供的能力</th>
          <th>你还没有这一层</th>
          <th>你已经有这一层</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Webhook 触发器</td>
          <td>系统第一批事件驱动入口</td>
          <td>和时间驱动（cron）互补，无冲突</td>
      </tr>
      <tr>
          <td>400+ SaaS 连接器</td>
          <td>省掉几周手写 Notion / Slack / Stripe 等 API 客户端</td>
          <td>仍然有用——给你没有的连接器，不和已有的竞争</td>
      </tr>
      <tr>
          <td>内置重试 + 状态机</td>
          <td>开箱的成熟重试 / 错误处理</td>
          <td>用经过生产验证的默认值替换手写 try/except</td>
      </tr>
      <tr>
          <td>RSS / 轮询触发器</td>
          <td>不走 OAuth 的频道监控</td>
          <td>纯增量——大多数栈里没有这一层</td>
      </tr>
  </tbody>
</table>
<p>关键观察：<strong>这四个能力没有一个和已有 orchestrator 通常占据的层竞争。</strong> 它们坐在下面。它们填的是 code-based orchestrator 单独存在时仍然会有的空白：</p>
<ul>
<li>事件驱动入口（大多数自建栈只有 cron）</li>
<li>现成的 SaaS 适配器（大多数自建栈没有通用适配器层）</li>
<li>现成的重试语义（大多数自建栈是手写错误处理）</li>
<li>公开 RSS 轮询，绕过协议锁定的服务比如 YouTube（大多数自建栈没有）</li>
</ul>
<p><strong>我们的结论：吸收。</strong> n8n 成为一个依赖——一个维护良好、文档齐全、生产验证过的执行层——不和任何能跑的东西竞争。</p>
<hr>
<h2 id="浮现出的架构模式">浮现出的架构模式</h2>
<p>两个决定之后形成的心智模型：</p>
<pre tabindex="0"><code>                       Orchestrator（决策，判断）
                       ─────────────────────────────────
                                   │
            ┌──────────────────────┼──────────────────────┐
            │                      │                      │
            ▼                      ▼                      ▼
        知识层                  工具接口                 时间触发器
       （你的 RAG）            （你的 API /             （cron）
                                MCP server）
                                   │
                                   ▼
                       ┌────────────────────────┐
                       │  n8n（执行层）          │
                       │  ─────────────────────  │
                       │  • webhook            │
                       │  • SaaS 适配器         │
                       │  • RSS / 轮询          │
                       │  • 重试 / 状态机        │
                       └────────────────────────┘
</code></pre><p>给自己设的硬约束：<strong>n8n 只能当执行层，永远不当决策层。</strong> n8n workflow 里不放任何 AI 判断。n8n 负责接收信号、转发、失败重试、回传结果。所有判断留在 orchestrator 手里。</p>
<p>为什么需要这个约束？因为 n8n 有 LLM 节点。你<em>能</em>在 workflow 里塞一个&quot;用 GPT 总结这封邮件&quot;的调用。一旦这么干，你的推理就被切成两半——一部分在 orchestrator 的 prompt 上下文里，一部分在不透明的 n8n 节点里——于是你有两个系统在做决策，但没有共享记忆。这就是把简单 workflow 拖进维护噩梦的失败模式。</p>
<p>把 n8n 严格限定在 plumbing 角色，是让这个架构跑得起来的纪律。</p>
<hr>
<h2 id="部署-n8n-前值得知道的三个踩坑">部署 n8n 前值得知道的三个踩坑</h2>
<p>第一天跑 n8n 时常让人意外的三件事：</p>
<p><strong>1. REST API 不支持 PATCH archive workflow。</strong> API 能创建和读 workflow，但不能通过 API 删除或归档。清理必须走 Web UI。如果你计划动态生成 workflow，需要为手动清理留时间，或者直接写 SQLite 数据库。（在 n8n 2.22+ 修了，2.21.x 线还有这个限制。）</p>
<p><strong>2. Webhook 路径全局唯一，即使 workflow 未激活也占着。</strong> 删掉一个 workflow，但 webhook 路径还留着注册表里，挡住任何新 workflow 复用这个路径。把 webhook 命名空间当成你必须管理的扁平全局空间。从第一天起就用 workflow 名做路径前缀。</p>
<p><strong>3. API key scope 不包含 <code>workflow:execute</code>。</strong> API 能读 workflow，但不能编程触发——webhook 是唯一的执行入口。对大多数架构这其实是对的（webhook <strong>就是</strong>集成点），但如果你期待&quot;用 API 按需启动一次 workflow&quot;，n8n 不是这个思路。</p>
<hr>
<h2 id="什么时候你应该选-dify">什么时候你应该选 Dify</h2>
<p>公平地说：Dify 是合适的工具，当你：</p>
<ul>
<li><strong>不想写代码</strong>，也不想单独维护各个基础设施组件。</li>
<li>需要一个<strong>精致的 UI</strong> 给非技术用户去构建和调整 workflow。</li>
<li>想要<strong>一站式托管体验</strong>（RAG + 模型网关 + 可观测性 + UI），而且这些组件你还没串起来。</li>
<li>在为小团队搭一个<strong>面向客户的 chatbot</strong>，发布速度比架构灵活性重要。</li>
</ul>
<p>只要其中任何一条描述了你，Dify 是认真的选择，我们不会反对。</p>
<hr>
<h2 id="什么时候你应该选-n8n">什么时候你应该选 n8n</h2>
<p>n8n 是合适的工具，当你：</p>
<ul>
<li>需要集成<strong>特定的 SaaS 产品</strong>（Notion、Slack、Stripe、Telegram 等），不想手写每个 API 客户端。</li>
<li>想要<strong>事件驱动的 workflow</strong>（webhook、轮询、定时），不想自己搭事件总线。</li>
<li>想要<strong>可视化编辑器</strong>让非技术队友能看到和修改流水线。</li>
<li>接受 workflow 是 <strong>execution-only</strong>——没有判断，只有 plumbing。</li>
</ul>
<p>n8n <em>不</em>适合：</p>
<ul>
<li>需要<strong>多步 LLM 推理</strong>且步骤之间共享记忆。用 agent harness（Claude Code、LangGraph、OpenAI Agents SDK）。</li>
<li>需要<strong>完全控制 prompt 格式、token 预算、fallback 链</strong>。n8n 的 LLM 节点对认真的场景太抽象。</li>
<li>workflow 逻辑<strong>每周都变</strong>。可视化编辑器对稳定 workflow 很好，对快速迭代的 workflow 是拖累——代码比节点重构快得多。</li>
</ul>
<hr>
<h2 id="更深的原则模型是-commodity编排才是护城河">更深的原则：&ldquo;模型是 commodity，编排才是护城河&rdquo;</h2>
<p>跳过 Dify、吸收 n8n，这两个决定其实是同一个原则的两个侧面：</p>
<ul>
<li><strong>模型</strong>（DeepSeek、GPT、Claude、Mistral、Llama）是可互换的。一个环境变量切换。</li>
<li><strong>平台</strong>（Dify、LangFlow、Flowise）也是可互换的。它们用不同方式打包相似的能力。</li>
<li><strong>编排</strong>（orchestration）——把模型、知识、工具、结果连起来的那层系统——才是真正的杠杆所在。</li>
</ul>
<p>当你已经有一个强 orchestrator，你就不需要一个想当 orchestrator 的平台。你需要把 plumbing 做好的 plumbing。这就是 n8n 赢得位置的理由。</p>
<p>这个原则可以泛化。每次评估一个 AI 平台，先问自己：<strong>它想占据我的编排层，还是填补编排层下面的空白？</strong> 如果是&quot;占据&quot;而你已经有 orchestrator，跳过。如果是&quot;填补空白&quot;而那个空白真实存在，吸收。</p>
<hr>
<h2 id="收尾">收尾</h2>
<p>两个平台。相反的决定。底层逻辑一样：<em>它想占据哪一层？我是不是已经拥有那一层？</em></p>
<ul>
<li><strong>Dify</strong> 想占据编排层 → 已被覆盖 → <strong>拒绝</strong>。</li>
<li><strong>n8n</strong> 想占据执行层（事件触发、SaaS 集成、重试、轮询）→ 未被覆盖 → <strong>吸收</strong>。</li>
</ul>
<p>如果你现在正在评估自托管 AI 工具，这是第一个该问的问题。能省下很多没意义的部署，更能省下后面更没意义的返工。</p>
<hr>
<p><em>参考</em></p>
<ul>
<li><em><a href="https://github.com/langgenius/dify">Dify GitHub</a></em> — 55k+ ⭐，Apache 2.0</li>
<li><em><a href="https://github.com/n8n-io/n8n">n8n GitHub</a></em> — 162k+ ⭐，Sustainable Use License</li>
<li><em><a href="https://modelcontextprotocol.io/">Model Context Protocol（MCP）规范</a></em></li>
<li><em><a href="https://aibrew.ai/zh/2026/05/rag-vs-agent%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E7%94%A8%E5%93%AA%E4%B8%AA%E7%BB%93%E5%90%88%E6%88%91%E4%BB%AC%E8%87%AA%E5%B7%B1%E7%9A%84%E5%AE%9E%E6%88%98/">上一篇：RAG vs Agents</a></em></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>逆向 Claude Code 源码：它的 Agent 架构是怎么设计的，以及我们如何用同样思路给泰拉瑞亚做了个 AI 助手</title>
      <link>https://aibrew.ai/zh/2026/05/%E9%80%86%E5%90%91-claude-code-%E6%BA%90%E7%A0%81%E5%AE%83%E7%9A%84-agent-%E6%9E%B6%E6%9E%84%E6%98%AF%E6%80%8E%E4%B9%88%E8%AE%BE%E8%AE%A1%E7%9A%84%E4%BB%A5%E5%8F%8A%E6%88%91%E4%BB%AC%E5%A6%82%E4%BD%95%E7%94%A8%E5%90%8C%E6%A0%B7%E6%80%9D%E8%B7%AF%E7%BB%99%E6%B3%B0%E6%8B%89%E7%91%9E%E4%BA%9A%E5%81%9A%E4%BA%86%E4%B8%AA-ai-%E5%8A%A9%E6%89%8B/</link>
      <pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate>
      <guid>https://aibrew.ai/zh/2026/05/%E9%80%86%E5%90%91-claude-code-%E6%BA%90%E7%A0%81%E5%AE%83%E7%9A%84-agent-%E6%9E%B6%E6%9E%84%E6%98%AF%E6%80%8E%E4%B9%88%E8%AE%BE%E8%AE%A1%E7%9A%84%E4%BB%A5%E5%8F%8A%E6%88%91%E4%BB%AC%E5%A6%82%E4%BD%95%E7%94%A8%E5%90%8C%E6%A0%B7%E6%80%9D%E8%B7%AF%E7%BB%99%E6%B3%B0%E6%8B%89%E7%91%9E%E4%BA%9A%E5%81%9A%E4%BA%86%E4%B8%AA-ai-%E5%8A%A9%E6%89%8B/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — 我们逆向了 Claude Code 的 TypeScript 源码，搞清楚了它的 Agent 架构如何处理安全、复杂任务和工具权限。然后把这些模式用到了一个开源项目上——让玩家在泰拉瑞亚游戏里跟 AI 聊天，AI 还能给道具、改天气、传送玩家。以下是我们的发现、实现过程和踩坑总结。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;为什么要拆-claude-code-的源码&#34;&gt;为什么要拆 Claude Code 的源码&lt;/h2&gt;
&lt;p&gt;Claude Code 不只是个编程助手。底层它是一个 Agent 运行时——会 spawn 子 Agent、管理文件权限、跑 bash 命令、判断什么时候该问用户什么时候该直接做。我们想搞清楚它的内部机制，然后把这些想法用到一个完全不同的场景：泰拉瑞亚游戏服务器。&lt;/p&gt;
&lt;p&gt;我们的项目 &lt;a href=&#34;https://github.com/d99sfrmdbz-debug/terra_llm_bridge&#34;&gt;terra_llm_bridge&lt;/a&gt; 把泰拉瑞亚 TShock 服务器接到了一个 LLM 上。玩家在聊天框打 &lt;code&gt;@ai&lt;/code&gt; 就能跟 AI 对话——但 AI 不止能聊天，还能&lt;strong&gt;做事&lt;/strong&gt;：给道具、改天气、传送玩家，甚至能切换困难模式。最后那条就是我们翻车的地方。&lt;/p&gt;
&lt;p&gt;第一次有玩家让 AI 设成雨天，LLM 自作主张调了 &lt;code&gt;terra_world_hardmode(confirm=True)&lt;/code&gt;——把整个服务器的世界&lt;strong&gt;不可逆&lt;/strong&gt;地切成了困难模式。没人要求它这么做。模型自己觉得该做就做了。&lt;/p&gt;
&lt;p&gt;我们需要一个真正的权限系统。于是去翻 Claude Code 的源码。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;claude-code-的-7-层权限架构&#34;&gt;Claude Code 的 7 层权限架构&lt;/h2&gt;
&lt;p&gt;通读 &lt;code&gt;src/utils/permissions/permissions.ts&lt;/code&gt; 的约 1500 行代码，加上 Agent 工具的基础设施（约 3800 行），一套清晰的架构浮现出来。Claude Code 不是靠单点检查做安全——它有&lt;strong&gt;七层&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Layer 1a: 拒绝规则   →  &amp;#34;永远不允许 Bash(git push --force)&amp;#34;
Layer 1b: 询问规则   →  &amp;#34;Bash(curl *) 总是弹窗确认&amp;#34;
Layer 1c: 工具自检   →  每个工具 checkPermissions() 自己的逻辑
Layer 1d: 工具自拒   →  Read 工具白名单特定路径
Layer 1f: 内容规则   →  &amp;#34;就算 bypass 模式，npm publish 也要弹窗&amp;#34;
Layer 1g: 安全检查   →  &amp;#34;.git/、.claude/ 永远不能绕过用户确认&amp;#34;
Layer 2:  模式旁路   →  bypassPermissions / auto / acceptEdits / dontAsk
Layer 3:  YOLO 分类器 →  AI 读全文 transcript，判断是否安全
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最有意思的是 &lt;strong&gt;YOLO 分类器&lt;/strong&gt;——一个独立的小模型，读取完整对话记录，把每次工具调用分类为安全或危险。两阶段系统：快速分类器处理明显 case，深度思考分类器处理边界情况。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong> — 我们逆向了 Claude Code 的 TypeScript 源码，搞清楚了它的 Agent 架构如何处理安全、复杂任务和工具权限。然后把这些模式用到了一个开源项目上——让玩家在泰拉瑞亚游戏里跟 AI 聊天，AI 还能给道具、改天气、传送玩家。以下是我们的发现、实现过程和踩坑总结。</p>
</blockquote>
<hr>
<h2 id="为什么要拆-claude-code-的源码">为什么要拆 Claude Code 的源码</h2>
<p>Claude Code 不只是个编程助手。底层它是一个 Agent 运行时——会 spawn 子 Agent、管理文件权限、跑 bash 命令、判断什么时候该问用户什么时候该直接做。我们想搞清楚它的内部机制，然后把这些想法用到一个完全不同的场景：泰拉瑞亚游戏服务器。</p>
<p>我们的项目 <a href="https://github.com/d99sfrmdbz-debug/terra_llm_bridge">terra_llm_bridge</a> 把泰拉瑞亚 TShock 服务器接到了一个 LLM 上。玩家在聊天框打 <code>@ai</code> 就能跟 AI 对话——但 AI 不止能聊天，还能<strong>做事</strong>：给道具、改天气、传送玩家，甚至能切换困难模式。最后那条就是我们翻车的地方。</p>
<p>第一次有玩家让 AI 设成雨天，LLM 自作主张调了 <code>terra_world_hardmode(confirm=True)</code>——把整个服务器的世界<strong>不可逆</strong>地切成了困难模式。没人要求它这么做。模型自己觉得该做就做了。</p>
<p>我们需要一个真正的权限系统。于是去翻 Claude Code 的源码。</p>
<hr>
<h2 id="claude-code-的-7-层权限架构">Claude Code 的 7 层权限架构</h2>
<p>通读 <code>src/utils/permissions/permissions.ts</code> 的约 1500 行代码，加上 Agent 工具的基础设施（约 3800 行），一套清晰的架构浮现出来。Claude Code 不是靠单点检查做安全——它有<strong>七层</strong>：</p>
<pre tabindex="0"><code>Layer 1a: 拒绝规则   →  &#34;永远不允许 Bash(git push --force)&#34;
Layer 1b: 询问规则   →  &#34;Bash(curl *) 总是弹窗确认&#34;
Layer 1c: 工具自检   →  每个工具 checkPermissions() 自己的逻辑
Layer 1d: 工具自拒   →  Read 工具白名单特定路径
Layer 1f: 内容规则   →  &#34;就算 bypass 模式，npm publish 也要弹窗&#34;
Layer 1g: 安全检查   →  &#34;.git/、.claude/ 永远不能绕过用户确认&#34;
Layer 2:  模式旁路   →  bypassPermissions / auto / acceptEdits / dontAsk
Layer 3:  YOLO 分类器 →  AI 读全文 transcript，判断是否安全
</code></pre><p>最有意思的是 <strong>YOLO 分类器</strong>——一个独立的小模型，读取完整对话记录，把每次工具调用分类为安全或危险。两阶段系统：快速分类器处理明显 case，深度思考分类器处理边界情况。</p>
<p>但对我们最有用的不是 AI 分类器。而是 Claude Code <strong>如何在结构上防止某些工具在错误的上下文中被调用</strong>——通过工具白名单、黑名单和子 Agent 特化。</p>
<hr>
<h2 id="agent-模式不是多-agent-协作而是专项-worker">Agent 模式：不是多 Agent 协作，而是专项 Worker</h2>
<p>Claude Code 用的不是&quot;Agent 之间协商谈判&quot;的多 Agent 协作。它是一个<strong>主协调器 + 专项 Worker</strong>：</p>
<pre tabindex="0"><code>主 Agent（Tool Calling，全部工具）
  │
  ├─ 简单: &#34;读文件 X&#34; → Read 工具
  │
  └─ 复杂: &#34;审计这个分支&#34; → Agent(&#34;Explore&#34;)
                              │
                              ├─ 工具: [Read, Grep, Glob]  ← 白名单
                              ├─ 禁止: [Edit, Write]        ← 黑名单
                              ├─ 系统提示: &#34;你是文件搜索专家&#34;
                              └─ 返回结果 → 主 Agent 行动
</code></pre><p>每个子 Agent 类型由三要素定义：</p>
<ol>
<li><strong>工具权限</strong>（白名单 + 黑名单）——能碰什么</li>
<li><strong>系统提示</strong>——角色专属指令</li>
<li><strong>模型</strong>——Explore Agent 用 Haiku（便宜），Plan Agent 用 Sonnet（推理强）</li>
</ol>
<p>核心洞察：<strong>主 Agent 不会变更复杂</strong>。它保持简单，只有一个 <code>Agent</code> 工具让它把复杂任务委派出去。子 Agent 就是另一个 Tool Calling 循环，只是工具受限 + 提示词不同。</p>
<p>这套架构的可组合性是关键：每个零件简单，但组合起来能处理单个 prompt 消化不了的复杂度。</p>
<hr>
<h2 id="我们怎么把这个模式用到-terra_llm_bridge">我们怎么把这个模式用到 terra_llm_bridge</h2>
<p>我们的泰拉瑞亚桥接比 Claude Code 简单——46 个工具而非几百个，&ldquo;安全问题&quot;是&quot;别在玩家问天气时切 hardmode&quot;而不是&quot;别让 AI rm -rf /&quot;。但模式是直接可以搬的。</p>
<h3 id="问题">问题</h3>
<p>改之前：LLM 同时看到所有 46 个工具。当玩家问&quot;给我最强套装&rdquo;，LLM 会<strong>并行</strong>调 <code>wiki_search</code> 查资料 + <code>give_item</code> 给东西——一边查 wiki 一边已经预判了 Solar Flare 套装。有时候猜对，有时候给召唤师玩家塞了一套战士装备。</p>
<h3 id="解决方案两阶段工具开放">解决方案：两阶段工具开放</h3>
<p>我们没有加子 Agent——46 个工具不需要。但我们在 graph 层面用了<strong>工具限制模式</strong>：</p>
<pre tabindex="0"><code>route → llm(研究)  ⇄  tool      →  escalate  →  llm(行动)  ⇄  authorize  ⇄  tool  →  output
        17 个只读工具                          46 全工具      关键词 gate
        wiki、lookup、状态                       give、kick、spawn
</code></pre><p>图有两个阶段：</p>
<p><strong>研究阶段</strong>——LLM 只拿到 17 个只读工具（wiki_search、item_lookup、player_list、world_info 等）。它<strong>不能</strong>调 give_item、kick、spawn 或任何破坏性工具。先查资料。</p>
<p><strong>升级（escalate）</strong>——当 LLM 输出文本（没有更多 tool_call），图自动切到行动模式，注入提示：&ldquo;你现在可以访问全部工具了。&rdquo;</p>
<p><strong>行动阶段</strong>——LLM 拿到全部 46 个工具，可以对研究发现做出行动。</p>
<p>这是结构层面强制执行的，不是 prompt 建议。LLM 在研究阶段根本调不了 <code>give_item</code>，因为这个工具没绑定。</p>
<h3 id="权限-gate">权限 Gate</h3>
<p>在两阶段拆分之前，我们还加了 <code>authorize_node</code>——LLM 和 ToolNode 之间的硬拦截，检查玩家聊天最近的消息是否包含该工具领域的关键词：</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>GATED_TOOLS <span style="color:#ff79c6">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_world_hardmode&#34;</span>: {<span style="color:#f1fa8c">&#34;hardmode&#34;</span>, <span style="color:#f1fa8c">&#34;hard mode&#34;</span>, <span style="color:#f1fa8c">&#34;肉山&#34;</span>, <span style="color:#f1fa8c">&#34;困难模式&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_player_kick&#34;</span>:    {<span style="color:#f1fa8c">&#34;kick&#34;</span>, <span style="color:#f1fa8c">&#34;踢出&#34;</span>, <span style="color:#f1fa8c">&#34;踢了&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;terra_server_stop&#34;</span>:    {<span style="color:#f1fa8c">&#34;stop server&#34;</span>, <span style="color:#f1fa8c">&#34;关服&#34;</span>, <span style="color:#f1fa8c">&#34;停服&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># ... 还有 8 个</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果玩家说&quot;设个雨天试试&quot;而 LLM 想调 <code>world_hardmode</code>，authorize_node 检查：玩家最近的消息里有 hardmode 相关的关键词吗？没有？<strong>拦截。</strong> 这个工具调用被替换成 BLOCKED 消息，ToolNode 根本看不到。</p>
<p>这是粗过滤器——它检查的是玩家<strong>提到了</strong>什么，而不是玩家<strong>请求了</strong>什么。&ldquo;上次打肉山的时候&quot;会通过关键词检查，尽管玩家没要求开 hardmode。但粗够了：目标是拦截灾难性的跨界调用（天气 → hardmode），不是完美理解意图。</p>
<hr>
<h2 id="我们选择不做的">我们选择不做的</h2>
<h3 id="没有-yolo-分类器">没有 YOLO 分类器</h3>
<p>Claude Code 的 AI 分类器读完整 transcript，用另一个模型判断工具调用是否安全。我们没做，因为：</p>
<ul>
<li>增加延迟——每次 gated 工具调用前多一次 LLM 请求</li>
<li>泰拉瑞亚聊天风险低——给错套装可以补救</li>
<li>关键词匹配已经能拦住灾难性 case</li>
</ul>
<h3 id="没有子-agent-派生">没有子 Agent 派生</h3>
<p>Claude Code 为复杂任务 spawn 子进程。我们不需要：</p>
<ul>
<li>泰拉瑞亚工具面小（46 个）</li>
<li>多轮工具调用已经能处理我们实际面对的场景</li>
<li>给游戏聊天机器人 spawn 子进程是过度工程</li>
</ul>
<h3 id="没有-react-模式">没有 ReAct 模式</h3>
<p>经典的 Thought → Action → Observation 循环会增加 token 消耗，但不改变我们的核心能力。DeepSeek 的 thinking tokens 已经承担了推理，而两阶段工具访问比基于 prompt 的 ReAct 更可靠地强制了&quot;先研究再行动&rdquo;。</p>
<hr>
<h2 id="一张图看清架构">一张图看清架构</h2>
<pre tabindex="0"><code>┌──────────────────────────────────────────────────────────┐
│  泰拉瑞亚服务器（TShock + C# 插件，24 个游戏 Hook）        │
│  玩家输入 &#34;@ai 给我最好的套装&#34;                             │
└──────────────────────┬───────────────────────────────────┘
                       │ JSON webhook
┌──────────────────────▼───────────────────────────────────┐
│  Python aiohttp 监听器 (:9876)                            │
└──────────────────────┬───────────────────────────────────┘
                       │
┌──────────────────────▼───────────────────────────────────┐
│  LangGraph StateGraph                                     │
│                                                           │
│  route  →  llm(研究)  ⇄  tool      17 只读工具           │
│               │                                           │
│          escalate  →  llm(行动)  ⇄  authorize  ⇄  tool   │
│                          46 全工具      关键词拦截         │
│               │                                           │
│             output  →  广播到游戏聊天                      │
│                                                           │
│  记忆: AsyncSqliteSaver 按玩家（thread_id）持久化          │
└──────────────────────────────────────────────────────────┘
                       │
         ┌─────────────┴──────────────┐
         ▼                            ▼
   TShock REST API              Terraria Wiki API
   (give / kick / spawn)        (terraria.wiki.gg)
</code></pre><hr>
<h2 id="源码分析的启示">源码分析的启示</h2>
<p>读 Claude Code 源码教会我们三件事，适用于任何 Agent 项目：</p>
<p><strong>1. 安全是分层的，不是二元的。</strong> 一个 <code>confirm</code> 参数对 LLM 来说只是软建议。真正的安全需要结构性约束——LLM 不该能调用它无权使用的工具，就像 Web 服务器不该让你访问没有权限的端点，不管你怎么礼貌地请求。</p>
<p><strong>2. 工具限制是最便宜也最可靠的安全形式。</strong> Claude Code 的 Explore Agent 之所以&quot;只读&quot;，不是因为 prompt 写了——是因为 Edit 和 Write 不在它的工具列表里。我们的研究阶段之所以&quot;先查资料&quot;，不是 prompt 建议——是因为 give_item 根本没绑定。你不能通过 prompt injection 绕过不存在的工具。</p>
<p><strong>3. 特化胜过复杂化。</strong> Claude Code 的子 Agent 不比主 Agent 更聪明——只是更受约束。更少的工具 + 聚焦的 prompt = 更可靠的行为。我们的两阶段系统同理：先限制，准备就绪再扩展。</p>
<hr>
<h2 id="关于这个项目">关于这个项目</h2>
<p><code>terra_llm_bridge</code> 是一个连接泰拉瑞亚游戏服务器与 LLM 的开源项目。功能包括：</p>
<ul>
<li><strong>24 个游戏 Hook</strong>——自研 C# TShock 插件捕获聊天、Boss 击杀、死亡、登录等 24 种事件</li>
<li><strong>46 个管理工具</strong>——给道具、管玩家、控天气、召 NPC、管区域和权限</li>
<li><strong>两阶段 Agent</strong>——研究（17 工具）→ 行动（46 工具）</li>
<li><strong>硬权限 Gate</strong>——基于关键词的 authorize_node 拦截未授权工具调用</li>
<li><strong>MCP 服务端</strong>——同 46 工具暴露给 Claude Code 做服务器管理</li>
<li><strong>持久化记忆</strong>——通过 LangGraph AsyncSqliteSaver 按玩家保持对话历史</li>
</ul>
<p>项目目前处于<strong>活跃测试阶段</strong>，尚未发布到 GitHub。我们在私有泰拉瑞亚服务器上运行，迭代 Agent 架构后再开源。如果对代码感兴趣或想提前体验，欢迎联系。</p>
<hr>
<p><em>技术栈：Python 3.14, LangGraph 1.x, DeepSeek（Anthropic 兼容 API）, C# .NET 9, TShock v6.1.0, aiohttp, httpx.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
