<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.6">Jekyll</generator><link href="https://mengtnt.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mengtnt.com/" rel="alternate" type="text/html" /><updated>2026-07-28T00:03:49+00:00</updated><id>https://mengtnt.com/feed.xml</id><title type="html">mengtnt的Blog</title><subtitle>走自己的路，让别人说去吧</subtitle><author><name>mengtnt</name></author><entry><title type="html">DeepSeek 梁文锋亲述 AGI 路线图的五个阶梯</title><link href="https://mengtnt.com/2026/07/27/00-03.html" rel="alternate" type="text/html" title="DeepSeek 梁文锋亲述 AGI 路线图的五个阶梯" /><published>2026-07-27T23:58:00+00:00</published><updated>2026-07-27T23:58:00+00:00</updated><id>https://mengtnt.com/2026/07/27/00-03</id><content type="html" xml:base="https://mengtnt.com/2026/07/27/00-03.html">&lt;h1 id=&quot;deepseek-梁文锋亲述agi-路线图的五个阶梯通向奇点之路&quot;&gt;DeepSeek 梁文锋亲述：AGI 路线图的五个阶梯，通向奇点之路&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;来源：DeepSeek 创始人梁文锋在投资者交流会中的分享&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;写在前面&quot;&gt;写在前面&lt;/h2&gt;

&lt;p&gt;DeepSeek 的创始人梁文锋，向来低调。但在一次罕见的投资者交流会中，他详细阐述了 DeepSeek 通向通用人工智能（AGI）的核心战略与技术路线图。&lt;/p&gt;

&lt;p&gt;最引人注目的，是他提出的 &lt;strong&gt;“五个阶梯”路线图&lt;/strong&gt;——从基础的思维链到终极的具身智能，每一阶都回答了 AGI 进程中一个关键问题。同时，他关于”渐进的奇点”（而非突变的奇点）的判断，以及对当前 AI 热点的取舍态度，展现了一个技术人对行业本质的清醒认知。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;deepseek-的-agi-五个阶梯&quot;&gt;DeepSeek 的 AGI 五个阶梯&lt;/h2&gt;

&lt;h3 id=&quot;第一阶梯思维链chain-of-thought-cot&quot;&gt;第一阶梯：思维链（Chain of Thought, CoT）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;逻辑推理的底层能力&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;思维链是 AI 迈向 AGI 的第一步，也是基础。模型需要学会”逐步推理”，而不是仅靠模式匹配给出答案。&lt;/p&gt;

&lt;p&gt;梁文锋表示，DeepSeek &lt;strong&gt;已于去年完成了这一阶段的工作&lt;/strong&gt;。也正是基于 CoT 能力，DeepSeek 的模型才能在各种复杂推理任务中展现出令人印象深刻的表现。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;第二阶梯智能体agent&quot;&gt;第二阶梯：智能体（Agent）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;从对话到行动的关键跨越&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;梁文锋明确指出，&lt;strong&gt;这是 DeepSeek 当前正在主攻的阶段&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;核心方向有两个：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;编码智能体（Coding Agent）&lt;/strong&gt;：优先任务。DeepSeek 相信，能写代码、能操作工具的 AI 是实现 AGI 的最快路径。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;通用智能体（General Agent）&lt;/strong&gt;：继 Coding Agent 之后的下一个阶段。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agent 的本质是让 AI 从”对话系统”转变为”任务执行系统”。当前模型在提供足够上下文的情况下已经可以完成日益复杂的工作，&lt;strong&gt;但它们不像人类那样能积累经验&lt;/strong&gt;——这正是下一阶梯要解决的问题。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;第三阶梯持续学习continual-learning&quot;&gt;第三阶梯：持续学习（Continual Learning）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI 能否真正替代人的分水岭&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;梁文锋认为，持续学习是 &lt;strong&gt;DeepSeek 眼中下一个需要解决的重大突破&lt;/strong&gt;，也是目前 AI 最大的瓶颈之一。&lt;/p&gt;

&lt;p&gt;当前 AI 模型的困境是：每次处理新任务都需要完整上下文，模型本身不会在工作中”长记性”。它们无法像人类一样，在工作过程中持续学习、积累经验、改进方法。&lt;/p&gt;

&lt;p&gt;如果 AI 能够实现持续学习，它将：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;能够随时间积累经验，而不是每次从零开始&lt;/li&gt;
  &lt;li&gt;加速 AI 研究，帮助自身迭代&lt;/li&gt;
  &lt;li&gt;真正有能力替代人类员工，而不仅仅是辅助工具&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;第四阶梯ai-自我迭代--渐进的奇点ai-self-iteration--the-gradual-singularity&quot;&gt;第四阶梯：AI 自我迭代 / 渐进的奇点（AI Self-Iteration / The Gradual Singularity）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI 帮助人类研发更好的 AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在实现持续学习能力之后，模型将进入一个全新的阶段——&lt;strong&gt;模型能够自行开发更前沿的 AI 模型&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;这是梁文锋提出的 &lt;strong&gt;“渐进的奇点”&lt;/strong&gt; 概念。这不是一个突然爆发的技术奇点，而是一个&lt;strong&gt;相对漫长且连续的演进过程&lt;/strong&gt;。在这个阶段：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AI 可以完成人类能做的所有事情&lt;/li&gt;
  &lt;li&gt;AI 能够辅助甚至主导 AI 自身的研发&lt;/li&gt;
  &lt;li&gt;技术迭代速度会进一步加快，但不会瞬间颠覆一切&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;与一些人对”奇点”的想象不同——它不是一夜之间的突变，而是渐进式的加速。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;第五阶梯具身智能embodied-intelligence&quot;&gt;第五阶梯：具身智能（Embodied Intelligence）&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AGI 终极目标：进入物理世界&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这是整个路线图的最终阶段——&lt;strong&gt;让 AI 融入物理实体，进入现实世界提供劳动力&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;梁文锋认为，对人类而言，最终的需求往往不是电脑里的软件，而是能够像人一样在物理世界中行动的能力。机器人、自动驾驶、智能制造——这些都是具身智能的体现。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;战略取舍deepseek-不做什么&quot;&gt;战略取舍：DeepSeek 不做什么&lt;/h2&gt;

&lt;p&gt;梁文锋非常明确地陈述了 DeepSeek 的战略聚焦——&lt;strong&gt;不做什么&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;❌ &lt;strong&gt;不追求 3D 生成&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;❌ &lt;strong&gt;不追求视频生成&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;❌ &lt;strong&gt;不追求世界模型&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;背后的逻辑很清晰：他认为这些方向与”智能的上限”没有太大关系，&lt;strong&gt;并非通向 AGI 的主线&lt;/strong&gt;。DeepSeek 将所有资源集中于 AGI 这一条主线上。&lt;/p&gt;

&lt;p&gt;这其实也是一种”克制”——当全世界都在追视频生成、3D 生成的热点时，选择不去跟风，而是专注于自己认定的核心路径。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;商业策略克制而非利润最大化&quot;&gt;商业策略：克制而非利润最大化&lt;/h2&gt;

&lt;p&gt;梁文锋在商业策略上同样展现了独特的思路：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;不以利润最大化为目标&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;只寻求合理利润&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;开源和低价是一种战略克制&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为什么这样做？因为 DeepSeek 的核心目标是 &lt;strong&gt;提高实现 AGI 的概率&lt;/strong&gt;。开源让更多人参与、使用和反馈，低价让技术触达更广的人群——这些都指向同一个目标：更快、更大概率地抵达 AGI。&lt;/p&gt;

&lt;p&gt;在他看来，&lt;strong&gt;产品只是通往 AGI 过程中的副产品&lt;/strong&gt;。公司存在的唯一主线是那条技术路线图。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;唯一的不可谈判要素团队稳定性&quot;&gt;唯一的不可谈判要素：团队稳定性&lt;/h2&gt;

&lt;p&gt;在谈及 DeepSeek 的组织时，梁文锋提到了一个很强的判断：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;保持团队稳定性，是 DeepSeek 实现 AGI 唯一不可谈判的核心要素。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这意味着什么？技术方向可以调整，战略可以迭代，但核心团队不能散。在 AGI 这场马拉松中，拥有一个稳定、专注、长期存在的团队，可能比选择哪个具体技术方向更重要。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;总结梁文锋的-agi-世界观&quot;&gt;总结：梁文锋的 AGI 世界观&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;阶梯&lt;/th&gt;
      &lt;th&gt;阶段&lt;/th&gt;
      &lt;th&gt;关键能力&lt;/th&gt;
      &lt;th&gt;DeepSeek 状态&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;思维链 CoT&lt;/td&gt;
      &lt;td&gt;逐步推理&lt;/td&gt;
      &lt;td&gt;✅ 已完成&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;智能体 Agent&lt;/td&gt;
      &lt;td&gt;任务执行&lt;/td&gt;
      &lt;td&gt;🚧 进行中（优先 Coding Agent）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;持续学习&lt;/td&gt;
      &lt;td&gt;积累经验&lt;/td&gt;
      &lt;td&gt;🔜 下一个重大突破&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;自我迭代&lt;/td&gt;
      &lt;td&gt;AI 研发 AI&lt;/td&gt;
      &lt;td&gt;→ 渐进的奇点&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;具身智能&lt;/td&gt;
      &lt;td&gt;物理世界行动&lt;/td&gt;
      &lt;td&gt;🎯 终极目标&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;梁文锋和他的 DeepSeek 最让人印象深刻的地方，或许不在于技术实力本身，而在于一种 &lt;strong&gt;“极度聚焦的克制”&lt;/strong&gt;——知道该做什么，更知道不该做什么。&lt;/p&gt;

&lt;p&gt;在 AI 行业充斥着 FOMO（错失恐惧症）的今天，这种”归零到核心目标”的能力，可能才是走完这五个阶梯的真正基石。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;本文基于公开信息整理，内容已尽可能保持原意。&lt;/em&gt;&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">DeepSeek 梁文锋亲述：AGI 路线图的五个阶梯，通向奇点之路</summary></entry><entry><title type="html">DeepSeek 梁文锋拆解：中美 AI 算力差距有多大？国产芯片何时追上？</title><link href="https://mengtnt.com/2026/07/26/09-14.html" rel="alternate" type="text/html" title="DeepSeek 梁文锋拆解：中美 AI 算力差距有多大？国产芯片何时追上？" /><published>2026-07-26T09:14:40+00:00</published><updated>2026-07-26T09:14:40+00:00</updated><id>https://mengtnt.com/2026/07/26/09-14</id><content type="html" xml:base="https://mengtnt.com/2026/07/26/09-14.html">&lt;blockquote&gt;
  &lt;p&gt;DeepSeek 创始人梁文锋在内部交流中罕见地披露了公司的算力现状和未来规划：两万张 H 等效卡、落后美国两年、华为 950 四张顶一张。在英伟达 GPU 被禁运的现实下，中国 AI 公司到底该何去何从？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;引言&quot;&gt;引言&lt;/h2&gt;

&lt;p&gt;2026 年，中国 AI 行业面临一个绕不开的尖锐问题：&lt;strong&gt;算力。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenAI、Anthropic、Google 动辄几十万张 GPU 的训练集群，而国内模型公司能拿到的卡数少得可怜。梁文锋在这次投资者交流会上，罕见地披露了 DeepSeek 的真实算力数据，以及对中美算力差距、国产芯片替代路径的坦率判断。&lt;/p&gt;

&lt;p&gt;这些信息，是整个行业第一次如此清楚地看到中国的真实位置。&lt;/p&gt;

&lt;h2 id=&quot;一deepseek-的家底两万张-h-等效卡&quot;&gt;一、DeepSeek 的家底：两万张 H 等效卡&lt;/h2&gt;

&lt;p&gt;“我们现在大概是两万张 H 等效算力，其中大部分是最近一两个月刚到。”&lt;/p&gt;

&lt;p&gt;这是梁文锋首次明确披露 DeepSeek 的算力规模。两万张 H 等效卡是什么概念？这大约相当于 OpenAI 的几十分之一。&lt;/p&gt;

&lt;p&gt;更关键的是，这些卡是刚刚才到位的。在此之前 DeepSeek 的算力还要少得多。也就是说，轰动全球的 DeepSeek V3、R1 等一系列模型，其实是在一个极其有限的算力条件下训练出来的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;梁文锋的态度是：有多少资源就训多大的模型。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“我们训练这么大的模型，并不是因为我觉得这么大的模型就够了，而是我刚好有这么多资源。我是按照我的资源来算的。”&lt;/p&gt;

&lt;p&gt;梁文锋透露，融资之后 DeepSeek 的策略非常明确：&lt;strong&gt;在合理的价格里，能买多少卡就买多少卡。&lt;/strong&gt; 他甚至说，”如果半年之内我就把钱花完，那太幸福了，太理想了。”&lt;/p&gt;

&lt;p&gt;这不是豪赌，而是理性的算计——一台服务器的成本十个月就能收回，买卡比存银行划算得多。&lt;/p&gt;

&lt;p&gt;但现实是残酷的：有钱也买不到。&lt;/p&gt;

&lt;p&gt;“实际上要把这么多钱花完非常不容易，买不到那么多卡，很难买。”&lt;/p&gt;

&lt;h2 id=&quot;二中美差距不仅是数量级还有时间维度&quot;&gt;二、中美差距：不仅是数量级，还有时间维度&lt;/h2&gt;

&lt;p&gt;梁文锋用两组数据清晰地描述了差距：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第一，模型规模的差距。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“现在最大的模型，激活参数大概是 800B。国内的话，我们在几十 B 的规模，差一个数量级。”&lt;/p&gt;

&lt;p&gt;如果你要训练一个 800B 激活参数的模型，需要大概五万张 NVIDIA GB300，或者二十万张华为 950——而且这还只是训练，没有考虑做研究需要更多。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第二，时间的差距。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“落后美国两年，只用美国二十分之一的算力。”&lt;/p&gt;

&lt;p&gt;这里的”两年”是指，华为 950 超节点大概相当于英伟达两年前的产品。英伟达今年 Q3 已经有新一代了，而华为 950 今年 Q3/Q4 才能出货。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;但差距不仅体现在硬件上。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;梁文锋指出，算力的差距会传导到人才差距——因为算力少，能做的实验机会就少，人才的成长速度就慢。&lt;/p&gt;

&lt;p&gt;“人才的差距，本质上也是因为算力的差距。”&lt;/p&gt;

&lt;p&gt;这是一个正反馈的恶性循环：算力少 → 实验少 → 人才成长慢 → 追赶更困难。但 DeepSeek 正在尝试用另一种方式打破这个循环——用更高的效率来弥补算力的不足。&lt;/p&gt;

&lt;h2 id=&quot;三英伟达的护城河正在瓦解&quot;&gt;三、英伟达的护城河正在瓦解&lt;/h2&gt;

&lt;p&gt;梁文锋在交流中提出了一个让投资者非常关注的观点：&lt;strong&gt;英伟达 CUDA 的护城河正在快速瓦解。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;原因有三：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第一，AI 本身在瓦解生态壁垒。&lt;/strong&gt; 以前要构建一套芯片生态，需要大量人力去写算子、做适配，这是英伟达花了几十年建立的优势。但现在有了 AI，”用 AI 来构建这个生态比以前容易很多了，因为 AI 可以写代码。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第二，DeepSeek 自研的 TileLang 技术。&lt;/strong&gt; 这是一种高级语言，用它可以很快地把 CUDA 的整套生态全部重写一遍。梁文锋透露，V3 训练的时候已经用英伟达的卡但没有用英伟达的生态——”我们先写一个高级编译器叫 TileLang，基于 TileLang 的生态来完成其他所有的事情，就已经几乎不依赖英伟达的生态了。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第三，英伟达的游戏卡和计算卡正在脱钩。&lt;/strong&gt; CUDA 原本是从游戏卡演变出来的，它兼容游戏卡。但以前 AI 计算是个小领域，兼容游戏卡是合理的。现在计算卡的市场已经远超游戏卡，两者不再需要耦合。”专用芯片，不管华为还是英伟达自己，以后都是专用芯片。”&lt;/p&gt;

&lt;p&gt;这个判断如果成立，意味着华为等国产芯片面临的最大障碍——生态问题——可能在未来一两年内被彻底解决。&lt;/p&gt;

&lt;h2 id=&quot;四华为-950四张顶一张落后两年&quot;&gt;四、华为 950：四张顶一张，落后两年&lt;/h2&gt;

&lt;p&gt;梁文锋对华为 950 超节点的评价相当具体和坦诚：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;性能层面&lt;/strong&gt;：”华为 950 超节点在性能和价格上可以完全平替英伟达的 GB200、GB300。所有 GB300 能做的任务，华为超节点都能做，延时什么的都一样。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;效率层面&lt;/strong&gt;：”四张华为卡顶一张英伟达的卡。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;时间层面&lt;/strong&gt;：”落后两年。”华为 950 今年 Q3/Q4 出货，相当于英伟达两年前的 GB200。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;价格层面&lt;/strong&gt;：”价格贵但无所谓，贵百分之一百都无所谓。”因为能买到就是胜利。&lt;/p&gt;

&lt;p&gt;但最大的问题不是性能，而是产能。&lt;/p&gt;

&lt;p&gt;华为给 DeepSeek 的产能是一万六千张卡。互联网大厂是十几万张，差一个数量级。&lt;/p&gt;

&lt;p&gt;“我们买华为 950 的目的，还是希望帮华为把这个生态做好。一万六千张的华为 950，只相当于四千张的 B 系列，不是一个很大的量，意义不是很大。”&lt;/p&gt;

&lt;h2 id=&quot;五国产替代的未来乐观但需要时间&quot;&gt;五、国产替代的未来：乐观但需要时间&lt;/h2&gt;

&lt;p&gt;梁文锋对国产芯片的未来总体持乐观态度，但给出了清晰的时间预期。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;短期（1 年）&lt;/strong&gt;：”未来一年之内，我们能够看到国产芯片的生态没有问题。之前认为是有问题的，认为是用不起来、不好用，但未来一年之内我们能够扭转这个认知。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;中期（2-3 年）&lt;/strong&gt;：仍将卡在产能瓶颈上。”今年、明年、后年，我觉得可能都还是卡在产能问题上。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;长期（5 年）&lt;/strong&gt;：”我不太相信未来五年之后，我们还卡在产能的问题上。我还是比较乐观的。”&lt;/p&gt;

&lt;p&gt;梁文锋认为，现在有”天时地利”的优势——一方面是英伟达买不到，国内被迫自研；另一方面是 AI 技术的进步为生态建设提供了新工具。&lt;/p&gt;

&lt;p&gt;“在英伟达卡买不到的情况下，所有人都迫不得已都要去做国产芯片。在这个背景下，国产卡的适配是没有任何障碍的。”&lt;/p&gt;

&lt;p&gt;但他也承认一个残酷的现实：&lt;strong&gt;四倍加两年&lt;/strong&gt;——四张华为卡顶一张英伟达卡，加上两年的代差——这就是中国 AI 芯片在可预见的未来里要面对的现实。&lt;/p&gt;

&lt;h2 id=&quot;六deepseek-的策略在限制中寻找最优解&quot;&gt;六、DeepSeek 的策略：在限制中寻找最优解&lt;/h2&gt;

&lt;p&gt;面对算力上的巨大劣势，DeepSeek 的选择不是抱怨，而是&lt;strong&gt;利用这个劣势来建立自己的竞争优势&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;“我们跟美国的差距可能是 12 到 18 个月。我们用美国二十分之一的算力把事做出来。未来我们要把时间缩得更短——6 个月、3 个月。这是我们追求的目标。”&lt;/p&gt;

&lt;p&gt;这个目标的基础是 DeepSeek 对效率的极致追求：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;别人不关心成本时，DeepSeek 把成本优化到极致&lt;/li&gt;
  &lt;li&gt;别人用一万块卡时，DeepSeek 试着用一千块卡做出同样的事&lt;/li&gt;
  &lt;li&gt;别人用 CUDA 生态时，DeepSeek 自己写编译器脱离 CUDA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“因为成本越低，我就越能训练更大的模型，我就越能承担起更大的模型。在算力有限的情况下，如果我的计算效率更高，我就能够承担起更大的模型。”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;限制不是阻碍，限制是迫使你创新的动力。&lt;/strong&gt; 这或许是 DeepSeek 给整个中国 AI 行业最宝贵的启示。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;原文来源：梁文锋四小时投资者交流会（2026年5月20日）&lt;/em&gt;&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">DeepSeek 创始人梁文锋在内部交流中罕见地披露了公司的算力现状和未来规划：两万张 H 等效卡、落后美国两年、华为 950 四张顶一张。在英伟达 GPU 被禁运的现实下，中国 AI 公司到底该何去何从？</summary></entry><entry><title type="html">Bun + Tauri + Rust：AI Agent 时代的桌面应用”新铁三角”</title><link href="https://mengtnt.com/2026/06/02/10-44.html" rel="alternate" type="text/html" title="Bun + Tauri + Rust：AI Agent 时代的桌面应用&quot;新铁三角&quot;" /><published>2026-06-02T02:44:00+00:00</published><updated>2026-06-02T02:44:00+00:00</updated><id>https://mengtnt.com/2026/06/02/10-44</id><content type="html" xml:base="https://mengtnt.com/2026/06/02/10-44.html">&lt;blockquote&gt;
  &lt;p&gt;2019 年，有人受够了 Electron 的体积和内存占用，决定用 Rust + 系统 Webview 做一个轻量级的替代品。六年后，这个选择正成为 AI 时代桌面开发的默认答案。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;引言&quot;&gt;引言&lt;/h2&gt;

&lt;p&gt;如果你最近在 GitHub 上关注跨平台开发的动态，会发现一个有趣的现象：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;越来越多的人开始用同一套技术栈构建桌面应用——Bun 做前端构建，Tauri 做桥接框架，Rust 做后端运行时。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这不是偶然。这是一个正在成型的技术共识。&lt;/p&gt;

&lt;p&gt;让我们从头说起。&lt;/p&gt;

&lt;h2 id=&quot;一故事的起点一个被-electron-统治的时代&quot;&gt;一、故事的起点：一个被 Electron 统治的时代&lt;/h2&gt;

&lt;p&gt;2013 年，GitHub 发布了 Electron——一个”用 Web 技术写桌面应用”的框架。&lt;/p&gt;

&lt;p&gt;VS Code、Slack、Discord、Obsidian、Notion……过去十年，几乎所有我们日常使用的桌面应用都是 Electron 写的。&lt;/p&gt;

&lt;p&gt;Electron 的成功无可否认。但它也有三个所有人都知道但长期忍受的问题：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;问题一：包体积巨大。&lt;/strong&gt; 每个 Electron 应用自带一个完整的 Chromium 浏览器 + Node.js 运行时。一个”Hello World”也要 ~150MB。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;问题二：内存占用高。&lt;/strong&gt; 每个进程 200-500MB。同时开 Slack + Discord + VS Code = 1-2GB 内存被吃掉。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;问题三：安全模型过于宽松。&lt;/strong&gt; Electron 默认给前端 Node.js 的完整权限。XSS = 系统完全控制力。&lt;/p&gt;

&lt;p&gt;这些不是”小问题”。它们是整个 Electron 范式的结构性缺陷——因为 Electron 选择了”自带浏览器”这条路，这些问题就无法从根本上解决。&lt;/p&gt;

&lt;h2 id=&quot;二tauri-的诞生一次根本性的反思&quot;&gt;二、Tauri 的诞生：一次根本性的反思&lt;/h2&gt;

&lt;p&gt;2019 年底，Daniel Thompson-Yvetot 发起了 Tauri 项目。&lt;/p&gt;

&lt;p&gt;他的思路很简单但颠覆性：&lt;strong&gt;不自带浏览器，用系统原生的。不绑 Node.js，用 Rust 代替。&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;对比&lt;/th&gt;
      &lt;th&gt;Electron&lt;/th&gt;
      &lt;th&gt;Tauri&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;浏览器内核&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;自带 Chromium&lt;/td&gt;
      &lt;td&gt;系统原生 Webview&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;后端运行时&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Node.js&lt;/td&gt;
      &lt;td&gt;Rust&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Hello World 体积&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;~150MB&lt;/td&gt;
      &lt;td&gt;~3MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;内存占用&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;200-500MB/进程&lt;/td&gt;
      &lt;td&gt;50-150MB/进程&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;安全模型&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Node.js 全权限&lt;/td&gt;
      &lt;td&gt;白名单 + 权限沙箱&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这个差异不是”优化”级别的。这是&lt;strong&gt;架构级别&lt;/strong&gt;的差异。&lt;/p&gt;

&lt;p&gt;2022 年 6 月，Tauri 1.0 正式发布。2024-2025 年，Tauri 2.0 带来移动端支持。到今天，Tauri 已经成为 Rust 生态中最成功的桌面应用框架，也是 Electron 之外最被广泛采用的跨平台桌面方案。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么大家开始采用 Tauri？&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;包体积小 50 倍&lt;/strong&gt;——对需要分发给终端用户的应用，这是决定性的&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rust 的安全性和性能&lt;/strong&gt;——编译期内存安全，零成本抽象，静态编译&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;安全沙箱模型&lt;/strong&gt;——默认拒绝一切，只开放明确授权的能力，企业安全团队友好&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;前端框架无关&lt;/strong&gt;——React、Vue、Svelte、原生 HTML/JS 都行&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;开源社区驱动&lt;/strong&gt;——不被任何大公司的商业决策绑架&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;三bun-的加入让快变得更快&quot;&gt;三、Bun 的加入：让”快”变得更快&lt;/h2&gt;

&lt;p&gt;如果说 Tauri 解决了”轻”的问题，那 Bun 解决的就是”快”的问题。&lt;/p&gt;

&lt;p&gt;Bun 是一个 JavaScript 运行时 + 包管理器 + 打包器 + 测试运行器——&lt;strong&gt;一个工具替代了 npm + Node.js + Webpack + Jest 四个工具。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在 Tauri 的开发工作流中，Bun 的价值体现在：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;操作&lt;/th&gt;
      &lt;th&gt;Bun&lt;/th&gt;
      &lt;th&gt;传统方案&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;install&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;快 20-40 倍&lt;/td&gt;
      &lt;td&gt;npm/yarn/pnpm&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;冷启动&lt;/td&gt;
      &lt;td&gt;快 4 倍&lt;/td&gt;
      &lt;td&gt;Node.js&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;打包&lt;/td&gt;
      &lt;td&gt;快 10 倍+&lt;/td&gt;
      &lt;td&gt;Webpack/esbuild&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;测试&lt;/td&gt;
      &lt;td&gt;内置&lt;/td&gt;
      &lt;td&gt;Jest/Vitest&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;AI 编程的核心工作流是：生成 → 运行 → 审查 → 修改 → 再运行。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这个循环中，”运行”和”打包”的速度直接决定了迭代效率。Bun 把这个等待时间从”等 10 秒”压缩到”等 1 秒”。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;迭代速度差了 10 倍，就意味着 AI 编程的效率差了 10 倍。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;更有趣的是，Bun 在 2025 年底被 Anthropic 收购，2026 年 5 月开始从 Zig 重写为 Rust。这意味着什么？意味着 Bun 正在从一个”快的 JS 运行时”变成一个”为 AI 时代设计的开发基础设施”。&lt;/p&gt;

&lt;h2 id=&quot;四rustai-时代的最佳搭档&quot;&gt;四、Rust：AI 时代的”最佳搭档”&lt;/h2&gt;

&lt;p&gt;在这个组合中，Rust 才是真正的灵魂。&lt;/p&gt;

&lt;p&gt;为什么？因为 Rust 有一个其他语言没有的特性：&lt;strong&gt;它是 AI 编程的”最佳审查者”。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;之前 Bun 从 Zig 迁移到 Rust 时，V2EX 上有人说过一句话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;“Rust 是 AI 最严厉的父亲，编译器就尽可能地先把错误报出来，避免 AI 乱来。”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI 生成的代码质量不稳定。在 C/C++ 里，一个空指针可能要运行时才崩溃。在 JavaScript 里，一个类型错误可能在线上才暴露。&lt;strong&gt;在 Rust 里，编译期就被拦截了。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这意味着：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AI 写 Rust 代码 → 编译通过 → 大概率是正确的&lt;/li&gt;
  &lt;li&gt;AI 写 C++ 代码 → 编译通过 → 可能运行时崩溃&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rust 的编译器本质上就是一个免费的、自动化的代码审查工具。&lt;/strong&gt; 在 AI 大规模生成代码的时代，这个价值被放大了无数倍。&lt;/p&gt;

&lt;p&gt;同时，Rust 原生支持异步并发，这正是 AI Agent 所需要的——并行执行多个任务、处理流式响应、管理长连接。2026 年，已经出现了专门的 Rust AI Agent 框架（如 ADK-Rust、AgentAI），让开发者能用 Rust 构建生产级的 AI Agent。&lt;/p&gt;

&lt;h2 id=&quot;五tauri-沙箱ai-agent-时代的天然边界&quot;&gt;五、Tauri 沙箱：AI Agent 时代的”天然边界”&lt;/h2&gt;

&lt;p&gt;Tauri 的沙箱机制是这个组合在 AI 时代最被低估的优势。&lt;/p&gt;

&lt;p&gt;Tauri 的安全模型是：&lt;strong&gt;前端运行在系统 Webview 沙箱中，无权直接访问系统。所有系统操作必须通过 IPC 调用 Rust 后端。Rust 后端通过白名单机制控制哪些 API 可以被调用。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当 AI Agent 需要在桌面应用中执行操作时（读写文件、调用系统 API、启动进程），Tauri 的权限模型提供了天然的隔离层：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AI 只能调用&lt;strong&gt;被明确授权&lt;/strong&gt;的 Rust 后端 API&lt;/li&gt;
  &lt;li&gt;不能直接访问文件系统、网络、进程&lt;/li&gt;
  &lt;li&gt;所有操作经过 Rust 后端的验证和过滤&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这比 Electron 里”AI 直接操作 Node.js”安全得多。&lt;/p&gt;

&lt;p&gt;对比 uni-app 等移动端跨平台框架，Tauri 提供的是&lt;strong&gt;真正的后端&lt;/strong&gt;——文件系统读写、网络请求（不经过浏览器限制）、进程管理、系统托盘、数据库操作。uni-app 所有逻辑运行在前端 JS 环境中，系统能力依赖各平台的桥接插件。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tauri 是一个完整的桌面应用框架。uni-app 是一个移动端 UI 编译框架。它们不是竞争对手，而是互补关系。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;六ai-agent-时代的新铁三角&quot;&gt;六、AI Agent 时代的”新铁三角”&lt;/h2&gt;

&lt;p&gt;把这三个组件放在一起，我们看到的不是一个简单的技术选型，而是一个&lt;strong&gt;为 AI 时代量身定制的桌面应用架构&lt;/strong&gt;：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌──────────────────────────────────────┐
│          前端（React/Vue/Svelte）       │  ← 用任意 Web 框架
│          Bun 打包 / 运行 / 测试        │  ← 极速迭代
├──────────────────────────────────────┤
│           Tauri 桥接层                 │  ← 安全沙箱 + 权限控制
│        系统原生 Webview 渲染           │  ← 3MB 起步
├──────────────────────────────────────┤
│          Rust 后端                    │  ← AI Agent 运行时
│    文件系统 / 网络 / 系统调用 / AI推理  │  ← 编译期安全
└──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;这个架构在-ai-时代的优势&quot;&gt;这个架构在 AI 时代的优势：&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. 极致的资源效率 = 给本地 AI 模型留出空间&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;当桌面应用需要内置本地 AI 模型（几百 MB 到几 GB）时，框架本身的开销就变得至关重要。Tauri + Rust 的基础开销只有几十 MB，给 AI 模型留出了充足的资源空间。Electron 本身占 200MB，加上 AI 模型就直接爆内存了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. 编译期安全 = AI 生成代码的”保险丝”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI Agent 会生成大量代码。在 Rust 的编译期安全检查下，这些代码在运行前就被验证。这相当于给 AI Agent 加了一道”保险丝”——编译不通过的代码，根本不会运行。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. 极速迭代 = AI 编程的”加速器”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bun 的包管理、打包、测试速度比传统方案快 10-40 倍。AI 每次改完代码都要重新打包运行，这个等待时间被大幅压缩。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. 权限沙箱 = AI Agent 的”行为边界”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tauri 的权限模型天然适合限制 AI Agent 的行为范围。AI 能做什么，不能做什么，由 Rust 后端的白名单决定，而不是由 AI 自己决定。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. 小体积分发 = AI 应用的”可及性”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;一个完整的 AI 桌面应用，框架开销只有几 MB。这意味着它可以被轻松分发到任何设备——包括低端笔记本、树莓派等边缘设备。&lt;/p&gt;

&lt;h2 id=&quot;七未来展望&quot;&gt;七、未来展望&lt;/h2&gt;

&lt;p&gt;这个组合的潜力，才刚刚开始被发掘。&lt;/p&gt;

&lt;h3 id=&quot;趋势一本地-ai-工具成为主流&quot;&gt;趋势一：本地 AI 工具成为主流&lt;/h3&gt;

&lt;p&gt;随着本地 AI 模型（如 Ollama、llama.cpp）的成熟，越来越多的桌面应用将内置 AI 能力。Tauri + Rust 的轻量级架构为这种趋势提供了最佳承载平台。&lt;/p&gt;

&lt;h3 id=&quot;趋势二ai-agent-成为桌面应用的标配&quot;&gt;趋势二：AI Agent 成为桌面应用的”标配”&lt;/h3&gt;

&lt;p&gt;未来的桌面应用不再是”人操作界面”，而是”人告诉 AI Agent 要做什么，Agent 自动完成”。Tauri 的沙箱机制为 AI Agent 提供了安全的运行环境，Rust 为 Agent 提供了高性能的执行引擎。&lt;/p&gt;

&lt;h3 id=&quot;趋势三从桌面应用到个人-ai-工作站&quot;&gt;趋势三：从”桌面应用”到”个人 AI 工作站”&lt;/h3&gt;

&lt;p&gt;当 Bun + Tauri + Rust + 本地 AI 模型组合在一起时，桌面应用的概念正在被重新定义。它不再是一个”工具”，而是一个&lt;strong&gt;“个人 AI 工作站”&lt;/strong&gt;——能在本地运行 AI 模型、能安全地执行复杂任务、能在离线环境下工作、能保护你的数据隐私。&lt;/p&gt;

&lt;h2 id=&quot;八写在最后&quot;&gt;八、写在最后&lt;/h2&gt;

&lt;p&gt;回顾这个组合的演进路径：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2019 年&lt;/strong&gt;：Tauri 诞生，解决 Electron 的体积问题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2022 年&lt;/strong&gt;：Tauri 1.0，证明这条路走得通。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2024 年&lt;/strong&gt;：Tauri 2.0，扩展到移动端。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2025 年&lt;/strong&gt;：Bun 被 Anthropic 收购，面向 AI 时代。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2026 年&lt;/strong&gt;：Bun 用 Rust 重写，Rust 成为 AI Agent 首选语言。&lt;/p&gt;

&lt;p&gt;每一个节点都在指向同一个方向：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;轻量、安全、快速、AI 原生。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这不是”又一个跨平台框架”的故事。这是&lt;strong&gt;“在 AI 时代，桌面应用应该是什么样子”&lt;/strong&gt;的答案。&lt;/p&gt;

&lt;p&gt;Bun 让开发变快了。&lt;/p&gt;

&lt;p&gt;Tauri 让应用变轻了。&lt;/p&gt;

&lt;p&gt;Rust 让代码变安全了。&lt;/p&gt;

&lt;p&gt;当这三者结合在一起时，它们创造的不是一个工具，而是一个&lt;strong&gt;AI Agent 可以在上面安全、高效、自由地工作的环境。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;而这，可能就是未来十年桌面应用的标准形态。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;📌 Tauri 官网：tauri.app&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;📌 Bun 官网：bun.sh&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;📌 Rust 官网：rust-lang.org&lt;/em&gt;&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">2019 年，有人受够了 Electron 的体积和内存占用，决定用 Rust + 系统 Webview 做一个轻量级的替代品。六年后，这个选择正成为 AI 时代桌面开发的默认答案。</summary></entry><entry><title type="html">普通人用小龙虾可以做什么</title><link href="https://mengtnt.com/2026/04/16/14-04.html" rel="alternate" type="text/html" title="普通人用小龙虾可以做什么" /><published>2026-04-16T06:57:51+00:00</published><updated>2026-04-16T06:57:51+00:00</updated><id>https://mengtnt.com/2026/04/16/14-04</id><content type="html" xml:base="https://mengtnt.com/2026/04/16/14-04.html">&lt;h2 id=&quot;开场白&quot;&gt;开场白&lt;/h2&gt;

&lt;p&gt;Hacker News 上有个挺火的讨论，80 多条评论，一群人围着 OpenClaw 这个 AI Agent 工具聊得热火朝天。&lt;/p&gt;

&lt;p&gt;我花了一晚上爬完了所有评论，发现一个有意思的现象：&lt;strong&gt;同样是 AI 助手，有人拿它当管家，有人拿它当秘书，还有人拿它当学习伴侣。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;今天这篇文章，我就把 HN 上大家讨论最热烈的使用场景整理出来，挑出&lt;strong&gt;TOP 3 最佳场景&lt;/strong&gt;，每个场景都给你分析清楚利弊。&lt;/p&gt;

&lt;p&gt;看完你就知道，这只”小龙虾”到底能帮你干啥。&lt;/p&gt;

&lt;!-- [IMAGE: A cute red crayfish mascot wearing glasses, sitting in front of a computer screen displaying code and notes, digital art style, warm lighting, friendly and approachable] --&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-1obsidian-笔记自动化-&quot;&gt;TOP 1：Obsidian 笔记自动化 ⭐⭐⭐⭐⭐&lt;/h2&gt;

&lt;p&gt;这是 HN 上&lt;strong&gt;提及频率最高&lt;/strong&gt;的场景，没有之一。&lt;/p&gt;

&lt;h3 id=&quot;场景长什么样&quot;&gt;场景长什么样？&lt;/h3&gt;

&lt;p&gt;想象一下这个工作流：&lt;/p&gt;

&lt;p&gt;你白天在 Obsidian 里写了几篇笔记，可能是读书笔记、会议记录、或者学习笔记。晚上睡觉前，你什么都不用做，OpenClaw 会自动：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;拉取你私有 GitHub 仓库里的 Obsidian 更新&lt;/li&gt;
  &lt;li&gt;解析新增的笔记内容&lt;/li&gt;
  &lt;li&gt;用 LLM 提取关键知识点，生成闪卡（Flashcards）&lt;/li&gt;
  &lt;li&gt;把闪卡自动导入 Anki 或你的间隔重复系统&lt;/li&gt;
  &lt;li&gt;第二天早上，你打开闪卡 App，昨天学的内容已经变成可以复习的卡片了&lt;/li&gt;
&lt;/ol&gt;

&lt;!-- [IMAGE: A clean modern workspace with a laptop showing Obsidian notes on screen, flashcards floating above the keyboard like holograms, soft blue and white color scheme, minimalist tech illustration] --&gt;

&lt;p&gt;有位叫 dsiegel2275 的用户是这么说的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我每晚让 OpenClaw 拉取私有 GitHub 仓库的 Obsidian 笔记更新，然后运行’创建闪卡’skill 提取有用的闪卡用于间隔重复练习。第二天打开闪卡 App 时，已经有昨天课程的新卡片可以复习了。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;更进阶的玩法是&lt;strong&gt;自动汇总&lt;/strong&gt;：日报 → 周报 → 月报，全自动生成。&lt;/p&gt;

&lt;h3 id=&quot;技术实现&quot;&gt;技术实现&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Git 同步 + Markdown 解析 + LLM 提取知识点&lt;/li&gt;
  &lt;li&gt;通过 API 将闪卡插入 Anki 或自定义系统&lt;/li&gt;
  &lt;li&gt;可选：自动提醒复习&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;利-&quot;&gt;利 ✅&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;真正的”无感”学习&lt;/strong&gt;：你只管写笔记，剩下的交给它&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;知识沉淀自动化&lt;/strong&gt;：不再担心笔记写完就吃灰&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;间隔重复无缝衔接&lt;/strong&gt;：学习曲线更平滑&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;弊-&quot;&gt;弊 ❌&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;需要一定的技术基础&lt;/strong&gt;：得会配置 Git 仓库、API keys&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;闪卡质量依赖 LLM&lt;/strong&gt;：有时候提取的知识点不够精准&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;初期设置耗时&lt;/strong&gt;：有用户反馈配置需要 20~30 步&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;适合谁&quot;&gt;适合谁？&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;重度 Obsidian 用户&lt;/li&gt;
  &lt;li&gt;有持续学习习惯的人&lt;/li&gt;
  &lt;li&gt;愿意花时间搭建自动化工作流的技术爱好者&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-2信息摘要与每日简报-&quot;&gt;TOP 2：信息摘要与每日简报 ⭐⭐⭐⭐&lt;/h2&gt;

&lt;p&gt;这是第二个热门场景，多位用户分享。&lt;/p&gt;

&lt;h3 id=&quot;场景长什么样-1&quot;&gt;场景长什么样？&lt;/h3&gt;

&lt;p&gt;你每天关心 Hacker News、Twitter 上的某些话题，但没时间一条条刷。OpenClaw 可以：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;定时抓取你指定的信息源（HN、Twitter、RSS 等）&lt;/li&gt;
  &lt;li&gt;根据你的兴趣主题筛选内容&lt;/li&gt;
  &lt;li&gt;用 LLM 生成摘要&lt;/li&gt;
  &lt;li&gt;在固定时间（比如每天早上 8 点）把简报发送到你的 Telegram/Slack/Discord&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;用户 lizardking 说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我让它每晚扫描 Hacker News 和 Twitter 上我感兴趣的主题，摘要故事和讨论。这是一个很棒的每日摘要。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;还有个叫 jameson 的用户用它来：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“在预定时间总结 Top 10 Hacker News 或发送每日笑话。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;更智能的是，它可以&lt;strong&gt;条件触发&lt;/strong&gt;——只有当有重要内容时才通知你，避免信息过载。&lt;/p&gt;

&lt;h3 id=&quot;技术实现-1&quot;&gt;技术实现&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;RSS/爬虫 + LLM 摘要 + 定时任务（Cron）&lt;/li&gt;
  &lt;li&gt;支持条件触发（仅在有重要内容时通知）&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;利--1&quot;&gt;利 ✅&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;信息过载的解药&lt;/strong&gt;：你只看摘要，不用刷原始信息流&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;个性化筛选&lt;/strong&gt;：只关注你真正关心的话题&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;时间可控&lt;/strong&gt;：固定时间推送，不打断工作流&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;弊--1&quot;&gt;弊 ❌&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;可能错过上下文&lt;/strong&gt;：摘要再精准，也可能漏掉重要细节&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Token 消耗大&lt;/strong&gt;：处理大量文本，成本不低&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;误判风险&lt;/strong&gt;：AI 可能把重要内容判定为”不重要”&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;适合谁-1&quot;&gt;适合谁？&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;信息焦虑症患者&lt;/li&gt;
  &lt;li&gt;需要追踪多个信息源的专业人士&lt;/li&gt;
  &lt;li&gt;想节省刷社交媒体时间的任何人&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-3邮件管理与自动化-&quot;&gt;TOP 3：邮件管理与自动化 ⭐⭐⭐⭐&lt;/h2&gt;

&lt;p&gt;第三个热门场景，尤其是对于商务人士。&lt;/p&gt;

&lt;h3 id=&quot;场景长什么样-2&quot;&gt;场景长什么样？&lt;/h3&gt;

&lt;p&gt;你的邮箱每天收到几十上百封邮件，OpenClaw 可以帮你：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;监控特定发件人或主题的邮件&lt;/li&gt;
  &lt;li&gt;自动分类和整理到不同文件夹&lt;/li&gt;
  &lt;li&gt;从发票 PDF 中提取数据，录入成本跟踪表&lt;/li&gt;
  &lt;li&gt;草稿回复邮件（需要你确认后再发送）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;用户 zsiddique 分享：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我有自动化处理 incoming 邮件并执行任务。对于某些类型的邮件和问题，我已经有心理公式，现在交给 AI 处理。它可以快速回复，有时我会发现它偏离轨道（AI 数学还是有问题），但这些都是需要快速响应的事情。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;还有个很实用的场景是&lt;strong&gt;日历检查&lt;/strong&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我让它检查 Outlook 日历，告诉我是否有任何 1:1 会议对方已经拒绝（因为 Outlook 不清晰显示这个，我经常到会才发现对方取消了）。” — superfrank&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;技术实现-2&quot;&gt;技术实现&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;IMAP/邮件 API + 规则引擎 + LLM 分类&lt;/li&gt;
  &lt;li&gt;PDF 解析（发票等）&lt;/li&gt;
  &lt;li&gt;与日历 API 集成&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;利--2&quot;&gt;利 ✅&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;减少重复劳动&lt;/strong&gt;：分类、整理、提取数据，全自动&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;不错过重要邮件&lt;/strong&gt;：优先级排序，重要邮件优先处理&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;快速响应&lt;/strong&gt;：草稿自动写好，你只需要确认&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;弊--2&quot;&gt;弊 ❌&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;隐私风险&lt;/strong&gt;：邮件包含敏感信息，需谨慎授权&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AI 可能理解偏差&lt;/strong&gt;：尤其是复杂语境下的邮件&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;配置复杂&lt;/strong&gt;：不同邮件服务商 API 不同，需要分别配置&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;适合谁-2&quot;&gt;适合谁？&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;邮件量大的商务人士&lt;/li&gt;
  &lt;li&gt;需要处理大量发票/报销的创业者&lt;/li&gt;
  &lt;li&gt;经常开会、需要日历管理的职场人&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;其他值得关注的场景&quot;&gt;其他值得关注的场景&lt;/h2&gt;

&lt;p&gt;除了 TOP 3，还有一些场景也值得提一下：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;场景&lt;/th&gt;
      &lt;th&gt;热度&lt;/th&gt;
      &lt;th&gt;一句话描述&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;个人任务管理&lt;/td&gt;
      &lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
      &lt;td&gt;语音消息添加待办，像对真人助理说话&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;家庭自动化&lt;/td&gt;
      &lt;td&gt;⭐⭐⭐&lt;/td&gt;
      &lt;td&gt;Home Assistant 崩溃了？让机器人重启它&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;代码开发部署&lt;/td&gt;
      &lt;td&gt;⭐⭐⭐&lt;/td&gt;
      &lt;td&gt;记下一个 App 想法，几分钟后就部署好了&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;语言学习&lt;/td&gt;
      &lt;td&gt;⭐⭐⭐&lt;/td&gt;
      &lt;td&gt;日语学习 + Obsidian + Anki 一体化&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;健康数据追踪&lt;/td&gt;
      &lt;td&gt;⭐⭐&lt;/td&gt;
      &lt;td&gt;卡路里追踪、天气监控、心理健康提醒&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;有个场景我觉得特别有意思——&lt;strong&gt;心理健康支持&lt;/strong&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“一个记录你最近做的酷事的 agent，并主动提醒你，帮助你从更宏观的角度看问题，对我生活中因各种原因倾向于更负面认知模式的人很有帮助。” — piazz&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这只小龙虾，还能当你的情绪管家。&lt;/p&gt;

&lt;!-- [IMAGE: A friendly AI robot assistant helping a person organize emails and calendar on a smartphone, warm orange and teal colors, modern flat design illustration, professional yet approachable] --&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;用户怎么说&quot;&gt;用户怎么说？&lt;/h2&gt;

&lt;h3 id=&quot;正面评价&quot;&gt;正面评价&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“便利性是能够在购物时要求它做某事，并让它自动测试等。” — BeetleB&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“真正的解锁是保持在一个上下文中，不会迷失在琐碎细节的长尾中。” — ryanmcgarvey&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我有 GitHub 和 Vercel 连接，这意味着我可以记下一个小生产力 App 的想法，几分钟后它就出现并部署好了。” — ryanmcgarvey&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;负面评价&quot;&gt;负面评价&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“初始设置比我想象的要耗时。OpenClaw 设置和配置是 20~30 步的情况，需要很多 API keys。” — godot&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“潜力巨大。现实是令人沮丧和不可靠的。当它工作时，你真的很喜欢它。” — BeetleB&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Token 使用量不合理。要么使用更聪明的模型（成本更高），要么使用更便宜的模型（有时会陷入循环）。” — jameson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“OpenClaw 正好处于 prompt injection 致命三要素中。OpenClaw 实例能够重置你的账户密码听起来非常可怕。” — tcoff91&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;核心争议为什么不用脚本--cron&quot;&gt;核心争议：为什么不用脚本 + Cron？&lt;/h2&gt;

&lt;p&gt;这是整个讨论里最核心的争议点。&lt;/p&gt;

&lt;h3 id=&quot;支持-agent-的人说&quot;&gt;支持 Agent 的人说：&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“写一个脚本和 crontab 条目需要更多努力，而 OpenClaw 开箱即用。”&lt;/li&gt;
  &lt;li&gt;“老板可能甚至不知道 Bash 是什么，更不用说 crontab 了。”&lt;/li&gt;
  &lt;li&gt;“是的，但我现在不需要问你（开发者）了。”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;支持脚本的人说&quot;&gt;支持脚本的人说：&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“如果我可以 prompt AI 在几分钟内写一个实际的确定性脚本来解决问题，然后永远运行它，为什么要把它委托给非确定性 AI？”&lt;/li&gt;
  &lt;li&gt;“我更喜欢如果它不按预期工作时有东西可以调试。”&lt;/li&gt;
  &lt;li&gt;“那将是有效的，如果设置定时 AI 任务真的需要任何技术知识，但它不需要。ChatGPT 让你在 UI 中原生安排任务。”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;我的看法&quot;&gt;我的看法&lt;/h3&gt;

&lt;p&gt;这其实不是二选一的问题。&lt;strong&gt;核心业务用脚本，边缘任务用 Agent&lt;/strong&gt;，可能是更务实的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;总结小龙虾适合你吗&quot;&gt;总结：小龙虾适合你吗？&lt;/h2&gt;

&lt;h3 id=&quot;适合使用-openclaw-的场景&quot;&gt;适合使用 OpenClaw 的场景&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;非技术用户&lt;/strong&gt;需要自动化但不想学习脚本&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;快速原型&lt;/strong&gt;和实验性项目&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;个人助理&lt;/strong&gt;类任务（提醒、摘要、简单查询）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;学习/实验&lt;/strong&gt;Agent 技术和多 Agent 系统&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;不适合的场景&quot;&gt;不适合的场景&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;关键业务&lt;/strong&gt;流程（需要确定性）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;高价值/高风险&lt;/strong&gt;操作（银行、密码等）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;大规模生产&lt;/strong&gt;环境（成本不可控）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;需要审计/合规&lt;/strong&gt;的场景&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;最佳实践建议&quot;&gt;最佳实践建议&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;沙箱运行&lt;/strong&gt;：在 VPS 或容器中运行，限制访问权限&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;渐进式授权&lt;/strong&gt;：从低风险任务开始，逐步增加权限&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;监控与审计&lt;/strong&gt;：记录所有操作，定期检查&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;混合架构&lt;/strong&gt;：核心业务用脚本，边缘任务用 Agent&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;成本控制&lt;/strong&gt;：设置 Token 预算，使用便宜模型处理简单任务&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;最后说两句&quot;&gt;最后说两句&lt;/h2&gt;

&lt;p&gt;HN 上有位用户预测：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“我敢打赌几年后这将成为标准做法。” — bryan0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;另一位说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“这是一个很棒的工具。想象一下拥有你自己的管家，除了可靠性与可负担性还没有达到做任何严肃事情的程度。” — jameson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI Agent 还在早期阶段，但它已经在改变一些人的工作方式了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;关键是找到适合你的场景，从小处开始，逐步扩展。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这只小龙虾能帮你干啥，最终取决于你想让它干啥。&lt;/p&gt;

&lt;hr /&gt;</content><author><name>mengtnt</name></author><summary type="html">开场白</summary></entry><entry><title type="html">Claude 三种 Agent 架构全解析：SDK vs Teams vs Managed 如何选择</title><link href="https://mengtnt.com/2026/04/12/01-04.html" rel="alternate" type="text/html" title="Claude 三种 Agent 架构全解析：SDK vs Teams vs Managed 如何选择" /><published>2026-04-12T00:00:00+00:00</published><updated>2026-04-12T00:00:00+00:00</updated><id>https://mengtnt.com/2026/04/12/01-04</id><content type="html" xml:base="https://mengtnt.com/2026/04/12/01-04.html">&lt;h1 id=&quot;claude-三种-agent-架构全解析sdk-vs-teams-vs-managed-如何选择&quot;&gt;Claude 三种 Agent 架构全解析：SDK vs Teams vs Managed 如何选择&lt;/h1&gt;

&lt;h2 id=&quot;引言&quot;&gt;引言&lt;/h2&gt;

&lt;p&gt;Anthropic 最近连续发布了三个与 Agent 相关的重磅产品：&lt;strong&gt;Agent SDK&lt;/strong&gt;、&lt;strong&gt;Agent Teams&lt;/strong&gt; 和 &lt;strong&gt;Managed Agents&lt;/strong&gt;。这一系列动作标志着 Claude 正式从”对话模型”进化为”Agent 平台”。&lt;/p&gt;

&lt;p&gt;对于开发者而言，这三个产品代表了三种截然不同的 Agent 构建范式。选择错误的架构可能导致数周的开发工作付诸东流。本文基于实际项目经验，深入剖析三种架构的技术细节、优缺点和适用场景，帮助你在项目中做出正确的选择。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一三种架构概览&quot;&gt;一、三种架构概览&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;特性&lt;/th&gt;
      &lt;th&gt;Agent SDK&lt;/th&gt;
      &lt;th&gt;Agent Teams&lt;/th&gt;
      &lt;th&gt;Managed Agents&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;控制粒度&lt;/td&gt;
      &lt;td&gt;最高&lt;/td&gt;
      &lt;td&gt;中等&lt;/td&gt;
      &lt;td&gt;最低&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;开发工作量&lt;/td&gt;
      &lt;td&gt;最大&lt;/td&gt;
      &lt;td&gt;中等&lt;/td&gt;
      &lt;td&gt;最小&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;状态管理&lt;/td&gt;
      &lt;td&gt;自己实现&lt;/td&gt;
      &lt;td&gt;共享 Task List&lt;/td&gt;
      &lt;td&gt;平台托管&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;错误处理&lt;/td&gt;
      &lt;td&gt;自己实现&lt;/td&gt;
      &lt;td&gt;部分自动&lt;/td&gt;
      &lt;td&gt;平台自动&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;适合场景&lt;/td&gt;
      &lt;td&gt;核心业务&lt;/td&gt;
      &lt;td&gt;长任务协作&lt;/td&gt;
      &lt;td&gt;简单任务&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;二agent-sdk完全控制的代价&quot;&gt;二、Agent SDK：完全控制的代价&lt;/h2&gt;

&lt;h3 id=&quot;21-架构原理&quot;&gt;2.1 架构原理&lt;/h3&gt;

&lt;p&gt;Agent SDK 是最早发布的方案，它提供了一套底层 API，让你自己编写完整的 agent loop。你需要处理：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Token 计数和截断&lt;/li&gt;
  &lt;li&gt;工具调用和结果解析&lt;/li&gt;
  &lt;li&gt;错误重试和回退&lt;/li&gt;
  &lt;li&gt;无限循环检测&lt;/li&gt;
  &lt;li&gt;中间状态持久化&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;22-代码示例&quot;&gt;2.2 代码示例&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;anthropic&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;max_iterations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 70% 的代码是防御性编程
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Token 超限检测
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;truncate_context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            
            &lt;span class=&quot;c1&quot;&gt;# 调用 Agent
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;c1&quot;&gt;# 无限循环检测
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iteration_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentLoopError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Too many iterations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;c1&quot;&gt;# 持久化中间结果
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
            
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ToolError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# 工具失败重试
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;retry_with_backoff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TokenLimitError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Token 超限处理
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress_and_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;23-实际踩坑经验&quot;&gt;2.3 实际踩坑经验&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;坑 1：Token 超限&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Claude 的 context window 虽然大，但复杂任务很容易超限&lt;/li&gt;
  &lt;li&gt;必须实现智能截断策略：优先保留最近的消息和关键工具结果&lt;/li&gt;
  &lt;li&gt;建议设置软限制（180k）而非硬限制（200k）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;坑 2：工具调用失败&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;网络波动、API 限流、工具超时都会导致失败&lt;/li&gt;
  &lt;li&gt;必须实现指数退避重试&lt;/li&gt;
  &lt;li&gt;对于幂等操作可以重试，非幂等操作需要补偿机制&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;坑 3：无限循环&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Agent 可能陷入”调用工具→失败→再调用”的死循环&lt;/li&gt;
  &lt;li&gt;需要检测重复的工具调用模式&lt;/li&gt;
  &lt;li&gt;设置最大迭代次数并强制终止&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;24-优缺点&quot;&gt;2.4 优缺点&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;完全控制 Agent 行为&lt;/li&gt;
  &lt;li&gt;可以针对业务场景深度优化&lt;/li&gt;
  &lt;li&gt;不受平台配额限制&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;业务逻辑只占 30%，70% 是 harness 代码&lt;/li&gt;
  &lt;li&gt;开发周期长，维护成本高&lt;/li&gt;
  &lt;li&gt;需要深厚的工程经验&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;三agent-teams共享状态的协作范式&quot;&gt;三、Agent Teams：共享状态的协作范式&lt;/h2&gt;

&lt;h3 id=&quot;31-架构原理&quot;&gt;3.1 架构原理&lt;/h3&gt;

&lt;p&gt;Agent Teams 的核心创新是&lt;strong&gt;共享 Task List&lt;/strong&gt;。与传统子 Agent 的”fire-and-forget”模式不同：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;所有 Agent 共享同一个任务列表&lt;/li&gt;
  &lt;li&gt;Agent A 发现的问题可以直接添加到 Task List&lt;/li&gt;
  &lt;li&gt;Agent B 实时看到并可以接手处理&lt;/li&gt;
  &lt;li&gt;任务状态对所有 Agent 可见&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;32-代码示例&quot;&gt;3.2 代码示例&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;anthropic&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 创建共享任务列表
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;agents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;researcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;coder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;reviewer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;review_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shared_state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 关键：启用共享状态
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 所有 Agent 可以看到彼此的任务
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
完成以下项目：
1. 调研最新的 Agent 框架
2. 实现核心功能
3. 代码审查和优化
&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Agent A (researcher) 可以在任务列表中添加新任务：
# &quot;发现依赖库 X 有安全问题，需要升级到 Y 版本&quot;
# Agent B (coder) 会立即看到并处理
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;33-与传统子-agent-的对比&quot;&gt;3.3 与传统子 Agent 的对比&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;传统子 Agent 模式：
主 Agent → 子 Agent A (独立执行，返回结果)
        → 子 Agent B (独立执行，返回结果)
        
问题：子 Agent 之间无法通信，A 发现的问题 B 不知道

Agent Teams 模式：
共享 Task List:
- [✓] 调研框架 (Agent A 完成)
- [→] 实现功能 (Agent B 进行中)
- [!] 发现安全问题 (Agent A 添加，等待处理)
- [ ] 代码审查 (Agent C 待处理)

优势：所有 Agent 实时看到完整上下文
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;34-实际踩坑经验&quot;&gt;3.4 实际踩坑经验&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;坑 1：任务冲突&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;多个 Agent 可能同时处理同一个任务&lt;/li&gt;
  &lt;li&gt;需要实现任务锁或状态标记&lt;/li&gt;
  &lt;li&gt;建议设置”认领”机制&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;坑 2：状态同步延迟&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;共享状态不是完全实时的&lt;/li&gt;
  &lt;li&gt;关键决策前需要显式同步&lt;/li&gt;
  &lt;li&gt;避免依赖毫秒级一致性&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;35-优缺点&quot;&gt;3.5 优缺点&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;适合长任务、多阶段项目&lt;/li&gt;
  &lt;li&gt;Agent 之间可以自然协作&lt;/li&gt;
  &lt;li&gt;人类可以随时介入查看进度&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;共享状态增加复杂度&lt;/li&gt;
  &lt;li&gt;不适合完全独立的并行任务&lt;/li&gt;
  &lt;li&gt;调试难度较高&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;四managed-agents企业级稳定-agent-的加速器&quot;&gt;四、Managed Agents：企业级稳定 Agent 的加速器&lt;/h2&gt;

&lt;h3 id=&quot;41-架构原理&quot;&gt;4.1 架构原理&lt;/h3&gt;

&lt;p&gt;Managed Agents 不是为”简单任务”设计的玩具，而是&lt;strong&gt;为企业降低 Agent 开发成本、提升稳定性&lt;/strong&gt;而生的生产级方案。&lt;/p&gt;

&lt;p&gt;核心理念：&lt;strong&gt;让企业专注于业务逻辑，而非基础设施&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;你需要做的：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;定义业务 goal&lt;/li&gt;
  &lt;li&gt;提供领域 tools（API、数据库、内部系统）&lt;/li&gt;
  &lt;li&gt;平台处理所有工程复杂性&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;harness、memory、子 Agent 编排、错误恢复、状态持久化全部由平台托管。&lt;/p&gt;

&lt;h3 id=&quot;42-代码示例&quot;&gt;4.2 代码示例&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;anthropic&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ManagedAgent&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 企业级 Agent：客户服务自动化
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customer_agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ManagedAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;处理客户咨询，包括：
    1. 查询订单状态
    2. 处理退换货申请
    3. 解答常见问题
    4. 升级复杂问题到人工客服&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;order_lookup_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# 内部订单系统 API
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;refund_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# 退款系统 API
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;knowledge_base_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 企业知识库
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;ticket_escalation_tool&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 工单系统
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;memory_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;persistent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 持久化记忆
&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;retention_days&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# 客户对话保留 90 天
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;compliance_config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;data_residency&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;EU&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 数据驻留要求
&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;audit_logging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 审计日志
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 生产环境部署
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customer_agent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;production&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scaling_policy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;auto&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 自动扩缩容
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;sla_target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;99.9&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# SLA 目标
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;43-企业级优势&quot;&gt;4.3 企业级优势&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. 稳定性保障&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;平台级错误恢复：自动重试、fallback、熔断&lt;/li&gt;
  &lt;li&gt;状态持久化：Agent 崩溃后可从中断点恢复&lt;/li&gt;
  &lt;li&gt;版本管理：Agent 配置可版本化、回滚&lt;/li&gt;
  &lt;li&gt;监控告警：内置性能监控和异常告警&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. 开发成本大幅降低&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;无需编写 harness 防御代码（节省 70% 开发量）&lt;/li&gt;
  &lt;li&gt;无需处理 token 管理、错误重试、无限循环检测&lt;/li&gt;
  &lt;li&gt;无需搭建监控、日志、追踪系统&lt;/li&gt;
  &lt;li&gt;小团队也能构建生产级 Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. 企业合规支持&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;数据驻留控制（GDPR 合规）&lt;/li&gt;
  &lt;li&gt;审计日志（金融、医疗行业要求）&lt;/li&gt;
  &lt;li&gt;访问控制和权限管理&lt;/li&gt;
  &lt;li&gt;PII 数据自动脱敏&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. 可扩展性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;自动扩缩容应对流量峰值&lt;/li&gt;
  &lt;li&gt;多区域部署降低延迟&lt;/li&gt;
  &lt;li&gt;与企业现有系统无缝集成&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;44-实际踩坑经验&quot;&gt;4.4 实际踩坑经验&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;坑 1：初期配置复杂度高&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;企业级功能需要详细配置（合规、审计、权限）&lt;/li&gt;
  &lt;li&gt;建议：从最小可行配置开始，逐步迭代&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;坑 2：自定义逻辑受限&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;核心循环逻辑由平台管理&lt;/li&gt;
  &lt;li&gt;非常规需求可能需要切换到 SDK&lt;/li&gt;
  &lt;li&gt;建议：80% 标准化流程用 Managed，20% 特殊场景用 SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;坑 3：供应商锁定风险&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;深度依赖平台特性后迁移成本高&lt;/li&gt;
  &lt;li&gt;建议：关键业务逻辑保持模型无关，tools 层抽象化&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;45-优缺点&quot;&gt;4.5 优缺点&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;开发成本降低 70%+（无需 harness 代码）&lt;/li&gt;
  &lt;li&gt;生产级稳定性（平台级错误恢复、监控）&lt;/li&gt;
  &lt;li&gt;企业合规开箱即用&lt;/li&gt;
  &lt;li&gt;小团队也能构建复杂 Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;核心逻辑控制力较弱&lt;/li&gt;
  &lt;li&gt;深度定制需求受限&lt;/li&gt;
  &lt;li&gt;存在供应商锁定风险&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;46-适用场景&quot;&gt;4.6 适用场景&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;强烈推荐：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;企业客户服务 Agent&lt;/li&gt;
  &lt;li&gt;内部知识库问答系统&lt;/li&gt;
  &lt;li&gt;标准化业务流程自动化&lt;/li&gt;
  &lt;li&gt;需要合规审计的场景（金融、医疗）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;不推荐：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;需要完全控制 Agent 行为的场景&lt;/li&gt;
  &lt;li&gt;非常规、实验性 Agent 设计&lt;/li&gt;
  &lt;li&gt;预算极其有限的小项目&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;五选型建议实战中的混搭策略&quot;&gt;五、选型建议：实战中的混搭策略&lt;/h2&gt;

&lt;p&gt;基于实际项目经验，推荐的架构选择策略：&lt;/p&gt;

&lt;h3 id=&quot;51-核心业务--agent-sdk&quot;&gt;5.1 核心业务 → Agent SDK&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;场景：&lt;/strong&gt; 支付处理、用户数据操作、关键业务流程&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;理由：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;需要完全控制错误处理&lt;/li&gt;
  &lt;li&gt;业务逻辑复杂，需要深度优化&lt;/li&gt;
  &lt;li&gt;不能接受平台配额限制&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 核心支付流程必须用 SDK
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 自定义重试、补偿、审计日志
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;retry_policy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExponentialBackoff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_retries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audit_log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuditLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_payment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 100% 控制力
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;52-跨平台分发--传统子-agent-并行&quot;&gt;5.2 跨平台分发 → 传统子 Agent 并行&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;场景：&lt;/strong&gt; 同时发布到微信公众号、Twitter、博客&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;理由：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;各平台完全独立&lt;/li&gt;
  &lt;li&gt;不需要共享状态&lt;/li&gt;
  &lt;li&gt;并行执行效率最高&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 完全独立的任务，用传统子 Agent
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;publish_to_wechat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;publish_to_twitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;publish_to_blog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gather&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;53-长任务复盘--agent-teams&quot;&gt;5.3 长任务复盘 → Agent Teams&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;场景：&lt;/strong&gt; 项目开发、代码重构、内容创作&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;理由：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;任务周期长，需要多阶段协作&lt;/li&gt;
  &lt;li&gt;可能需要人类中途介入&lt;/li&gt;
  &lt;li&gt;共享状态便于追踪进度&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 长周期项目用 Teams
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_team&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;agents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;planner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reviewer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shared_state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project_team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;重构用户认证模块&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;54-内部杂事--managed-agents&quot;&gt;5.4 内部杂事 → Managed Agents&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;场景：&lt;/strong&gt; 数据清洗、日志分析、简单报告&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;理由：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;任务简单标准化&lt;/li&gt;
  &lt;li&gt;开发效率优先&lt;/li&gt;
  &lt;li&gt;可以接受平台限制&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 简单任务用 Managed
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ManagedAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;整理昨天的错误日志&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;六接入自定义模型能力&quot;&gt;六、接入自定义模型能力&lt;/h2&gt;

&lt;p&gt;虽然 Anthropic 的三种架构默认使用 Claude 模型，但在实际生产中，你可能需要：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;降低成本：用廉价模型处理简单任务&lt;/li&gt;
  &lt;li&gt;特定能力：某个模型在特定领域表现更好&lt;/li&gt;
  &lt;li&gt;数据合规：使用本地部署模型&lt;/li&gt;
  &lt;li&gt;避免供应商锁定：保持架构的模型无关性&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;61-sdk-架构完全灵活的模型接入&quot;&gt;6.1 SDK 架构：完全灵活的模型接入&lt;/h3&gt;

&lt;p&gt;SDK 的最大优势是可以无缝切换任意模型提供商。&lt;/p&gt;

&lt;h4 id=&quot;方案-a通过-openrouter-统一接入&quot;&gt;方案 A：通过 OpenRouter 统一接入&lt;/h4&gt;

&lt;p&gt;OpenRouter 提供统一的 API 格式，支持 100+ 模型：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;openai&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# OpenRouter 兼容 OpenAI SDK
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://openrouter.ai/api/v1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;YOUR_OPENROUTER_KEY&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MultiModelAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_router&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;complex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;anthropic/claude-sonnet-4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;simple&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;google/gemini-3-flash-preview&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;deepseek/deepseek-coder-v2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;local/llama-3-70b&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 本地模型
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;complexity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;medium&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_router&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;complexity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;complex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tool_choice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;auto&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_agent_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;一个 API 接入所有模型&lt;/li&gt;
  &lt;li&gt;按实际使用付费，无需预充值&lt;/li&gt;
  &lt;li&gt;自动 fallback 机制&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;成本对比（每 1M tokens）：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;模型&lt;/th&gt;
      &lt;th&gt;输入&lt;/th&gt;
      &lt;th&gt;输出&lt;/th&gt;
      &lt;th&gt;适用场景&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Claude Sonnet 4&lt;/td&gt;
      &lt;td&gt;$3&lt;/td&gt;
      &lt;td&gt;$15&lt;/td&gt;
      &lt;td&gt;核心业务&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Gemini 3 Flash&lt;/td&gt;
      &lt;td&gt;$0.075&lt;/td&gt;
      &lt;td&gt;$0.3&lt;/td&gt;
      &lt;td&gt;简单任务&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;DeepSeek Coder&lt;/td&gt;
      &lt;td&gt;$0.14&lt;/td&gt;
      &lt;td&gt;$0.28&lt;/td&gt;
      &lt;td&gt;代码生成&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Llama 3 70B (本地)&lt;/td&gt;
      &lt;td&gt;$0&lt;/td&gt;
      &lt;td&gt;$0&lt;/td&gt;
      &lt;td&gt;数据敏感&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;方案-b本地模型部署ollamavllm&quot;&gt;方案 B：本地模型部署（Ollama/vLLM）&lt;/h4&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;openai&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 本地 Ollama 服务
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://localhost:11434/v1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ollama&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HybridAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 云端 Claude 处理复杂任务
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cloud_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Anthropic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 本地模型处理敏感数据
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_client&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_sensitive_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 敏感数据不出本地
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;completions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;llama3:70b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;complex_reasoning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 复杂推理用 Claude
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cloud_client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;方案-c模型路由策略&quot;&gt;方案 C：模型路由策略&lt;/h4&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SmartRouter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;claude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnthropicClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;gemini&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GeminiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OllamaClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;select_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 根据任务特征选择模型
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sensitive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;local&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;complexity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;claude&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;claude&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 或 deepseek
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;gemini&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 便宜够用
&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;62-agent-teams有限的模型自定义&quot;&gt;6.2 Agent Teams：有限的模型自定义&lt;/h3&gt;

&lt;p&gt;Agent Teams 目前&lt;strong&gt;仅支持 Claude 模型&lt;/strong&gt;，但可以通过以下方式变通：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 变通方案：外部模型作为&quot;工具&quot;
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;anthropic&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call_local_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;作为工具调用的本地模型&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:11434/api/generate&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;llama3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;prompt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;response&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;agents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;researcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search_tool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 本地模型作为工具被 Claude 调用
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;Agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;local_processor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_local_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;限制：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;主模型必须是 Claude&lt;/li&gt;
  &lt;li&gt;其他模型只能作为工具被调用&lt;/li&gt;
  &lt;li&gt;无法让某个 Agent 完全使用非 Claude 模型&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;63-managed-agents不支持自定义模型&quot;&gt;6.3 Managed Agents：不支持自定义模型&lt;/h3&gt;

&lt;p&gt;Managed Agents &lt;strong&gt;完全绑定 Claude 模型&lt;/strong&gt;，无法切换。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;应对策略：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;简单任务用 Managed + Claude（省心）&lt;/li&gt;
  &lt;li&gt;需要自定义模型的任务降级为 SDK 架构&lt;/li&gt;
  &lt;li&gt;在 SDK 层实现模型路由，Managed 层只处理标准化流程&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;64-实战建议混合模型架构&quot;&gt;6.4 实战建议：混合模型架构&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProductionAgentSystem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;生产环境的混合模型架构&quot;&quot;&quot;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 1. SDK 层：核心业务，支持任意模型
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core_agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SmartRouter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# 2. Teams 层：协作任务，用 Claude
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_team&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentTeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;claude-sonnet-4-20260514&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;agents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# 3. Managed 层：简单任务，用 Claude
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;simple_tasks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ManagedAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;日志分析&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ManagedAgent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;数据清洗&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;requires_custom_model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core_agent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;long_running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;simple_tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;成本优化效果（月处理 100 万 tokens）：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;策略&lt;/th&gt;
      &lt;th&gt;纯 Claude&lt;/th&gt;
      &lt;th&gt;混合模型&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;月成本&lt;/td&gt;
      &lt;td&gt;~$10&lt;/td&gt;
      &lt;td&gt;~$3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;性能&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
      &lt;td&gt;95%+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;灵活性&lt;/td&gt;
      &lt;td&gt;低&lt;/td&gt;
      &lt;td&gt;高&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;七架构对比总结&quot;&gt;七、架构对比总结&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;维度&lt;/th&gt;
      &lt;th&gt;SDK&lt;/th&gt;
      &lt;th&gt;Teams&lt;/th&gt;
      &lt;th&gt;Managed&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;开发时间&lt;/td&gt;
      &lt;td&gt;2-4 周&lt;/td&gt;
      &lt;td&gt;1-2 周&lt;/td&gt;
      &lt;td&gt;1-2 天&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;控制力&lt;/td&gt;
      &lt;td&gt;★★★★★&lt;/td&gt;
      &lt;td&gt;★★★☆☆&lt;/td&gt;
      &lt;td&gt;★☆☆☆☆&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;维护成本&lt;/td&gt;
      &lt;td&gt;高&lt;/td&gt;
      &lt;td&gt;中&lt;/td&gt;
      &lt;td&gt;低&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;适合团队&lt;/td&gt;
      &lt;td&gt;资深工程师&lt;/td&gt;
      &lt;td&gt;中级工程师&lt;/td&gt;
      &lt;td&gt;任何开发者&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;配额限制&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
      &lt;td&gt;中等&lt;/td&gt;
      &lt;td&gt;严格&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;结语&quot;&gt;结语&lt;/h2&gt;

&lt;p&gt;Anthropic 发布的三个 Agent 产品，不是让你三选一，而是提供了一套完整的工具箱：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;SDK&lt;/strong&gt; 是手术刀——精确、强大，但需要高超技艺&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Teams&lt;/strong&gt; 是协作平台——适合团队作战的长周期项目&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Managed&lt;/strong&gt; 是自动化工具——简单任务的首选&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;明智的开发者会根据场景混搭使用：核心业务用 SDK 保证控制力，跨平台任务用并行子 Agent 提升效率，长周期项目用 Teams 便于协作，日常杂事用 Managed 节省精力。&lt;/p&gt;

&lt;p&gt;记住：&lt;strong&gt;架构没有银弹，只有取舍&lt;/strong&gt;。选择最适合你当前场景的方案，而不是最酷的方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;本文基于实际项目经验总结，欢迎交流讨论。&lt;/em&gt;&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">Claude 三种 Agent 架构全解析：SDK vs Teams vs Managed 如何选择</summary></entry><entry><title type="html">Sora 退场，Seedance 进场</title><link href="https://mengtnt.com/2026/04/03/15-45.html" rel="alternate" type="text/html" title="Sora 退场，Seedance 进场" /><published>2026-04-03T07:45:35+00:00</published><updated>2026-04-03T07:45:35+00:00</updated><id>https://mengtnt.com/2026/04/03/15-45</id><content type="html" xml:base="https://mengtnt.com/2026/04/03/15-45.html">&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;摘要&lt;/strong&gt;：2026 年初，OpenAI 宣布暂停 Sora 的公开服务，而字节跳动旗下的 Seedance 2.0 却正式开启企业级公测。这一”一退一进”的背后，折射出中美两国在 AI 视频生成领域的不同战略选择。本文从技术瓶颈与成本、版权合规、地缘竞争三个维度，深度剖析两种商业化路径的底层逻辑。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;引言冰火两重天的-2026&quot;&gt;引言：冰火两重天的 2026&lt;/h2&gt;

&lt;p&gt;2026 年 3 月，AI 视频生成赛道上演了一幕颇具戏剧性的”换角戏”。&lt;/p&gt;

&lt;p&gt;一边是曾被寄予厚望的 &lt;strong&gt;OpenAI Sora&lt;/strong&gt; 悄然宣布”暂停服务”，官方声明中仅以”技术迭代与安全检查”为由，未透露复出时间表。另一边，字节跳动旗下的 &lt;strong&gt;Seedance 2.0&lt;/strong&gt; 在武汉高调宣布开启企业级公测，火山引擎”2026 Force Link AI 创新巡展”现场人头攒动，API 申请通道瞬间涌入数千家企业认证主体。&lt;/p&gt;

&lt;p&gt;这一”一退一进”，绝非偶然。它背后折射出的是中美两国在 AI 视频生成领域的&lt;strong&gt;不同战略判断&lt;/strong&gt;、&lt;strong&gt;不同合规路径&lt;/strong&gt;，以及&lt;strong&gt;不同商业化节奏&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;本文将从三个核心维度展开分析：&lt;strong&gt;大模型视频能力的技术瓶颈与成本结构&lt;/strong&gt;、&lt;strong&gt;版权与肖像权的合规策略&lt;/strong&gt;、&lt;strong&gt;中美科技竞争背景下的产业博弈&lt;/strong&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一技术瓶颈与成本理想很丰满现实很骨感&quot;&gt;一、技术瓶颈与成本：理想很丰满，现实很骨感&lt;/h2&gt;

&lt;h3 id=&quot;11-视频生成的算力黑洞&quot;&gt;1.1 视频生成的”算力黑洞”&lt;/h3&gt;

&lt;p&gt;AI 视频生成被业内戏称为”算力黑洞”。与文本生成（LLM）和图像生成（Diffusion）不同，视频生成需要同时处理&lt;strong&gt;时间维度的一致性&lt;/strong&gt;和&lt;strong&gt;空间维度的细节&lt;/strong&gt;，这对算力提出了指数级增长的需求。&lt;/p&gt;

&lt;p&gt;根据公开数据，生成一段&lt;strong&gt;60 秒 1080p&lt;/strong&gt; 的视频，Sora 需要消耗约&lt;strong&gt;12,000 个 A100 GPU 小时&lt;/strong&gt;的算力资源。即使按批发价计算，单次生成的算力成本也高达&lt;strong&gt;30-50 美元&lt;/strong&gt;。这还不包括模型训练、推理优化、存储带宽等隐性成本。&lt;/p&gt;

&lt;p&gt;Seedance 2.0 的定价策略则更为”务实”。根据火山引擎公布的公测政策，企业用户生成一段&lt;strong&gt;2 分钟科幻短片&lt;/strong&gt;的成本约为&lt;strong&gt;330 元人民币（约 45 美元）&lt;/strong&gt;。看似与 Sora 相近，但 Seedance 提供了&lt;strong&gt;分层计费&lt;/strong&gt;和&lt;strong&gt;并发优化&lt;/strong&gt;机制：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;计费维度&lt;/th&gt;
      &lt;th&gt;Sora（暂停前）&lt;/th&gt;
      &lt;th&gt;Seedance 2.0&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基础生成&lt;/td&gt;
      &lt;td&gt;按秒计费，无折扣&lt;/td&gt;
      &lt;td&gt;按 Token 计费，量大从优&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;并发限制&lt;/td&gt;
      &lt;td&gt;单账户 5 并发&lt;/td&gt;
      &lt;td&gt;企业认证 10 并发，可扩容&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;定制功能&lt;/td&gt;
      &lt;td&gt;不开放&lt;/td&gt;
      &lt;td&gt;需 100 万保证金 + 保底协议&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;退款机制&lt;/td&gt;
      &lt;td&gt;无&lt;/td&gt;
      &lt;td&gt;保证金期满可返还&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这种差异反映了两种不同的&lt;strong&gt;成本转嫁策略&lt;/strong&gt;：Sora 试图通过统一高价覆盖所有用户，而 Seedance 则通过”保证金 + 分层”将成本压力转移给有支付能力的企业客户。&lt;/p&gt;

&lt;h3 id=&quot;12-技术瓶颈的天花板效应&quot;&gt;1.2 技术瓶颈的”天花板效应”&lt;/h3&gt;

&lt;p&gt;2025 年下半年，多家 AI 实验室内部评估报告显示，当前视频生成模型在以下方面遭遇瓶颈：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;长视频一致性&lt;/strong&gt;：超过 3 分钟的视频，角色面部特征漂移率超过 15%&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;物理规律模拟&lt;/strong&gt;：复杂流体力学、光影反射等场景仍有明显瑕疵&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;多镜头叙事&lt;/strong&gt;：场景切换时的连贯性不足，需要人工后期修补&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenAI 内部人士透露，Sora 暂停服务的核心原因之一是&lt;strong&gt;技术迭代遇到瓶颈&lt;/strong&gt;。在达到”工业可用”标准之前，继续开放服务只会消耗大量算力却无法产生商业回报。&lt;/p&gt;

&lt;p&gt;反观字节跳动，其策略更为”渐进式”。Seedance 2.0 明确&lt;strong&gt;限制视频时长&lt;/strong&gt;（公测版单段不超过 5 分钟），并&lt;strong&gt;禁用真人人脸生成&lt;/strong&gt;，从而规避了最耗算力和最易引发争议的场景。这种”先做减法，再做加法”的策略，使得 Seedance 能够在可控成本下实现商业化闭环。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;二版权问题中美不同的合规护城河&quot;&gt;二、版权问题：中美不同的合规”护城河”&lt;/h2&gt;

&lt;h3 id=&quot;21-美国的诉讼驱动模式&quot;&gt;2.1 美国的”诉讼驱动”模式&lt;/h3&gt;

&lt;p&gt;Sora 的暂停，版权诉讼是重要诱因之一。&lt;/p&gt;

&lt;p&gt;2025 年 12 月，好莱坞编剧工会（WGA）与美国演员工会（SAG-AFTRA）联合发起集体诉讼，指控 Sora 在训练过程中使用了&lt;strong&gt;未经授权的影视作品片段&lt;/strong&gt;。诉讼文件显示，OpenAI 无法提供完整的训练数据来源清单，这成为其”致命软肋”。&lt;/p&gt;

&lt;p&gt;此外，多位知名导演（包括诺兰、卡梅隆等）公开表态，认为 AI 生成视频”侵犯了人类创作者的署名权与改编权”。在法律压力与舆论压力的双重夹击下，OpenAI 选择了”以退为进”。&lt;/p&gt;

&lt;h3 id=&quot;22-中国的预审--水印模式&quot;&gt;2.2 中国的”预审 + 水印”模式&lt;/h3&gt;

&lt;p&gt;相比之下，Seedance 2.0 的合规策略更为”前置化”：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;训练数据白名单&lt;/strong&gt;：字节跳动宣布其训练数据全部来自”已授权内容库”，包括抖音、西瓜视频、即梦平台等自有生态，以及与部分影视公司的合作协议。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;C2PA 水印技术&lt;/strong&gt;：所有生成视频自动嵌入&lt;strong&gt;不可见数字水印&lt;/strong&gt;，可追溯生成时间、生成主体、使用模型版本等信息。这符合欧盟《AI 法案》的透明度要求，也为未来可能的版权纠纷提供证据链。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;真人肖像”双锁”机制&lt;/strong&gt;：公测版默认禁用真人人脸生成。企业如需解锁，需缴纳&lt;strong&gt;100 万元保证金&lt;/strong&gt;并签署《肖像权合规承诺书》，一旦违规，保证金全额扣除并承担法律责任。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这种”预审 + 水印 + 保证金”的三重机制，实质上是将&lt;strong&gt;合规成本内部化&lt;/strong&gt;，让企业用户成为版权保护的”第一责任人”。&lt;/p&gt;

&lt;h3 id=&quot;23-合规成本的隐性门槛&quot;&gt;2.3 合规成本的”隐性门槛”&lt;/h3&gt;

&lt;p&gt;值得注意的是，Seedance 的合规策略虽然降低了法律风险，但也抬高了&lt;strong&gt;市场准入门槛&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;100 万元保证金对于大型影视公司而言可能只是”九牛一毛”，但对于中小微内容创作者、独立工作室而言，却是&lt;strong&gt;难以逾越的门槛&lt;/strong&gt;。这导致 Seedance 的公测用户结构呈现明显的”头部集中”特征——目前通过企业认证的客户中，超过 60% 为注册资本超过 1000 万元的企业。&lt;/p&gt;

&lt;p&gt;这种策略的长期影响尚待观察：它可能促进内容产业的”集约化”，也可能抑制创新生态的”多样性”。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;三中美竞争ai-视频赛道的地缘博弈&quot;&gt;三、中美竞争：AI 视频赛道的”地缘博弈”&lt;/h2&gt;

&lt;h3 id=&quot;31-技术封锁与去美化供应链&quot;&gt;3.1 技术封锁与”去美化”供应链&lt;/h3&gt;

&lt;p&gt;2025 年以来，美国对华 AI 芯片出口管制持续收紧。NVIDIA A100/H100 等高端 GPU 的对华出口基本冻结，这直接影响了中国 AI 企业的&lt;strong&gt;训练效率&lt;/strong&gt;与&lt;strong&gt;推理成本&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;在此背景下，Seedance 2.0 选择了一条”差异化竞争”路线：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;模型架构优化&lt;/strong&gt;：采用华为昇腾 910B 等国产芯片进行推理加速，降低对 NVIDIA 生态的依赖。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;应用场景聚焦&lt;/strong&gt;：优先落地于短视频、电商营销、教育培训等”轻量级”场景，而非好莱坞级别的”重工业”制作。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;市场区域选择&lt;/strong&gt;：国际推广首选东南亚、拉美等”非敏感”市场，暂时避开欧美监管高压区。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;32-数据生态的护城河效应&quot;&gt;3.2 数据生态的”护城河效应”&lt;/h3&gt;

&lt;p&gt;字节跳动的核心优势在于其&lt;strong&gt;庞大的内容生态&lt;/strong&gt;。抖音、今日头条、西瓜视频等平台每天产生&lt;strong&gt;数亿条视频内容&lt;/strong&gt;，这为 Seedance 提供了得天独厚的训练数据与应用场景。&lt;/p&gt;

&lt;p&gt;相比之下，OpenAI 的数据来源更为”分散”。虽然其拥有 GPT 系列模型的文本数据优势，但在视频领域的”原生数据”积累远不如字节跳动。这也是 Sora 在”多镜头叙事一致性”上表现不如 Seedance 的原因之一。&lt;/p&gt;

&lt;h3 id=&quot;33-商业化节奏的战略耐心&quot;&gt;3.3 商业化节奏的”战略耐心”&lt;/h3&gt;

&lt;p&gt;OpenAI 作为私营公司，面临来自微软等投资方的&lt;strong&gt;盈利压力&lt;/strong&gt;。Sora 的高成本与低回报，使其在商业上难以持续。暂停服务，某种程度上是”止损”的理性选择。&lt;/p&gt;

&lt;p&gt;字节跳动则展现出更强的”战略耐心”。Seedance 2.0 的公测政策明确表示，&lt;strong&gt;2026 年内不以盈利为目标&lt;/strong&gt;，而是以”生态培育”和”场景验证”为核心。这种”先占市场，再谈盈利”的策略，与中国互联网行业的传统打法一脉相承。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;四结论与展望谁能笑到最后&quot;&gt;四、结论与展望：谁能笑到最后？&lt;/h2&gt;

&lt;p&gt;Sora 的退场与 Seedance 的进场，并非简单的”胜负之分”，而是两种&lt;strong&gt;不同发展阶段&lt;/strong&gt;、&lt;strong&gt;不同市场环境&lt;/strong&gt;、&lt;strong&gt;不同战略选择&lt;/strong&gt;的体现。&lt;/p&gt;

&lt;h3 id=&quot;41-短期判断2026-2027&quot;&gt;4.1 短期判断（2026-2027）&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Seedance 将占据中国市场主导地位&lt;/strong&gt;，尤其在短视频、电商、教育等垂直场景。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sora 可能在 2027 年以”企业版”形式回归&lt;/strong&gt;，但定价将大幅提高，面向高端影视制作市场。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;开源模型将填补中低端市场空白&lt;/strong&gt;，如 Stability AI 的 Stable Video、Meta 的 Movie Gen 等。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;42-长期展望2028-及以后&quot;&gt;4.2 长期展望（2028 及以后）&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;技术融合&lt;/strong&gt;：文本、图像、视频、音频的”多模态一体化”将成为标配，单一模态模型将被淘汰。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;监管趋同&lt;/strong&gt;：中美欧三方将在 AI 版权、水印、溯源等方面形成”最低共识”，跨境合规成本下降。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;成本下降&lt;/strong&gt;：随着芯片效率提升与模型优化，视频生成成本有望下降&lt;strong&gt;1-2 个数量级&lt;/strong&gt;，个人创作者将成为主力用户。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;43-给从业者的建议&quot;&gt;4.3 给从业者的建议&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;不要押注单一模型&lt;/strong&gt;：多模型冗余部署，降低供应链风险。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;关注合规前置&lt;/strong&gt;：版权、肖像、水印等合规成本应纳入预算。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;深耕垂直场景&lt;/strong&gt;：通用视频生成竞争惨烈，垂直场景（如医疗、法律、工业）仍有蓝海。&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;结语&quot;&gt;结语&lt;/h2&gt;

&lt;p&gt;AI 视频生成的”黄金时代”才刚刚开始。Sora 的退场不是终点，Seedance 的进场也不是终点。真正的赢家，将是那些能够&lt;strong&gt;平衡技术、成本、合规&lt;/strong&gt;三者关系的玩家。&lt;/p&gt;

&lt;p&gt;对于内容创作者而言，这是一个最好的时代——工具从未如此强大；这也是一个最坏的时代——规则从未如此复杂。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;唯有拥抱变化者，方能立于不败之地。&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;</content><author><name>mengtnt</name></author><summary type="html">摘要：2026 年初，OpenAI 宣布暂停 Sora 的公开服务，而字节跳动旗下的 Seedance 2.0 却正式开启企业级公测。这一”一退一进”的背后，折射出中美两国在 AI 视频生成领域的不同战略选择。本文从技术瓶颈与成本、版权合规、地缘竞争三个维度，深度剖析两种商业化路径的底层逻辑。</summary></entry><entry><title type="html">Harness 设计</title><link href="https://mengtnt.com/2026/03/31/Harness-Design.html" rel="alternate" type="text/html" title="Harness 设计" /><published>2026-03-31T08:28:18+00:00</published><updated>2026-03-31T08:28:18+00:00</updated><id>https://mengtnt.com/2026/03/31/Harness-Design</id><content type="html" xml:base="https://mengtnt.com/2026/03/31/Harness-Design.html">&lt;h1 id=&quot;harness-设计构建长时间运行的应用程序开发框架&quot;&gt;Harness 设计：构建长时间运行的应用程序开发框架&lt;/h1&gt;

&lt;p&gt;本文是阅读 Harness 框架这篇文章后，做的一些简单的摘要总结，再配合着最近泄漏的 claud code 源码，Anthropics 真是给广大开发者送福利了。&lt;/p&gt;

&lt;p&gt;过去几个月里，我们一直在研究两个相互关联的问题：如何让 Claude 产出高质量的前端设计，以及如何让它在无需人工干预的情况下构建完整的应用程序。这项工作源于我们早期在&lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/plugins/frontend-design/skills/frontend-design/SKILL.md&quot;&gt;前端设计技能&lt;/a&gt;和&lt;a href=&quot;https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents&quot;&gt;长时运行编码 Agent 框架&lt;/a&gt;上的努力。通过提示词工程和框架（Harness）设计，我们能够将 Claude 的性能提升到远超基准线的水平，但这两者最终都遇到了瓶颈。&lt;/p&gt;

&lt;p&gt;为了突破瓶颈，我们寻求了一种新颖的 AI 工程方法，该方法横跨了两个完全不同的领域：一个由主观审美定义，另一个由可验证的正确性和可用性定义。受&lt;a href=&quot;https://en.wikipedia.org/wiki/Generative_adversarial_network&quot;&gt;生成对抗网络&lt;/a&gt;（GANs）的启发，我们设计了一个由 &lt;strong&gt;生成器(Generator)&lt;/strong&gt; 和 &lt;strong&gt;评估器(Evaluator)&lt;/strong&gt;  Agent 组成的多智能体结构。构建一个能够可靠且具备审美能力地对输出进行评分的评估器，意味着首先要开发一套标准，将诸如“这个设计好吗？”之类的主观判断转化为具体的、可量化的术语。
随后，我们将这些技术应用到了长时运行的自主编码中，并借鉴了早期框架工作的两个教训：将构建任务分解为可处理的代码块，以及使用结构化的“成品”在不同 Session 之间传递上下文。最终结果是一个三智能体架构——计划者（Planner）、生成器（Generator）和评估器（Evaluator）——它能在长达数小时的自主编码 Session 中构建出功能丰富、视觉精美的全栈应用。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“框架（Harness）的设计对长时运行的 Agent 编码有效性有着实质性影响。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;为什么-agent-的实现会失败&quot;&gt;为什么 Agent 的实现会失败？&lt;/h2&gt;

&lt;p&gt;我们之前已经证明，Harness 的设计对长时运行 Agent 编码的有效性有着实质性影响。在早期的&lt;a href=&quot;https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents&quot;&gt;实验&lt;/a&gt;中，我们使用一个初始 Agent 将产品规格书分解为任务列表，再由一个编码 Agent 逐一实现功能，最后通过转移成品来跨 Session 携带状态。开发者社区也得出了类似的见解，例如使用钩子或脚本让 Agent 处于持续迭代循环中的方法。&lt;/p&gt;

&lt;p&gt;但一些核心问题依然存在。对于更复杂的任务，Agent 随着时间的推移仍容易“跑偏”。在分析这个问题时，我们观察到了两种常见的失败模式。&lt;/p&gt;

&lt;h3 id=&quot;1-连贯性丢失与上下文焦虑&quot;&gt;1. 连贯性丢失与“上下文焦虑”&lt;/h3&gt;

&lt;p&gt;首先是模型在长任务中往往会丧失连贯性。随着上下文窗口被填满，一些模型还会表现出“上下文焦虑”——当它们认为接近上下文限制时，会过早地试图收尾工作。&lt;strong&gt;上下文重置（Context Resets）&lt;/strong&gt;——即完全清空上下文窗口并启动一个全新的 Agent，配合携带前序状态和后续计划的结构化移交——可以同时解决这两个问题。&lt;/p&gt;

&lt;p&gt;这与单纯的“压缩”（Compaction）不同。压缩保留了连贯性，但没有提供“干净的底板”，这意味着上下文焦虑依然可能存在。重置则提供了一个干净的起点，代价是移交制品必须包含足够的信息，以便下一个 Agent 能够无缝接手。&lt;/p&gt;

&lt;h3 id=&quot;2-自我评价的盲目性&quot;&gt;2. 自我评价的盲目性&lt;/h3&gt;

&lt;p&gt;第二个问题是自我评价。当被要求评价自己产出的工作时，Agent 往往会自信地赞美自己——即使在人类观察者看来，质量明显平庸。这在设计等主观任务中尤为严重，因为这里不存在像软件测试那样的二进制校验。&lt;/p&gt;

&lt;p&gt;将“干活的 Agent”与“评价的 Agent”分开，是解决这一问题的强力杠杆。这种分离本身并不能立即消除各种宽容；评估器依然是一个倾向于对 LLM 生成内容表现慷慨的大模型。但将一个独立的评估器调校得“挑剔且多疑”，要比让一个生成器批判自己的工作容易得多。一旦这种外部反馈存在，生成器就有了一个具体的迭代目标。&lt;/p&gt;

&lt;h2 id=&quot;前端设计让主观质量变得可量化&quot;&gt;前端设计：让主观质量变得可量化&lt;/h2&gt;

&lt;p&gt;我们首先在前端设计上做了实验，因为自我评价问题在这里最为明显。如果没有干预，Claude 通常会倾向于安全、可预测的布局，这些布局在技术上可行，但在视觉上平庸。&lt;/p&gt;

&lt;p&gt;我为前端设计编写了四个评估标准：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;设计质量&lt;/strong&gt;：设计是否感觉是一个连贯的整体而非零件的堆砌？色彩、排版、布局、意向等细节是否结合并创造了独特的氛围和身份？&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;原创性&lt;/strong&gt;：是否有自定义决策的证据？人类设计师应该能识别出刻意的创意选择。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;精致性&lt;/strong&gt;：技术执行力，包括层级、间距一致性、色彩和谐度、对比度。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;功能性&lt;/strong&gt;：独立于审美的可用性。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们特别强调了设计质量和原创性，因为 Claude 本身在工艺和功能性上的得分通常很高。标准中明确惩罚了高度通用的“AI 垃圾（AI slop）”模式，迫使模型在审美上进行更多冒险。&lt;/p&gt;

&lt;h2 id=&quot;扩展到全栈编码三智能体协作&quot;&gt;扩展到全栈编码：三智能体协作&lt;/h2&gt;

&lt;p&gt;基于这些发现，我将这种受 GAN 启发的模式应用到了全栈开发中。系统包含以下三种角色：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;计划者 (Planner)&lt;/strong&gt;：将模糊的指令（1-4 句话）扩展为完整的产品规格（Spec）。它专注于产品背景和高层技术设计，而不是详细的实现细节。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;生成器 (Generator)&lt;/strong&gt;：采用“冲刺（Sprint）”模式，每次只根据 Spec 开发一个特定功能。它还拥有 Git 权限用于版本控制。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;评估器 (Evaluator)&lt;/strong&gt;：这是核心。它使用 Playwright MCP 像真实用户一样点击运行中的应用，测试 UI、API 端点和数据库状态。如果任何一项标准低于阈值，冲刺即宣告失败，生成器会收到详细的 Bug 反馈。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在每次执行前，生成器和评估器会协商一份 &lt;strong&gt;冲刺合约&lt;/strong&gt; ：在写代码前双方便达成共识，确定“完成”的定义。&lt;/p&gt;

&lt;h2 id=&quot;实验从-20-分钟到-6-小时的质变&quot;&gt;实验：从 20 分钟到 6 小时的质变&lt;/h2&gt;

&lt;p&gt;我们让模型尝试创建一个“2D 复古游戏制作工具”。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;框架&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;运行时间&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;总成本&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;结果质量&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;单模型 (Solo)&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;20 分钟&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$9&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;布局浪费空间，流程死板，且游戏引擎库是坏的。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;全套 Harness&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;6 小时&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$200&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;极其精美，功能包含精灵编辑、动画系统、AI 辅助设计。最重要的是，真的可以玩。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这种差异是立竿见影的。全套 Harness 构建的应用具备了一致的视觉特征和极高的完成度。评估器通过 Playwright 进行了极其细致的测试，例如：“矩形填充工具在鼠标抬起时未能触发”、“FastAPI 路由冲突”等细微 Bug 均被捕捉并修复。&lt;/p&gt;

&lt;h2 id=&quot;简单化与未来演进&quot;&gt;简单化与未来演进&lt;/h2&gt;

&lt;p&gt;随着 Claude Opus 4.6 等更强模型的推出，我们可以开始精简框架。每增加一层复杂度，其实都是对模型“在这个环节做不到”的假设。当模型变强，这些假设就会过期。&lt;/p&gt;

&lt;p&gt;例如，Opus 4.6 已经可以原生处理长时间的连贯任务，不再需要强行切碎成多个子任务。但我们依然保留了计划者和评估器，因为它们能提供核心的价值提升。&lt;/p&gt;

&lt;h2 id=&quot;结语工具的终极意义&quot;&gt;结语：工具的终极意义&lt;/h2&gt;

&lt;p&gt;随着模型能力的提升，有趣的设计空间并不会因为模型变强而消失，而是向更高阶的自动化迈进。AI 工程师的挑战在于通过不断寻找新的组合，利用 Harness 突破模型本身的能力边界。&lt;/p&gt;

&lt;p&gt;正如我们所看到的，它不是在替你写代码，它是在帮你构建属于你自己的生产力引擎。&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">Harness 设计：构建长时间运行的应用程序开发框架</summary></entry><entry><title type="html">OpenClaw 效率提升实战</title><link href="https://mengtnt.com/2026/03/28/15-03.html" rel="alternate" type="text/html" title="OpenClaw 效率提升实战" /><published>2026-03-28T07:37:12+00:00</published><updated>2026-03-28T07:37:12+00:00</updated><id>https://mengtnt.com/2026/03/28/15-03</id><content type="html" xml:base="https://mengtnt.com/2026/03/28/15-03.html">&lt;h1 id=&quot;告别繁琐我是如何利用-openclaw-打造个人博客发布自动驾驶系统的&quot;&gt;告别繁琐：我是如何利用 OpenClaw 打造个人博客发布“自动驾驶”系统的&lt;/h1&gt;

&lt;p&gt;在数字化写作的今天，博客发布依然是许多创作者的重要阵地。今天，我想深入分享一下我是如何利用 &lt;strong&gt;OpenClaw&lt;/strong&gt; 彻底重构我的博客发布流程，将原本需要一整天的开发调试工作，压缩到寥寥数次对话即可搞定的全过程。&lt;/p&gt;

&lt;h2 id=&quot;一-提出问题那些被浪费在发布上的时间&quot;&gt;一、 提出问题：那些被浪费在“发布”上的时间&lt;/h2&gt;

&lt;p&gt;我刚开始写博客的时候，发布流程可以用“支离破碎”来形容。我比较喜欢 Obsidian 工具来作为我的写作工具，我的博客时基于 GitHub + Jekyll 的静态博客系统，但每次从写作的灵感形成文章，并且到 GitHub 上的正式上线，都要经历一段极其乏味的“手工折磨”：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;元数据（Frontmatter）的格式地狱&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Jekyll 博客需要严格的 YAML 头信息，包括 &lt;code class=&quot;highlighter-rouge&quot;&gt;title&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;date&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;layout&lt;/code&gt; 等。在 Obsidian 里，我更习惯随心所欲地组织内容，这意味着每次发布前，我得手动计算当前的 ISO 时间戳，核对文件名格式，稍有不慎，博客引擎就会因为解析错误而拒绝编译。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;图片附件的搬运工&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这是流程中最耗时的部分。Obsidian 的图片通常保存在专门的附件文件夹中。发布时，我必须：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;手动找到这些图片。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;复制到博客项目的 &lt;code class=&quot;highlighter-rouge&quot;&gt;images&lt;/code&gt; 文件夹。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 Markdown 里将相对路径（或是 Wikilinks 格式）手动修改为博客项目的路径（如 &lt;code class=&quot;highlighter-rouge&quot;&gt;/images/my_photo.png&lt;/code&gt;）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果遇到同名图片，还得小心翼翼地重命名并更新所有引用。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;版本控制的重复劳动&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;最后，我得切换到终端，输入那套已经滚瓜烂熟却毫无生趣的命令：&lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;git commit -m &quot;update&quot;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt;。如果遇到远程仓库有更新，还得处理冲突。&lt;/p&gt;

&lt;p&gt;之前我都是通过写过 Shell 脚本来做这些枯燥的发布工作，但脚本的维护成本极高：路径配置、异常处理、逻辑扩展……每当我想增加一个小功能时，都得面对那一坨凌乱的代码重新调试。有时候发布一篇文章的心理负担，甚至盖过了写作本身的乐趣。&lt;/p&gt;

&lt;h2 id=&quot;二-重点介绍openclaw--obsidian-的配置全过程&quot;&gt;二、 重点介绍：OpenClaw + Obsidian 的配置全过程&lt;/h2&gt;

&lt;p&gt;这就是本文的核心所在。我整个过程一行 shell 脚本都没写。而是在带娃去游乐场用手机做出来的过程。下面就介绍下如何通过 &lt;strong&gt;OpenClaw&lt;/strong&gt; 的 &lt;strong&gt;Skill-Creator&lt;/strong&gt; 打造了一个名为 &lt;code class=&quot;highlighter-rouge&quot;&gt;blog-publisher&lt;/code&gt; 的智能技能。&lt;/p&gt;

&lt;h3 id=&quot;1-技能构思描述即开发&quot;&gt;1. 技能构思：描述即开发&lt;/h3&gt;

&lt;p&gt;在 OpenClaw 中，我不需要编写繁琐的逻辑判断。我只需要以人类的语言告诉它：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“创建一个叫 blog-publisher 的技能。它需要能自动搜寻 Obsidian 里的文章，处理图片迁移，自动生成 Frontmatter，最后执行 Git 提交并推送到 nas 分支。如果推送失败，要自动执行强制推送。”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这种“描述即开发”的模式，直接跳过了传统编程中的语法纠结，让我能专注于业务逻辑本身。&lt;/p&gt;

&lt;h3 id=&quot;2-核心逻辑配置多技能联动&quot;&gt;2. 核心逻辑配置：多技能联动&lt;/h3&gt;

&lt;p&gt;我的 &lt;code class=&quot;highlighter-rouge&quot;&gt;blog-publisher&lt;/code&gt; 技能并不是孤立存在的，它巧妙地调用了 OpenClaw 已有的生态：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Obsidian 联动&lt;/strong&gt;：利用 &lt;code class=&quot;highlighter-rouge&quot;&gt;obsidian-official-cli&lt;/code&gt; 技能，通过命令行与我的笔记库直接对话，实现毫秒级的文章检索。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;环境韧性&lt;/strong&gt;：我为技能配置了严格的 &lt;code class=&quot;highlighter-rouge&quot;&gt;git pull&lt;/code&gt; 预处理。这意味着无论我在哪台设备上工作，OpenClaw 都能保证我面对的是最新的代码。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-细节配置脚本化注入&quot;&gt;3. 细节配置：脚本化注入&lt;/h3&gt;

&lt;p&gt;为了追求极致的稳定性，我让 OpenClaw 为该技能生成了一个 Python 核心脚本 &lt;code class=&quot;highlighter-rouge&quot;&gt;publish.py&lt;/code&gt;。这个脚本专门负责最棘手的图片处理逻辑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;正则扫描&lt;/strong&gt;：自动识别 Markdown 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;![]()&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;[[]]&lt;/code&gt; 格式引用。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;冲突检测&lt;/strong&gt;：如果目标 &lt;code class=&quot;highlighter-rouge&quot;&gt;images&lt;/code&gt; 文件夹已存在同名图片，脚本会自动采用 MD5 或计数器方案重命名，并反向更新 Markdown 中的链接。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;路径重写&lt;/strong&gt;：统一将路径前缀修正为 &lt;code class=&quot;highlighter-rouge&quot;&gt;/images/&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-严格规范执行命令的洁癖&quot;&gt;4. 严格规范：执行命令的“洁癖”&lt;/h3&gt;

&lt;p&gt;在配置过程中，我利用 OpenClaw 对技能进行了微调，强制要求所有的 Git 提交必须经过我的审查确认，这种严格的规范确保了博客仓库提交记录的整洁，避免了 AI 生成随机提交信息的混乱。&lt;/p&gt;

&lt;h2 id=&quot;三-使用感受效率的降维打击&quot;&gt;三、 使用感受：效率的“降维打击”&lt;/h2&gt;

&lt;p&gt;回顾今天使用 OpenClaw 的整个过程，最让我震撼的是其带来的生产力释放。&lt;/p&gt;

&lt;h3 id=&quot;1-从天到分钟的飞跃&quot;&gt;1. 从“天”到“分钟”的飞跃&lt;/h3&gt;

&lt;p&gt;以往如果要从零开发这样一套完整的、具备健壮图片处理和异常重试逻辑的发布系统，即使是资深开发者，从编写核心代码、处理各种特殊字符路径的 Corner Case，到最终在服务器上稳定运行，起码需要投入一整天的时间和精力。&lt;/p&gt;

&lt;p&gt;但在 OpenClaw 中，我仅仅花费了不到一百个提示词（Prompt）进行沟通和微调，它就完美地生成了文档、脚本并完成了打包。这种效率提升不是百分之几，而是量级（Order of Magnitude）的跨越。&lt;/p&gt;

&lt;h3 id=&quot;2-真人案例游乐场里的自动驾驶发布&quot;&gt;2. 真人案例：游乐场里的“自动驾驶”发布&lt;/h3&gt;

&lt;p&gt;最不可思议的场景发生在今天下午：&lt;strong&gt;我正带着孩子在游乐场玩耍，坐在喧闹的长椅上休息。就在这时，我通过手机端的 Telegram 客户端，像聊天一样给 OpenClaw 发送了指令：“把 OpenClaw 效率提升实战 这篇文章发布。”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;几秒钟后，OpenClaw 向我反馈：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;✅ Obsidian 同步完成。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;✅ 找到文章并生成 Frontmatter。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;✅ 自动处理了文中的 Mermaid 图表。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;✅ 脚本成功将更改推送到 nas 分支。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;整个过程，我手机的亮屏时间不到一分钟，而背后却完成了一系列复杂的开发运维动作。文章现在已静静地躺在我的 GitHub 博客仓库中，等待着全球读者的访问。&lt;/p&gt;

&lt;h2 id=&quot;四-结语工具的终极意义&quot;&gt;四、 结语：工具的终极意义&lt;/h2&gt;

&lt;p&gt;OpenClaw 给我带来的不仅仅是时间上的节省，更是一种“掌控感”。它消除了技术琐碎带来的焦虑，让我能重新回到创作的本质。如果你也觉得自己的工作流中有太多繁琐的手工环节，不妨尝试把它们交给 OpenClaw。它不是在替你写代码，它是在帮你构建属于你自己的生产力引擎。&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">告别繁琐：我是如何利用 OpenClaw 打造个人博客发布“自动驾驶”系统的</summary></entry><entry><title type="html">如何用 Metal 做视频帧合流渲染</title><link href="https://mengtnt.com/2024/10/01/metal-render.html" rel="alternate" type="text/html" title="如何用 Metal 做视频帧合流渲染" /><published>2024-10-01T02:00:19+00:00</published><updated>2024-10-01T02:00:19+00:00</updated><id>https://mengtnt.com/2024/10/01/metal-render</id><content type="html" xml:base="https://mengtnt.com/2024/10/01/metal-render.html">&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;

&lt;p&gt;最近在做远程控制的功能，使用的技术方案是远程控制的鼠标数据流和桌面画面视频流分开传输。这样观看端就需要把鼠标的画面和桌面的画面进行合并渲染。如果在数据层面做利用 FFmpeg 框架就可以实现两路视频流的合并。这个可能在直播场景比较适合，由于视频会议对实时性要就比较高，用户在远程控制时，延时忍受度较低，所以为了体验就采用了客户端端在渲染时，来合并两路流进行显示。&lt;/p&gt;

&lt;p&gt;这里就引申到本文讨论的话题了，在 iOS 平台用 Metal 如何来渲染两路视频流到一个视图上？&lt;/p&gt;

&lt;h1 id=&quot;1-metal-是如何渲染图形的&quot;&gt;1. Metal 是如何渲染图形的&lt;/h1&gt;

&lt;p&gt;首先我们先看下图形如何通过GPU显示出来的。下面是一个简单的示意图。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/93b28736-b6e0-4e9d-9c17-a252c07d582c.png&quot; alt=&quot;Description&quot; style=&quot;width:640px; height:600px;&quot; /&gt;
渲染过程通过  CPU 给 GPU 发送相应的渲染指令，然后把数据拷贝到 GPU 中，用 GPU 渲染上屏。从上图的过程可以看出，图形渲染的过程 CPU 负责组装指令，而 GPU 负责上屏渲染这个过程。
那Metal 其实就是封装了 GPU 图形化的接口，方便开发者调用 GPU 的能力。整个渲染过程和大多数图形框架 OpenGL Vulkan 基本相似，我们来看下这个流程。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;代码上配置的 GPU 指令，这个配置过程是在 CPU 中执行的，有兴趣可以看下&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide&quot;&gt;官方文档&lt;/a&gt;。下图是 Metal 框架中可以配置的命令的种类。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/ffb7030f-d848-4fd4-b8f3-040349563bc2.png&quot; alt=&quot;&quot; /&gt;
上图中位块命令编码器（Blit Command Encode）是用于处理数据传输和图像处理的操作。它允许开发者高效地在 GPU 上执行内存拷贝、图像缩放、图像转换等任务。 计算命令编码器(Compute Command Encoder)用于编码在 GPU 上执行计算内核的命令。可以执行通用计算，例如图像处理、机器学习或任何并行化的任务。而我们做图形渲染主要关心的是渲染编码器(Render Command Encoder)如何使用的，渲染编码器需要配置的有顶点坐标数据、纹理数据、采样信息、深度信息然后发送到 GPU中，从此图中也可以看出来，除了图形处理外，同时 Metal 还可以做机器学习相关的计算。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;下面看下渲染指令的配置过程，下图就描述了一个渲染指令的创建过程
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/9861328c-bdee-41f3-bddf-56194d9a2b46.png&quot; alt=&quot;Description&quot; style=&quot;width:600px; height:600px;&quot; /&gt;
其中可以看到 Vertex Function 和 Fragment Function 就是 GPU 可以被编程的核心，通过注入顶点和片段着色器就可以利用 GPU 自定义渲染的逻辑。Metal 会把这些指令编译优化后，配置到 GPU中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最后就开始执行 Metal 的渲染 ，下图就是渲染出最终图形这个过程中 GPU 需要执行的各个阶段，也就是我们常说的渲染管道 PipLine。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/bb113629-6b01-4240-a42c-833d4f5ce74e.png&quot; alt=&quot;&quot; /&gt;
渲染管道的流程就是从上面我们配置的数据和自定义的渲染逻辑，然后计算出相应位置的像素点位信息，经过深度和模版测试(本质上就是对深度信息、透明度、以及叠加视图进行裁剪和融合操作)，渲染到缓冲区，最终根据屏幕的刷新率渲染上屏。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上述就是 Metal 渲染 Pipline 的基本描述，有兴趣可以下载&lt;a href=&quot;https://developer.apple.com/documentation/metal/using_a_render_pipeline_to_render_primitives?language=objc&quot;&gt;官方示例代码&lt;/a&gt;。从上面的配置过程可以看出，开发 Metal 渲染要做的两件事情，在 CPU 中配置渲染的 GPU 指令，通过框架提供的可编程PipLine，编写自定义的 Shader 给 GPU，执行定制化的渲染。&lt;/p&gt;

&lt;h1 id=&quot;2-webrtc-的渲染架构&quot;&gt;2. WebRTC 的渲染架构&lt;/h1&gt;

&lt;p&gt;在我们开始写视频合流的逻辑时，先来看下我们音视频会议用的 WebRTC 框架，是如何使用 Metal 来渲染的？下图是 WebRTC Metal 渲染的类图。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/8cb4e6e6-f80f-46b2-a8e7-55b3a635679e.png&quot; alt=&quot;&quot; /&gt;
从上图看还是比较简单的，获取解码后的视频数据帧，传递给 RTCMTLVideoView 后，RTCMTLVideoView 通过 DisplayLink 定时器指定渲染帧率，然后根据视频帧的不同颜色格式，分发给不同的 Render 渲染器执行渲染。&lt;/p&gt;

&lt;p&gt;然后我们鼠标合流的过程可以用下面的流程图描述下。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/66eb0c46-227a-485f-8d67-2369ea2a5c0b.png&quot; alt=&quot;Description&quot; style=&quot;width:400px; height:600px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;流程图中鼠标渲染的逻辑也很简单，在渲染视频帧的时候，发现相应时间点有鼠标帧过来时，就通过 Metal 合流的方式渲染上屏。下面我们就详细来看下 Metal 渲染上屏的代码。&lt;/p&gt;

&lt;h1 id=&quot;3-metal-渲染示例代码&quot;&gt;3. Metal 渲染示例代码&lt;/h1&gt;

&lt;h2 id=&quot;31-渲染管道配置&quot;&gt;3.1 渲染管道配置&lt;/h2&gt;
&lt;p&gt;开发 Metal 渲染的代码，先看下 CPU 如何配置渲染管道这个过程，下面是一个示例代码。&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonnull&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instancetype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initWithMetalKitView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonnull&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTKView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mtkView&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mtkView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;NSURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imageFileLocation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSBundle&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mainBundle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;URLForResource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;background&quot;&lt;/span&gt;
                                                           &lt;span class=&quot;nl&quot;&gt;withExtension:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tga&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;_texture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loadTextureUsingAAPLImage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageFileLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Set up a simple MTLBuffer with vertices which include texture coordinates&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AAPLVertex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quadVertices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Pixel positions, Texture coordinates&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;

            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Create a vertex buffer, and initialize it with the quadVertices array&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_vertices&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newBufferWithBytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quadVertices&lt;/span&gt;
                                         &lt;span class=&quot;nl&quot;&gt;length:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quadVertices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                        &lt;span class=&quot;nl&quot;&gt;options:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLResourceStorageModeShared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Calculate the number of vertices by dividing the byte length by the size of each vertex&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_numVertices&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;/// Create the render pipeline.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Load the shaders from the default library&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLLibrary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultLibrary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newDefaultLibrary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLFunction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertexFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultLibrary&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFunctionWithName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vertexShader&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLFunction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fragmentFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultLibrary&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFunctionWithName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;samplingShader&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Set up a descriptor for creating a pipeline state object&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;MTLRenderPipelineDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLRenderPipelineDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Texturing Pipeline&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertexFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertexFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fragmentFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fragmentFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colorAttachments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelFormat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mtkView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colorPixelFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;NSError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_pipelineState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newRenderPipelineStateWithDescriptor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipelineStateDescriptor&lt;/span&gt;
                                                                 &lt;span class=&quot;nl&quot;&gt;error:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;NSAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pipelineState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to create pipeline state: %@&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_commandQueue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCommandQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;通过上面的代码可以看出，主要是在配置顶点向量和着色器函数。我们画如下的图来看下顶点坐标是如何定义的。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/7027766f-c806-4579-8c28-1a28bc071f41.png&quot; alt=&quot;Description&quot; style=&quot;width:400px; height:400px;&quot; /&gt;
可以看出来中心点是 (0,0) 。因为我们视频帧只用了 2D 坐标，如果 3D 坐标的话，坐标系如下。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/20022f4f-759f-4fc3-af2b-7758a2ad885f.png&quot; alt=&quot;Description&quot; style=&quot;width:400px; height:400px;&quot; /&gt;
Metal 渲染使用的是左手坐标系，如果是OpenGL右手坐标系，Z 轴就是反向的，个人感觉左手更符合人的直觉。&lt;/p&gt;

&lt;p&gt;这里我们要注意的配置渲染顶点坐标的类型，用的 MTLPrimitiveTypeTriangle。MTLPrimitiveTypeTriangleStrip  和  MTLPrimitiveTypeTriangle  是 Metal 框架中用于指定图形渲染顶点数据类型的枚举值。它们之间的主要区别在于如何处理顶点数据以形成三角形。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;MTLPrimitiveTypeTriangle 这种类型表示独立的三角形。在绘制时，每三个顶点组成一个三角形。因此如果你有 N 个三角形，你需要提供 3 * N 个顶点。 每个三角形的顶点之间没有共享，所有的三角形都是独立的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;MTLPrimitiveTypeTriangle，这种类型表示对三角形顶点做适当裁剪。在绘制时，第一个三角形由前两个顶点和第三个顶点组成，之后的每个新顶点都会与前两个顶点一起形成一个新的三角形。 这样绘制 N 个三角形只需要 N + 2 个顶点，效率更高，因为可以减少顶点数据的传输。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MTLPrimitiveTypeTriangle 用于独立三角形，而  MTLPrimitiveTypeTriangleStrip  用于通过共享顶点来高效地绘制一系列相连的三角形。选择哪种类型取决于你的具体需求和数据结构。下面的这个顶点坐标类型就是完整的三角形模式，后面可以看到我们实际项目中用的是 MTLPrimitiveTypeTriangleStrip ，因为视频帧是 2D 平面比较简单，可以用4个顶点代表一个正方形。&lt;/p&gt;

&lt;h2 id=&quot;32-渲染命令提交&quot;&gt;3.2 渲染命令提交&lt;/h2&gt;
&lt;p&gt;提交渲染命令给 GPU 的的过程中。看下面的示例代码。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawInMTKView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonnull&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTKView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Create a new command buffer for each render pass to the current drawable&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLCommandBuffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_commandQueue&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyCommand&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Obtain a renderPassDescriptor generated from the view's drawable textures&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MTLRenderPassDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderPassDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentRenderPassDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderPassDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLRenderCommandEncoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderCommandEncoderWithDescriptor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderPassDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyRenderEncoder&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Set the region of the drawable to draw into.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setViewport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLViewport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setRenderPipelineState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pipelineState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setVertexBuffer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_vertices&lt;/span&gt;
                                &lt;span class=&quot;nl&quot;&gt;offset:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
                              &lt;span class=&quot;nl&quot;&gt;atIndex:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setVertexBytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;
                               &lt;span class=&quot;nl&quot;&gt;length:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_viewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                              &lt;span class=&quot;nl&quot;&gt;atIndex:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AAPLVertexInputIndexViewportSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Set the texture object.  The AAPLTextureIndexBaseColor enum value corresponds&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;///  to the 'colorMap' argument in the 'samplingShader' function because its&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//   texture attribute qualifier also uses AAPLTextureIndexBaseColor for its index.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setFragmentTexture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Draw the triangles.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawPrimitives&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLPrimitiveTypeTriangle&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;vertexStart:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;vertexCount:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderEncoder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Schedule a present once the framebuffer is complete using the current drawable&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presentDrawable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentDrawable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Finalize rendering here &amp;amp; push the command buffer to the GPU&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commandBuffer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_numVertices];&lt;/code&gt;这里就可以看到我们提交给 GPU 的顶点向量的类型为 MTLPrimitiveTypeTriangle ，所以正方形的话需要2个三角形来表示。这里还有个注意点，我们顶点向量传递的是实际像素的话，需要额外传递下 _viewportSize ，因为我们之后片段着色器用到的顶点位置其实都是相对位置，不是实际的像素位置，主要是为了方便纹理贴图时，计算纹理位置。之后我们也会讲到。&lt;/p&gt;

&lt;h2 id=&quot;33-数据传递&quot;&gt;3.3 数据传递&lt;/h2&gt;
&lt;p&gt;内存数据传输，例如 &lt;code class=&quot;highlighter-rouge&quot;&gt;[texture replaceRegion:region mipmapLevel:0 withBytes:image.data.bytes bytesPerRow:bytesPerRow];&lt;/code&gt; 给片段着色器传递数据，本质上就是 CPU 把内存的数据给 GPU 的内存。这里要注意颜色格式问题，一定要约定好像素的格式，MTLPixelFormatBGRA8Unorm 表示的32位的纹理数据。不然数据拷贝的时候会因为格式问题导致拷贝的数据 GPU 无法处理。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLTexture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loadTextureUsingAAPLImage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;AAPLImage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AAPLImage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initWithTGAFileAtLocation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to create the image from %@&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;absoluteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MTLTextureDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLTextureDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelFormat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTLPixelFormatBGRA8Unorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Set the pixel dimensions of the texture&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Create the texture from the device by using the descriptor&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLTexture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTextureWithDescriptor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Calculate the number of bytes per row in the image.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSUInteger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytesPerRow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;MTLRegion&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;                   &lt;span class=&quot;c1&quot;&gt;// MTLOrigin&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// MTLSize&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Copy the bytes from the data object into the texture&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;replaceRegion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;
                &lt;span class=&quot;nl&quot;&gt;mipmapLevel:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
                  &lt;span class=&quot;nl&quot;&gt;withBytes:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;
                &lt;span class=&quot;nl&quot;&gt;bytesPerRow:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytesPerRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上面是纹理数据配置的代码，我们讲下纹理坐标位置，纹理本质就是贴图，因为图片都是2D的，所以一般都是用 float2 向量定义的。float2 这种变量是 SIMD （单指令多数据结构）代表的就是2维的平面向量(x,y)。下图就是纹理坐标的定义。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/09/bb468dac-1a0d-4d92-afdc-50fe7e90f247.png&quot; alt=&quot;Description&quot; style=&quot;width:600px; height:400px;&quot; /&gt;
纹理坐标都是用相对位置计算的，这样着色时方便和顶点向量位置进行计算，尤其涉及到缩放和位移时，相对位置的优势体现出来。&lt;/p&gt;

&lt;h2 id=&quot;34-着色器的编写&quot;&gt;3.4 着色器的编写&lt;/h2&gt;
&lt;p&gt;下面就是顶点着色器和片段着色器代码编写了。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;我们就可以看下顶点着色器如何工作了，下面是顶点着色器代码。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vertex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RasterizerData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;vertex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RasterizerData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vertexPassthrough&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vertex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verticies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]],&lt;/span&gt;
                                   &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;RasterizerData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vertex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verticies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// Get the viewport size and cast to float.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewportSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewportSizePointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

       &lt;span class=&quot;c1&quot;&gt;// To convert from positions in pixel space to positions in clip-space,&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;//  divide the pixel coordinates by half the size of the viewport.&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;// Z is set to 0.0 and w to 1.0 because this is 2D sample.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vector_float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pixelSpacePosition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewportSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先来看几个概念，&lt;code class=&quot;highlighter-rouge&quot;&gt;float4 position [[position]];&lt;/code&gt; 这个是属性限定的语法结构，顾名思义就是这个属性限定它的使用方式。position 代表就是从顶点向量中获取到的裁剪空间位置，用来输出裁剪空间的位置信息给片段着色器。如何理解裁剪空间位置，裁剪的过程其实就是把我们定义好的顶点向量构成三角形，然后根据传递的视图实际像素大小，进行像素插值裁剪，然后计算出相应的位置。例如下面这个顶点着色器函数 vertexPassthrough ，其中定义的变换输出的结构 Varyings out; 这个变量的值赋值是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;const device Vertex &amp;amp;v = verticies[vid];&lt;/code&gt; 而这里面的 vid 本质上就是实际屏幕的像素位置，比如你的屏幕是320 px，采样的时候就会从 0 到 320 计算三角形实际的位置，然后赋值给 out。这里也可以看到实际像素都做了相对位置的变换，方便之后使用。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;顶点着色器的输出，就是片段着色器的输入。VertexIn in [[stage_in]]; 表示该变量是顶点着色器的输出，传入已经插值裁剪好的像素点位，给片段着色器使用，以便于给每个点位分配颜色值。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;samplingShader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RasterizerData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stage_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;texture2d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;half&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colorTexture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sampler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureSampler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mag_filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                      &lt;span class=&quot;n&quot;&gt;min_filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Sample the texture to obtain a color&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colorSample&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colorTexture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureSampler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureCoordinate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colorSample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// return the color of the texture&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上面 texture2d&lt;half&gt; colorTexture [[ texture(0) ]] 这个属性限定符的作用，从我们在管道中配置的片段着色器数据的索引，例如：``` [renderEncoder setFragmentBuffer:_kernelSizeBuffer offset:0 atIndex:0];  ``` 这个就是获取我们这个配置的纹理数据。然后读取纹理中颜色值渲染到相应的顶点坐标位置。至此整个渲染过程就完成了。&lt;/half&gt;&lt;/p&gt;

&lt;h1 id=&quot;4-视频流混合的过程&quot;&gt;4. 视频流混合的过程&lt;/h1&gt;

&lt;p&gt;渲染的基本流程梳理完成后，那我们看下鼠标纹理数据如何渲染到视频上的，其中核心就是设置鼠标的顶点坐标和纹理数据，以及在 Shader 中融合鼠标纹理和视图画面的纹理。我们先看下 WebRTC 视频帧顶点向量的定义。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropBottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;coordX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropBottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coordX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;coordY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropLeft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;coordX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;coordY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropRight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cropTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中 coordX ，coordY 这些都是裁剪过的视频帧的坐标位置，在 WebRTC 中这些顶点向量都是用相对位置来表示。所以我们在设置鼠标的顶点位置时，也用相对位置如下：&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupTexturesForMouseFrame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonnull&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RTCMouseCursorFrame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;incomingFrame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MTLTextureDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLTextureDescriptor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textureType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTLTextureType2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pixelFormat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTLPixelFormatBGRA8Unorm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;textureDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MTLTextureUsageShaderRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_cursorTexture&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_device&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newTextureWithDescriptor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cursorTexture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;RTCLogError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[RND]MTLRender:%p Failed to create cursor texture&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 纹理数据&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cursorTexture&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;replaceRegion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MTLRegionMake2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                      &lt;span class=&quot;nl&quot;&gt;mipmapLevel:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
                        &lt;span class=&quot;nl&quot;&gt;withBytes:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mouseFrameBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rgbaData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                      &lt;span class=&quot;nl&quot;&gt;bytesPerRow:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mouseFrameBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// 顶点数据的相对位置&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;memcpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cursorBlendRectBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后下面就是融合的片段着色器。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half4&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fragmentColorBlend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Varyings&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stage_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;texture2d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;texture2d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureCbCr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;texture2d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureBlend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;constant&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]],&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// (left, top, right, bottom)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;constant&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enableBlend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sampler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clamp_to_edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sampler&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendSampler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clamp_to_edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureCbCr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;344&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;714&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;770&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enableBlend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factorW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factorH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendTextureCoordinate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factorW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;texcoord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factorH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendResult&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;textureBlend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blendSampler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendTextureCoordinate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;float4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blendResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;half4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;融合的方法很简单。这里我们用了个取巧的方式，直接把鼠标的顶点相对位置存储到了片段着色器的 buffer  中。然后在片段着色器执行时，从 buffer 读取鼠标上下左右的顶点 blendRect，只要判断鼠标的顶点落入到了 blendRect 区域中。就从鼠标纹理中 textureBlend 获取相应的颜色值，通过向量运算返回融合的纹理数据。这里需要注意是纹理采样的数据都是2维的向量。但是片段着色器输出的是4维的向量分别表示 (r,g,b,a)，这个过程中要注意向量的定义，避免赋值造成的错误。&lt;/p&gt;

&lt;h1 id=&quot;5-总结&quot;&gt;5. 总结&lt;/h1&gt;

&lt;p&gt;通过上面的分享基本可以了解用 Metal 如何把画面渲染上屏了。可以看到正是 Metal 框架的封装，才让我们很容易的利用 GPU 的资源渲染画面。虽然视频处理 Metal 渲染往往是 2D，但是了解其工作原理，对于我们做 3D 方面的渲染还是很有借鉴意义。当然如果要做 3D 渲染工作，其实还有很多复杂的工作要做，例如配置顶点向量的法线，光照以及纹理材质的深度的融合等等。这个过程往往会交给 3D 引擎来做，建模师只需要把模型配置好，加载模型后引擎来解析这些数据配置给 GPU。&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">背景</summary></entry><entry><title type="html">VisionPro所需的SwiftUI</title><link href="https://mengtnt.com/2024/03/02/vision-swiftui.html" rel="alternate" type="text/html" title="VisionPro所需的SwiftUI" /><published>2024-03-02T02:00:19+00:00</published><updated>2024-03-02T02:00:19+00:00</updated><id>https://mengtnt.com/2024/03/02/vision-swiftui</id><content type="html" xml:base="https://mengtnt.com/2024/03/02/vision-swiftui.html">&lt;h2 id=&quot;1-背景&quot;&gt;1. 背景&lt;/h2&gt;
&lt;p&gt;在我们开发 Vision Pro 开发之前，先大致了解下 SwiftUI 框架是非常有必要的，不然很多系统的新特性苹果往往只会提供 SwiftUI 的演示代码，甚至有些 API 只能 SwiftUI 才能使用。所以想要更好的适配 Vision Pro 最好的方式是用 SwiftUI 进行开发。&lt;/p&gt;

&lt;p&gt;SwitUI 几个特点相比大家都有所耳闻：声明式编程、数据驱动、自适应布局。我们来看下数据驱动的 UI 框架一个经典的公式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/6fa0034e-2f2a-427f-a5c8-bfe4cb7a0b64.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个公式就一目了然了，UI 的展示就是数据状态的一个函数映射，所有的数据驱动的 UI 框架都遵循这个原则。那么 SwiftUI 的框架又是什么样子的呢？下面针对 SwiftUI 几个重要特征的由来做个介绍，限于篇幅详情的学习可以参看 &lt;a href=&quot;[https://](https://developer.apple.com/tutorials/swiftui)&quot;&gt;官方文档&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-dsl语法&quot;&gt;2. DSL语法&lt;/h2&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;systemName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;shareplay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于熟悉 Swift 语言，但是初次接触 SwiftUI 的同学来说，上面的语法大概率会有下面的疑惑：“为啥 body 这个函数没有 return 任何变量？” 假如说我们把上面的这段代码，改为下面的形式，相信做过 iOS 开发的同学一眼都能看明白。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;textView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;imageView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;systemName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;shareplay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;container&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addSubView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;textView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addSubView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imageView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其实这种语法就是 SwifUI 描述视图布局的领域专用语言 (DSL)，在编译时就会把这种 DSL 转换成我们上面可以看懂的视图布局方式。DSL 的好处就是简化语法，只需要描述问题即可不用提供问题的解决过程，然后框架层会自动生成解决问题的繁琐代码。这看起来其实有点类似数据库的 SQL 语句，只要描述下我要解决的问题即可，大大节省了代码量，让代码可读性也变的更好。&lt;/p&gt;

&lt;p&gt;那么就有下个疑问了，SwiftUI 这种 DSL 语法是如何构建的呢？答案是通过 ViewBuilder 构建出来的。要了解 ViewBuilder 这里我们就需要了解 Swift 语言标记语法了，何为标记语法？说白了就是可以帮我们在语法树中插入一段我们自定义的代码，方便我们简化代码的结构，我简单的用下面的图示表示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/281f8d1d-d21b-4fb6-aebe-45ff0a8949bd.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中图 1 就代表正常的语法树，而图 2 就代表我们自定义的语法树，标记语法就相当于给了我们一个能力，把语法树上的一个节点替换成我们自定义的语法树的内容。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// The type of view representing the body of this view.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// When you create a custom view, Swift infers this type from your&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// implementation of the required ``View/body-swift.property`` property.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;associatedtype&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// The content and behavior of the view.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// When you implement a custom view, you must implement a computed&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// `body` property to provide the content for your view. Return a view&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// that's composed of built-in views that SwiftUI provides, plus other&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// composite views that you've already defined:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     struct MyView: View {&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///         var body: some View {&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///             Text(&quot;Hello, World!&quot;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///         }&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///     }&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// For more information about composing views and a view hierarchy,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// see &amp;lt;doc:Declaring-a-Custom-View&amp;gt;.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@ViewBuilder&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@MainActor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面这段代码就是 body 的声明，可以看出来这个函数传递的参数是一个 ViewBuilder 。然后 SwiftUI 通过 ViewBuilder 的能力重新构造语法树，从而把添加视图的过程给实现了。ViewBuilder 是通过 Swift 语言中 ResultBuilder 这个标记语法能力构建的，ResultBuilder 是 Swift 语言提供给开发者构建自定义 DSL 的能力。而 ResultBuilder 这个语法又是从 FunctionBuilder 延伸出来的，他们的演化历史简单的表述如下：&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果你想要详细了解苹果标记语法的能力，建议你观看 &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10253/&quot;&gt;apple result builder&lt;/a&gt; 这个 WWDC 的视频会有详细的讲解，这里不再赘述。&lt;/p&gt;

&lt;p&gt;通过上面的分析过程，我们其实可以归纳出，支持高阶函数的语言，本质上构建这种语法应该都不困难。在前端开发看来，SwiftUI 这种 DSL 是他们很早就具备的能力，例如 React 和 VUE 都有自己定义的 DSL。并且像 google 的跨平台框架 Flutter 也是用的声明式的标记语法实现的，还有最近华为的纯血鸿蒙系统，前端 UI 构建也采用了声明式的框架。可见声明式的 UI 框架还是非常受欢迎的。&lt;/p&gt;

&lt;h2 id=&quot;3-视图树&quot;&gt;3. 视图树&lt;/h2&gt;

&lt;p&gt;下面我们就来看下 SwiftUI 的视图树是如何通过上述的 DSL 构建的。&lt;/p&gt;

&lt;h3 id=&quot;31-链式语法&quot;&gt;3.1 链式语法&lt;/h3&gt;
&lt;p&gt;我们来看一个简单的例子如下：&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;backgound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;我们再来看下这段代码，我们把链式调用的位置交换了下，代码如下：&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;来看下两段代码运行的结果：左一就是第一段代码生成的视图，右一是第二段代码的视图。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/86f27896-c29e-4cd1-863f-ba02bf541291.png&quot; alt=&quot;Image Description&quot; width=&quot;600&quot; height=&quot;auto&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为什么会不同呢？分析这个视图的构建过程，SwiftUI 的视图构建是一个递归的过程，根据视图树上面所有子视图的尺寸决定视图的大小，再根据链式调用自下而上的构建相应的视图，每个链式操作其实都会创建一个子视图，第一段代码的构建过程如下：
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/0029e12d-de6a-4362-9482-b052fd5c7bb7.png&quot; alt=&quot;Image Description&quot; width=&quot;400&quot; height=&quot;auto&quot; /&gt;
可以看出来确定完视图大小后，会先放置 &lt;code class=&quot;highlighter-rouge&quot;&gt;.backgound&lt;/code&gt; 这个子视图，然后再放置它的子视图 &lt;code class=&quot;highlighter-rouge&quot;&gt;.padding&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;.color&lt;/code&gt;，最后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;.text&lt;/code&gt; 是属于 &lt;code class=&quot;highlighter-rouge&quot;&gt;.padding&lt;/code&gt; 的子视图，所以自然背景色范围就比较广。下图就是第二段代码的构建过程。
&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/8f04a6ec-a385-4e14-8fad-f8146e0e19c4.png&quot; alt=&quot;Image Description&quot; width=&quot;400&quot; height=&quot;auto&quot; /&gt;
可以看出来是先构建了 &lt;code class=&quot;highlighter-rouge&quot;&gt;.padding&lt;/code&gt; 这个子视图，然后再放置 &lt;code class=&quot;highlighter-rouge&quot;&gt;.backgound&lt;/code&gt; 子视图，自然背景就被裁剪掉了。
&lt;strong&gt;每个链式调用本质上就是创建一个子视图，然后自下而上构建视图的&lt;/strong&gt; ，记住这个构建规则，对于你写 SwiftUI 的代码会有很大的帮助。那我们可以自定义链式语法的函数么？这就涉及到另一个概念「修饰器」，下面我们来看下。&lt;/p&gt;

&lt;h3 id=&quot;32-修饰器-viewmodifer&quot;&gt;3.2 修饰器 (ViewModifer)&lt;/h3&gt;

&lt;p&gt;修饰器相当于给视图增加了扩展能力。下面就是扩展视图背景的一个例子。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meterial&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pureColor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dtBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;modifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;dtBackgroundEffectModifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dtBackgroundEffectModifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ViewModifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effectType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;glass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glassBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;displayMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;always&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;meterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regularMaterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pureColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;uiColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dt_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withHexString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;29231F&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的 switch case 语法给修饰器创建了不同的视图，这样调用的地方就只要一行代码 &lt;code class=&quot;highlighter-rouge&quot;&gt;Text(&quot;name&quot;).dtBackgroundEffect(effectType:effect)&lt;/code&gt; 就可以了，避免了写大量这种重复的代码，对于代码复用这种方式非常有用。不过看到这里你可能会有疑问， 扩展视图能力类似下面这样的写法不是也可以么？&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dtBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effectType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;glass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glassBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;displayMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;always&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;meterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regularMaterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pureColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;uiColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dt_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withHexString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;29231F&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码初看没啥问题，但是编译的话就会报错 &lt;code class=&quot;highlighter-rouge&quot;&gt;Function declares an opaque return type 'some View', but the return statements in its body do not have matching underlying types&lt;/code&gt; 。从编译报错里面可以明确看出，虽然返回的都是 &lt;code class=&quot;highlighter-rouge&quot;&gt;some View&lt;/code&gt; 但是 Swift 语言对类型检测非常严格的，我们来看下 Background 定义。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;@inlinable&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignoresSafeAreaEdges&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Edge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ShapeStyle&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里返回的 some view 类型会有一个传递的模版参数 S 的定义，如果模版类型不一致的话 Swift 就会认为他们类型本质上不一样的。如何来解决这个问题，其实我们只要增加 ViewBuilder 标记语法，用如下的方式就可以编译通过了。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;@ViewBuilder&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dtBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;effectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BGType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effectType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;glass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glassBackgroundEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;displayMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;always&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;meterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regularMaterial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pureColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;uiColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dt_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;withHexString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;29231F&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里就可以看出来 ViewBuilder 其实会重新组装我们的 View 成为一个确定的类型，就不会报错了。那么问题来了，ViewModifer 和用普通的 Extension Function 来扩展有什么大的区别呢？这里简单描述下用法的区别。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;如果你代码重用的部分比较复杂，涉及到很多条件创建不同的 View，这时候就适合 ViewModifer ，因为在性能方面，ViewModifier它可以利用 SwiftUI 的优化机制，避免不必要的视图层次重建。而我们自定义的扩展就没这个能力了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如果你定义的方法只用在特定的一个 View 上，不需要大量的重用，并且扩展相对比较简单，其实就没必要使用 ViewModifer。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;像上面这种需要用 switch case 来创建各种不同的视图来修饰的话，就比较适合用 ViewModifer 了，视图树的构建方式讲到这里，那么数据如何驱动这些视图渲染呢？下面就开始分析下 SwiftUI 的状态管理。&lt;/p&gt;

&lt;h2 id=&quot;4-状态管理&quot;&gt;4. 状态管理&lt;/h2&gt;

&lt;p&gt;我们先来看下面这段代码，SwiftUI 是如何映射数据状态到视图树上面的?&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@state&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;showChinese&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;showChinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;你好&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;backgound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;showChinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;red&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我简单的画了一个数据和视图树的依赖关系图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://oss-ata.alibaba.com/article/2024/02/e35c1d33-f7b5-4544-aa6a-4b75b5924bc2.png&quot; alt=&quot;Image Description&quot; width=&quot;600&quot; height=&quot;auto&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从上面的代码可以看出，模型是通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;@state&lt;/code&gt; 这种语法来映射到 SwiftUI 的视图树上的，那么 &lt;code class=&quot;highlighter-rouge&quot;&gt;@state&lt;/code&gt; 的作用我们下面来简单分析下。&lt;/p&gt;

&lt;h3 id=&quot;41-数据驱动的标记语法&quot;&gt;4.1 数据驱动的标记语法&lt;/h3&gt;

&lt;p&gt;SwiftUI 中&lt;code class=&quot;highlighter-rouge&quot;&gt;@state&lt;/code&gt; 这种语法，其实属于属性包装器 (Property Wrapper),也是 Swift 语言的一个语法特性。如果想要详细了解这个语法的话，可以参看 &lt;a href=&quot;[https://](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/)&quot;&gt;官方文档&lt;/a&gt;。它的作用就是当标记的数据有变化时，通知视图树来刷新 UI。&lt;/p&gt;

&lt;p&gt;类似这种属性包装器的语法，SwiftUI 还有很多个，&lt;code class=&quot;highlighter-rouge&quot;&gt;@State @StateObject @Binding @ObservedObject&lt;/code&gt; 。记住这些语法都是用来做数据驱动 UI 使用的。至于这些属性包装器具体如何使用，下面列出来了一些规则可供参考。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;初始化属性
 如果属性无法在声明时进行初始化，而是需要接收父节点的数据，应该考虑使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@Binding&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;@ObservedObject&lt;/code&gt;，而不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;@State&lt;/code&gt; 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;@StateObject&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;普通属性 vs. 属性包装器
 尽可能在视图中使用普通属性，当仅需将值传递到视图中时，不需要使用属性包装器。&lt;/li&gt;
  &lt;li&gt;@State
 当视图需要对一个值进行读写访问，并且该值作为本地的私有视图状态时，应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@State&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;@Binding
 当视图需要对一个值进行读写访问，但该值不属于视图本身，而是由外部传递进来时，应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@Binding&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;@StateObject 和 @ObservedObject
 当视图需要以对象的形式拥有状态，并且这个状态是本地私有的视图状态时，应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@StateObject&lt;/code&gt;。当需要从外部传入一个对象时，应该使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@ObservedObject&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;注意:上面这样原则其实是 iOS17 之前需要遵守的，之后语法有了相应的更新，使用起来会更加的简单。等下我们就会讲到 iOS17 带来的更新。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;在我们定义模型对象时，还有个重要的概念要讲 Identifiable Protocol （身份协议）。因为 SwiftUI 所有的视图树都是值类型，不同于 UIKit 每个 UIView 在内存中都有唯一的地址。而 SwiftUI 中的 View 是和映射的模型绑定到一起，框架是依靠模型的不同来确定是否为不同的视图，而如何确定模型的身份就是靠 Identifiable Protocol 这个协议，我们我就拿官网的示例代码展示下，Identifiable 作用。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Identifiable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Animal&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UUID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dataBaseID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FavoritePets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;PetView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面这个方式构建的 SwiftUI 的视图会出现一个问题，每次向 pets 中添加新的模型时，整个列表都会刷新，所有的视图重新创建了。再看下面的代码。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FavoritePets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Pet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataBaseID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;PetView&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果在创建视图时，改成上面的实现方式，给一个确定的 id 例如 dataBaseID 是一个持久化到数据库中。就不会出现上述的问题，所以我们在实现模型的 Identifiable 协议时，一定要牢记他就是对应 SwiftUI 的视图。&lt;/p&gt;

&lt;p&gt;如果想要详细了解数据如何驱动视图树的原理，建议参看苹果 WWDC &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10022/&quot;&gt;揭开 SwiftUI 的神秘面纱&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;42-ios17-之后的变化&quot;&gt;4.2 iOS17 之后的变化&lt;/h3&gt;

&lt;p&gt;为什么要讲 iOS17 之后 SwiftUI 的变化呢？因为 visionOS 使用的 SwiftUI 就是 iOS17 之后的。我们来看下对比 iOS17 前后状态驱动语法的变化。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;iOS17 之前&lt;/li&gt;
&lt;/ol&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;类型&lt;/th&gt;
      &lt;th&gt;根状态&lt;/th&gt;
      &lt;th&gt;子状态&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;值类型&lt;/td&gt;
      &lt;td&gt;@State&lt;/td&gt;
      &lt;td&gt;@Binding&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;引用类型&lt;/td&gt;
      &lt;td&gt;@StateObject&lt;/td&gt;
      &lt;td&gt;@ObservedObject&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;ol&gt;
  &lt;li&gt;iOS17 之后&lt;/li&gt;
&lt;/ol&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;类型&lt;/th&gt;
      &lt;th&gt;根状态&lt;/th&gt;
      &lt;th&gt;子状态&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;值类型&lt;/td&gt;
      &lt;td&gt;@State&lt;/td&gt;
      &lt;td&gt;@Binding&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;引用类型&lt;/td&gt;
      &lt;td&gt;@State&lt;/td&gt;
      &lt;td&gt;@Bindable&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;从上面可以看出来 iOS17 之后 SwiftUI 状态绑定的用法更简单了。iOS17 之前如果你想要一个全局的数据源，驱动 UI 变化需要写下面的代码？&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Combine&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 1. 定义全局数据源&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 单例对象&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 2. 订阅数据变化&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;globalData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Counter: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;globalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Increment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 修改全局数据源的值&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;iOS17 之后的 Swift 语法有个重大更新，就是支持了宏。然后在 SwiftUI 中， 新增了 @Observable 宏定义，开发者完全不用使用 Combine 框架的数据绑定能力了，只需要简单的写下面的代码：&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;@Observable&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 单例对象&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;再使用 iOS17 之后 SwiftUI 为 View 新增的 &lt;code class=&quot;highlighter-rouge&quot;&gt;environment&lt;/code&gt; API，创建 View 的时候简单的调用  &lt;code class=&quot;highlighter-rouge&quot;&gt;ContentView.environment(self.GlobalData.shared)&lt;/code&gt;，然后视图就可以用下面的代码监听数据变化。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@available&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iOS&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;14.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tvOS&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watchOS&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;10.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Observable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 订阅数据变化&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;globalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Counter: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;globalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Increment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 修改全局数据源的值&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那如果说我们创建的 Model 某些属性不需要监听怎么办？比较好的方式定义为 let 常量，如果说需要变量存储中间状态可以使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;@ObservationIgnored&lt;/code&gt; 这种标记语法。&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@Observable&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;@ObservationIgnored&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tempValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;shared&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;GlobalData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 单例对象&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样当 tempValue 变化时就不会触发通知，对控制 UI 频繁刷新很有用途。&lt;/p&gt;

&lt;h2 id=&quot;5-总结&quot;&gt;5. 总结&lt;/h2&gt;

&lt;p&gt;如果上面说的这些能力都掌握的话，开发 SwiftUI 基本就没什么问题了。虽然说 SwiftUI 可以大大节省 UI 的开发量。但是开发过程中遇到的棘手问题就是调试。在 UIKit 所有的 UIView 都有内存地址，来确定某个视图什么时候创建、销毁、以及放置到那里。所以 UIKit 调试的时候，只要看下内存地址就很方便定位是那个视图，但是 SwiftUI 中的视图 View 你无法打印内存地址，就像上文说的你只能跟踪他的模型状态，但是在 Debug 过程中，往往视图的变化堆栈不会有模型变化的堆栈，在调试一些 UI 层级问题时就会遇到很多问题。&lt;/p&gt;</content><author><name>mengtnt</name></author><summary type="html">1. 背景 在我们开发 Vision Pro 开发之前，先大致了解下 SwiftUI 框架是非常有必要的，不然很多系统的新特性苹果往往只会提供 SwiftUI 的演示代码，甚至有些 API 只能 SwiftUI 才能使用。所以想要更好的适配 Vision Pro 最好的方式是用 SwiftUI 进行开发。</summary></entry></feed>