知鱼
子非鱼,焉知鱼之乐

《The Stack》播客:Nvidia Dynamo 深度解析

trylab 发布于 2025-9-6 13:43    14 次阅读

问题意识

Interview with NVIDIA Dynamo Architect Kyle Kranen 发布于 2025年3月19日

2025-09-06 批注

阅读收获:

  1. 掌握Nvidia Dynamo如何通过解耦推理,将LLM吞吐量提升数十倍,有效降低运营成本。
  2. 理解全局KV缓存管理与智能路由机制,优化长序列和多轮对话场景下的推理效率。
  3. 探索Dynamo在平衡模型质量、推理延迟与计算成本方面的策略,为AI应用落地提供新思路。
  4. 了解Dynamo的开源特性及与主流LLM服务框架的集成方式,加速自身项目实践。

开放性问题:

  1. Nvidia Dynamo的解耦推理、KV缓存管理和智能路由三大核心技术,在实际部署中可能面临哪些挑战?如何权衡其带来的性能提升与系统复杂性?
  2. 随着智能体工作流的普及,LLM推理的令牌生成量将呈指数级增长。除了Dynamo,未来还需要哪些技术创新才能有效应对这一挑战?
  3. Dynamo通过降低智能成本解锁更多应用场景。你认为在哪些“临界点”领域,廉价的AI智能将带来颠覆性变革?

《The Stack》播客:Nvidia Dynamo 深度解析

纳特: 大家好,我是来自 Nvidia 的纳特。

卡特: 我是来自 Nvidia 的卡特。

凯尔: 大家好,我是来自 Nvidia 的凯尔。今天在《The Stack》播客的第二集中,我们将与 Dynamo 架构师凯尔,哦不,是 Dynamo 架构师之一的凯尔,一起聊聊 Nvidia Dynamo。

凯尔: 谢谢邀请我来。Dynamo 终于发布了,我终于可以这样说了,感觉真好。

纳特: 看到和听到你们为此努力了几个月,真是令人兴奋。

凯尔: 是啊,大概是去年年初的时候我们开始着手,现在能发布出来感觉很棒。

纳特: 有趣的是,我们第一次见面,也就是我们刚加入公司的时候,我们坐下来聊的就是推理优化(inference optimizations)等等。也许从宏观层面来说,Dynamo 到底是什么?

凯尔: 这是一个很好的问题。Dynamo 是一个分布式推理框架(distributed inference framework),在我看来,它能从你正在处理的部署规模中,榨取或获得显著的性能提升。基本上,当你谈论部署时,如果你有更多的用户,你就需要更多的 GPU(图形处理器)来为他们提供令牌(tokens),这些令牌是你的产品,或者说是你产品的一部分。在 Dynamo 的案例中,如果你试图托管这项服务,并且用户增多,你就会不断增加更多的 GPU。Dynamo 的目的,至少从我的角度来看,是确保你拥有正确的杠杆(levers),以便能够有效地优化你所运行的 GPU 数量,更高效地利用你正在使用的数百个 GPU 来提供令牌。

纳特: 我觉得有趣的是,如果我们把视角拉远,你在 GPU 上可以做的事情包括微调(fine-tune)、训练(train)、构建新模型(build new models),然后你就可以使用它们,我们称之为推理(inference)。当你进行推理时,你会说你最终可能会使用更多的计算资源,因为你会有更多的用户,就像模型真正在生产环境中运行一样?

凯尔: 我会给出一个长答案,因为我认为这值得花时间。推理是一个非常有趣的空间,因为有三件事决定了你是否可以将模型用于给定的应用程序。其中两件事是模型质量(quality of the model),即模型是否能够完成被要求执行的任务。你会在许多推理模型(reasoning models)中看到这一点,推理模型用于提高输出的质量。

纳特: 此外,我认为模型大小(size)也很重要,因为当很多人听到“模型质量”时,他们可能会想:“好吧,是什么让这个模型比那个更好?”但此外,如果你有 80 亿参数的模型,对比 700 亿参数或 450 亿参数的模型,这可能也会决定这个模型是否能够满足你的需求。

凯尔: 是的,这实际上引出了下一点,那就是另一个维度是成本(cost)。如果你有一个大模型,大模型通常或在很多情况下需要更多的计算资源和更多的 GPU 来提供服务,因此每个生成的令牌的成本更高。第三个维度是延迟(latency)。如果你的应用程序需要快速响应,比如你正在与一个聊天机器人(chat bot)互动,如果你希望它在合理的时间内响应,你还必须关心应用程序的延迟。这三个杠杆是当今 LLM(大型语言模型)推理的核心,即找到正确的系统以及正确的模型来服务你的应用程序。

我喜欢从几个方面来思考这个问题,稍后我可以深入探讨 Dynamo 如何影响这些杠杆。但让我们先思考一下推理模型。当你进行推理时,你实际上是在生成最终输出中不包含的令牌,它们被一个特殊的思考标签(thinking tag)包围,你可以将其剥离并弃用。你正在牺牲应用程序的延迟以获得更好的质量,你也在牺牲成本,因为你正在生成更多的令牌。

纳特: 这就像我使用 R1 时,它需要很长时间,并且告诉我它正在思考。所以你是说,是的,我们获得了更好的质量,但代价是需要更长的时间才能得到输出,代价是 GPU 运行了更多的周期?

凯尔: 完全正确。如果你正在调用 R1,并且它需要一段时间,它可能正在后台生成大量的令牌。正因为如此,你将需要更长的时间才能产生答案,你还将生成更多的令牌,这显然会占用 GPU 的时间,转化为功耗或金钱,你正在承担更大的成本。

实际上,还有很多其他方法可以修改这些维度。例如,你可以权衡成本和延迟。一个常见的例子是推测(speculation),它基本上是使用一个小模型(smaller model)来预测一个大模型(larger model)会说什么,然后使用大模型来验证这些令牌是否应该被接受,或者哪组令牌应该被接受。在这种情况下,你权衡的是你实际上生成了更多的令牌,这在某种程度上类似于推理,你生成了更多的令牌,但你实际上是利用这些令牌来加快速度,而不是利用这些令牌来产生更好的质量输出。

纳特: 这我都没听说过,太有趣了。在我看来,这就像,如果你是一个分析师,正在制作一些最终可能会被高管批准的东西,就好像那是他们自己的东西一样,这有点像,初级员工(junior employee)来做会更便宜,但高管仍然必须确认:“这确实是高质量的,我愿意为此署名。”

凯尔: 任何创作过程都是如此,你只是把一堆东西扔到墙上,然后雕刻出你真正想要的东西。是的,完全正确。这只是操纵推理以满足你应用程序的众多技术之一。

我想用这个来结束,并回到你最初的问题,你问到,现在这些模型在推理方面越来越好,越来越大,有时甚至越来越小,推理是如何发展的?我们正在获得更小但能力更强的模型,比如今天的 70B 模型,其准确性与三四年前的 405B 或万亿参数模型相当。我们正在更好地学习如何训练模型。这意味着,实际上,因为我们正在改进所有这些维度,你可以,或者你可以将应用程序空间视为受到某些特定约束的限制,它必须这么快,必须花费这么多,并且必须产生这么好的输出。随着质量的提高,成本的降低,以及延迟的改善,你解锁了越来越多的应用程序。所以这有点像,你拥有越来越大的区域来获取任务,对推理的需求也越来越大。

纳特: 有趣的是,我不知道这是否属实,但我一直以来的理论是,2024 年代码生成用例(code gen use cases)之所以如此出色,是因为你相对安全地避免了一些延迟问题和质量问题,因为如果它吐出糟糕的代码,我会在那里告诉它让它变得更好。我会忍受延迟,因为它比我手动编写语法要快。

凯尔: 是的,有太多现实世界的用例被解锁了。我认为这是一个非常好的例子,当时模型的特定属性非常适合代码生成。另一个很好的例子可能是,我们还没有的一个例子是,如果你有一个 LLM 能够为某种疾病提供个性化护理,比如它们正在读取你的基因代码(genetic code),并开发出一种可以在实验室中制造的合成化合物(synthetic compound),并治愈你的疾病,这非常有价值,以至于这样做的延迟和成本如此之高,以至于你似乎应该百分之百地一直这样做。这有点像这种权衡是如何运作的,存在这些临界点(break points),要么它变得如此便宜,要么质量变得如此好,要么它变得如此快,以至于它解锁了一个新的应用程序。我们解锁新应用程序的速度,以及这个三维空间(因为记住,它是三个维度)的增长速度正在加速,因为我们有更多的方法可以权衡维度,而且有更多,有更好的模型。

纳特: 所以 Dynamo 是分布式推理,我仍然有很多问题,我很高兴能深入探讨,当它回到那三个维度时。所以对于 R1,我获得了更好的质量,但延迟更高。那么 Dynamo 是否能让我们在使用推理模型(如 DeepSeek R1)时获得更好的延迟?

凯尔: 是的,这是一个很好的问题。首先,答案是肯定的。但它实际上更复杂一些,因为你同时有两个维度。现在我们假设模型是固定的。你有延迟和成本这两个维度。我将使用它们被描述的术语,即系统的总吞吐量(total throughput)和用户端的 TPS(Tokens Per Second,每秒令牌数)。Dynamo 允许你使用解耦(disaggregation)、改进的 KV 缓存路由(improved KV cache routing)、全局 KV 缓存管理(Global KV cache management)等技术,我们稍后会深入探讨,来确保你以前想要达到的延迟目标,现在能够聚合地提供更多的令牌,从而降低了成本。

纳特: 那是隔离延迟,还是你能再解释一下?

凯尔: 是的,假设你有一个给定的 SLA(服务水平协议),并且你有一个没有运行 Dynamo 且没有利用 Dynamo 所使用的技术的现有系统。它固定在某个延迟上,并且你使用一定数量的 GPU 在该延迟下提供服务。Dynamo 基本上为该延迟下的令牌生成提供了一个乘数,为该机器上在该延迟下生成的总令牌提供了一个乘数。Jensen 在他的主题演讲中展示了这条曲线,称为帕累托前沿(Pareto Frontier),它是潜在延迟的集合,或者说是用户总 TPS 的集合,即用户延迟与总 TPS(成本指标)的对比。它看起来像这样,它看起来像一个弧线,从生成大量令牌但每个用户非常慢,到几乎不生成令牌但为单个用户生成令牌的速度很快。你可以进行这种权衡,你可以在该前沿上选择一个点,这就是 Dynamo 所做的。你可以从两个方面来思考它,要么它使 Y 轴加倍,要么它将 X 轴向外推。

纳特: 我有一种思考方式,因为我认为 Nvidia Dynamo 的定位,在我看来,它有点像推理的物流(logistics),就像供应链(supply chain)一样。所以,如果你考虑模型优化(model optimizations),就像我们在 R1 中看到的那样,那就像你在工厂里制造了一台机器,使其更高效。

凯尔: 是的。

纳特:Dynamo 所做的是确保将包裹交付给最终用户的整体物流供应链更加高效,速度更快(AI factory 的核心框架)

凯尔: 完全正确。这就像工厂里的机器与工厂本身。Dynamo 就像那个工厂,它试图改进自己以生产更多的输出。而模型本身,或者模型由创建它的人表示的方式,就是机器。是的,所以你只是,这是一个很好的思考方式。如果你工厂里有一堆机器,你如何让这些机器以最佳方式运行,以生产最多的输出?这在很多方面都很好,它经济高效,对环境更好,因为每个令牌的功耗更低,而且它普遍提高了效率。

纳特: 那么你刚才提到的三个优化是什么?

凯尔: 好的,需要明确的是,这并非全部三个,但周二发布的三大优化是解耦(disaggregation),它基本上是将 LLM 生成的两个阶段分开以提高效率。

解耦基本上是一种改进的 LLM 流水线技术(pipelining technique)。当 LLM 生成令牌时,有输入,然后有输出。当你读取输入时,你需要做的一件事是为注意力机制(attention)(搜索算法) 生成这些 KV 值(KV values)。如果有人听说过 QKV 注意力机制(QKV attention),我们谈论的就是 K 和 V。我们需要为注意力机制生成这些 KV 值,因为它们用于计算未来令牌的注意力。在传统的,我们称之为聚合式 LLM 推理(aggregated LLM inference)中,你最终会遇到一种情况,即预填充(prefill),也就是 KV 的生成,以及使用 KV 来生成未来令牌,它们是共置(collocated)在同一组 GPU 上的。这有几个陷阱。第一个陷阱是,你会有抖动(thrash)。如果你不断地为每个阶段进行调度(scheduling),就很难说:“嘿,我有一个这种大小的预填充块,我需要生成一个依赖于我们已经计算出的许多其他令牌的令牌。”

简单来理解:PD分离试图解决不同场景(提示词长度、RAG上下文)造成的系统抖动,从而优化了计算过程的资源消耗,做到长、短问题按需消耗,而不是定额分配计算资源,虚拟机分配走向容器化调度。

纳特: 感觉如何?我如何将它们相互调度?这种调度,你放入 KV 缓存(KV cache)与使用它来生成令牌,它调度什么被处理,因为它基本上调度你是否生成 KV 令牌或使用其他 KV 令牌来生成其他输出令牌。

凯尔: 值得深入探讨 KV 缓存和预填充吗?

纳特: 是的,这是一个很好的观点。

凯尔: 所以,KV 缓存,正如我所说,它是用于注意力机制的值。当注意力机制发生时,你将它们与当前令牌的 Q(query,查询)结合起来,然后你使用该查询以及 K 和 V 值来计算注意力,这基本上是这个新令牌或我们正在生成的下一个令牌应该在多大程度上关注注意力机制中的每个值。

纳特: 我认为,这是我们在 Transformer 模型(Transformers)中看到的突破,即最初的论文《Attention Is All You Need》(注意力就是你所需要的一切),我的理解是,键值矩阵(key and value matrices)本质上会接收输入,然后,恕我直言,加权(weight)序列中的每个词与其他所有词进行比较,以理解它们如何组合在一起的推理。

凯尔: 是的,Q 和 K 之间的关系是你获得权重的方式(权重即经验范式,即智能)然后你使用 V 或值来实际,你根据该权重组合 V 以产生结果。

纳特: 所以这包括从提示(prompt)到生成的令牌的一切?

凯尔: 是的,它是初始提示,它是被视为输入一部分的任何内容,然后你之后生成的任何令牌都会被重新添加回缓存中,并成为你生成未来令牌时的上下文(context)的一部分,因为你不想有空白。你不想有输入,然后生成一堆,然后有你生成的内容的空白,因为你只会失去你的上下文。所以你有这个 KV 缓存。当你生成时,你正在为你基本上生成的令牌添加新的 K 和 V 值。然后,你需要保留该 KV 缓存,除非你正在使用窗口注意力机制(window attention),我们不会谈论它,你需要保留该 KV 缓存直到查询结束或你回答之后,因为如果你把它扔掉,你就必须重新计算它,因为它本身是注意力机制所必需的。

纳特: 所以很有趣,我以前没听说过,但你说传统推理你称之为聚合式推理(aggregated inference)。那么,如果 KV 缓存是用来生成未来令牌的,你如何将生成的令牌与 KV 缓存分开呢?

凯尔: 是的,这是一个很好的问题。本质上,你正在预填充机器上生成令牌,或者说生成令牌的 KV。预填充机器实际上做了一些非常聪明的事情,它开始逐层(layer-wise)地发送 K 和 V 值,因为它们是计算出来的。

纳特: 你说预填充机器是指,对于 Dynamo,我们有一个单独的 GPU,也就是任何 GPU。

凯尔: 所以在解耦时,当你将它分开时,处理创建 K 和 V 值的机器被称为预填充(prefill),而负责获取该 KV 并用它生成新令牌,并为这些令牌生成新的 KV,然后自回归地(auto-aggressively)生成更多令牌的机器被称为解码(decode)。

纳特: 好的。

凯尔: 你知道,现在的 LLM,很多 GPT 模型都被称为仅解码器模型(decode only models)。所以这是解码阶段。基本上,当你将它分开时,你基本上是根据输入、根据你的提示、根据你的查询等等,在 KV 的生成上将其分开。预填充生成 KV,然后它开始将 KV 发送到它将要使用的解码机器上。它这样做很聪明,因为它不会等到你完全完成所有操作,你可以实际发送,因为 LLM 是逐层生成 KV 的,你正在逐层逐层地进行(PD 分离的另一个优势:通过逐层输出优化计算成本)你更早地发送较早的层,你开始复制它们,这样传输它的成本就与计算它的成本重叠(overlapped)。

纳特: 我明白了。

凯尔: 这通常被称为计算通信重叠(compute communication overlapping)。这是 DeepSeek 实际上也用于训练的技术。如果你还没读过 DeepSeek V3 论文(DeepSeek V3 paper),去读一下。在发送到解码机器时,解码机器现在基本上拥有了过去的上下文(缓存,印象里看过一篇材料,说的是PD分离被广泛实践的核心要义是:以相对廉价的存储资源,换取有限的GPU计算资源)。用一个类比来说,你已经为你的作业做了研究,你已经为你的研究论文做了研究,现在是时候开始写作了。然后解码使用该 KV 开始生成令牌。

纳特: 那么,如果解码器可能没有完整的 KV 缓存,它怎么知道何时开始生成令牌呢?我不知道 KV 缓存是分层的,但这完全说得通。

凯尔: 是的,所以它实际上是阻塞的(blocking)。所以你进行异步发送,但你有一个阻塞的读取,基本上是“不要生成,直到你拥有 KV 缓存”。

纳特: 那是因为你总是知道 KV 缓存中有多少层,在它发送过来之前?

凯尔: 是的,所以层数基本上与模型中的层数相同。

纳特: 好的。那么解耦只有在你将其与 Dynamo 中的其他一些优化结合使用时才有意义,还是它本身就可以独立存在?

凯尔: 它可以产生巨大的好处。这可能,你知道,说仅仅使用一种软件技术,仅仅在相同数量的 GPU 上将其分开,仅仅将这两个东西分开,例如在 Llama 70B 模型上,就能使每个用户在相同 TPS 下每秒生成的总令牌数增加两倍以上,这真是太疯狂了。因为这很重要,你必须保持相同的延迟,但你能增加多少吞吐量?

纳特: 所以,假设在这种情况下,你将一些 GPU 分开,比如说解码发生在一半的 GPU 上。当解码器生成令牌时,一个新的 KV 缓存是否已经开始预填充,其中包含一些,所以它们基本上是,一旦预填充机器,引用一下,放弃了预填充,一旦它传输了它,它就基本上将其移除,它会说:“嘿,我需要 KV 空间。”KV 空间基本上是预填充机器执行工作的能力,就像“我是否有足够的 KV 空间来做这件事?”所以它,你知道,让它随风而去,让它被逐出(evicted),因为它不再用于生成或生成 KV。

凯尔: 是的。然后它可以接受新的请求,这些请求将使用该内存。

纳特: 这会波动吗?或者你必须指定你用于存储的 GPU 数量?

凯尔: 这是一个有趣的问题。所以,对于静态应用程序(static applications),假设你总是输入 3000 个令牌,输出 2000 个令牌,它理想情况下不会波动。如果你设置正确,你基本上会做一些修补工作,你总是发送相同数量,并且完美地计划。但生活并不总是这样。用户有时会发送数十万个令牌,然后我的每个 ChatGPT 线程都截然不同。

纳特: 是的,完全正确。

凯尔: 所以每次调用都大相径庭,而且总的来说,在许多不同的用户之间,它也随时间高度变化。一个很好的例子是,人们会在夜间提交批量工作(batch work),因为他们有更好的延迟保证,而且在某些情况下,对于某些服务提供商来说,它更便宜。所以突然间,夜幕降临,人们开始提交批量作业,它们可能与聊天机器人应用程序完全不同。所以,实时地,你需要多少预填充 GPU,以及你需要多少解码 GPU,这完全是非静态的(non-static)的,它时刻都在变化。这实际上是我们正在 Dynamo 中研究的一些东西,基本上允许解码机器要么成为填充机器,这也是我们正在研究的未来功能,以及允许编排器(orchestrator)说:“嘿,我可能需要更多的预填充机器,所以也许我应该将一些解码机器变异成它们,因为它们具有相同的权重。”你实际上可以告诉它:“嘿,你现在是一台预填充机器。”

纳特: 如果它变成了一台预填充机器,它是否需要去获取来自不同 GPU 的 KV 缓存?

凯尔: 不,我的意思是它会开始接收新的请求,所以它会用这些请求的 KV 填充,并将其发送出去。基本上,它是一个流水线(pipeline),预填充接收请求,生成 KV,推送 KV,然后接受新的请求。然后一旦解码接收到 KV,它就开始为该查询生成令牌。

纳特: 我看到 Dynamo 的一个很酷的地方是,你说你把 KV 缓存扔掉了,但这并不完全正确。

凯尔: 是的,这并不完全正确。对于预填充机器,取决于你如何配置它们,这可能是真的。但通常 KV 是有价值的,KV 可以在查询之间重用(reused)。一个很好的例子是多轮对话(multi-term chat)。你正在与 ChatGPT 聊天,你有一个在整个对话中基本保持不变的上下文(context),你添加了一些小部分,上下文也随之增长。如果你扔掉了对话的 KV 缓存,你每次继续与聊天机器人交谈时都必须重新计算它。所以你真的不想把它扔掉。同时,你希望确保与该 KV 对齐的未来查询,使用该 KV 的查询,无论是多轮对话,还是你正在使用具有相似语料库(corpus)的智能体(agent),你都非常非常非常希望将其保存在某个地方。

所以我们在 Dynamo 中有两件事对于做到这一点非常有用。一个是全局 KV 管理器(global KV manager),它允许将 KV 缓存卸载(offloading),当它被逐出时,卸载到不是 GPU 内存的介质上,在那里它们主动占用空间,而是卸载到主机内存(host memory)上,也就是机器上的 RAM(随机存取存储器),然后卸载到磁盘(disks)或 SSD(固态硬盘)、NVMe 硬盘(NVMe drives)上。基本上,你有一个缓存层级(hierarchy of cache),其中 HBM(高带宽内存)是 GPU 速度非常非常快的内存,它相当,引用一下,昂贵,因为它用于高价值。然后你有较低的层级,比如主机内存,然后将东西存储在冷存储(cold storage)的磁盘或 NVMe 或其他存储模式中非常便宜,只要你能在必要时将其取回系统。所以基本上,这种 KV 缓存卸载允许你将剩余的 KV 存储在冰箱里以备后用,然后当有人饿了,它就会取出来。(全局KV管理器是如何实现的?)

纳特: 将其存储在冷存储中比重新计算快多少?

凯尔: 这根据数量而变化很大。所以它有点取决于,有一个有趣的特性,即预填充,就目前而言,以当前形式的注意力机制,通常相对于生成的令牌数量是二次方(quadratic)的,因为你必须在序列中的所有令牌之间进行注意力计算。这意味着,随着你的输入序列长度变得非常非常长,你的扩展曲线是二次方的。如果存储它的成本不同,因为你存储的 KV 数量相对于令牌数量实际上是线性(linear)的,并且检索它的成本也是线性的。所以随着序列越来越长,将东西移动到存储中变得越来越有利。

纳特: 那么,序列长度达到多少时,这真的开始发挥作用?

凯尔: 这完全取决于模型,也完全取决于你运行它的 GPU。它可能低至 1000 到 2000 个令牌,但一旦你达到数万甚至数十万个令牌,你就会看到巨大的好处。不过还有一件事,即使从存储中检索它的成本,即使时间成本很高,你实际上也在阻止(preventing)或避免(avoiding)进行预填充工作的需要,因为你仍然在检索它,即使它慢一点。你最终会遇到一种情况,即你进行预填充所需的 GPU 数量实际上更少,因为该请求不再命中预填充节点了。

纳特: 有道理。那么,在哪些实际应用中,输入序列长度会很早变得非常长?

凯尔: 我的意思是,任何代码库(codebase)的代码库都非常庞大。代码有点像免费的。非常长的对话,你正在进行其他形式的检索(retrieval)的地方,比如像 Deep Research 或 Perplexity 这样的应用程序,你基本上是在获取大量检索到的东西,并将其用于给定的上下文,这会在开始时生成一个非常大的上下文,因为你基本上是从互联网或其他语料库中进行检索,这确实会在很早的时候生成大量的初始上下文。

纳特: 我不知道这是否有意义,但将一个代码库(repo)预填充(pre-fill),然后将其放入冷存储中,如果知道它会被大量访问,这是否有意义?

凯尔: 是的,这非常有用。对。例如,如果你有一堆智能体正在处理一个代码库,这是一个很好的例子,并且你将不断地检索它或检索代码库的块,你会希望它们在冷存储中。而且你希望它们易于访问,比如,如果你要启动一堆智能体,它们将在夜间处理一个新功能,你会希望:“好的,我将从冷存储中拉取它。”

纳特: 并且在同一方面,异步计算 KV 缓存是否有意义,而不是在查询时计算?因为你知道它会是,如果它是一个智能体工作流(agentic workflow),其中系统提示(system prompt)总是相同的。

凯尔: 哦,天哪,解耦(disaggregated),你应该把它放在那里。完全正确。另一个技术是,如果你知道某些东西是必要的,你应该从冷存储中预取(prefetch)它。这是 Dynamo 启用的另一件事,你可以在你的控制流中添加,如果你知道它会在这段时间内有用,或者在这段时间后需要,你应该立即逐出(evict),移动到更冷的存储,然后在你需要它之前移动到热存储。

纳特: 有趣。

凯尔: 这就是我们希望 Dynamo 在未来能够实现的功能之一。

纳特: 那现在已经实现了吗?还是即将推出?

凯尔: 对于某些框架,实际上我应该澄清一下,对于常见的 LLM 服务框架,我们目前支持三个:vLLM(vLLM)、SGlang 和我们自己的 Nvidia 推理服务框架,称为 TensorRT-LLM(TensorRT-LLM)。我们称这些为运行时(runtimes),它们基本上是你生成实际令牌的方式。TensorRT-LLM 支持我们添加的功能,即基本上告诉 KV 缓存的逐出策略(eviction policy),该策略用于从 KV 缓存中逐出东西,或者决定从 HBM 中逐出或移除什么。它让你能够提供额外的信息,比如“我将在 20 秒内需要这个”或“我希望你保留这个 10 秒”,如果它没用,它就会被移除。我们称之为基于优先级的 KV 逐出(priority based KV eviction)。所以现在立即就支持这个功能,但我们希望变得更高级,并开始感知这些东西,并自动检测何时可以进行这种形式的优化。

纳特: 这太有趣了。你将如何感知你想要什么?

凯尔: 是的,所以最简单的方法之一甚至不是感知,而是用户直接告诉我们。如果你正在运行一个常见的智能体框架,比如 LangChain(LangChain)或 LlamaIndex(LlamaIndex),或者基本上任何具有循环先验知识的框架,你都可以提供这些信息。你可以说:“嘿,这是我能做的所有事情的树状结构,这是它长期需要多长时间。”我没有过多考虑这个问题,所以我将冒险,也许会思考未来。从长远来看,收集聚合统计数据可能更有意义,比如注意到:“嘿,这个人经常运行这个智能体,它正在生成这个,或者它不断地检索这个上下文。”它也像这个大小的额外上下文,然后它会休眠一段时间来调用工具或做其他事情,然后过一会儿再回来。你通常可以追踪(trace)使用 KV 的工作流,因为 KV,实际上 Dynamo 的工作方式之一是它会追踪运行时中 KV 何时被逐出或何时被添加。正因为如此,你可以将这种追踪扩展到:“嘿,我在这里的这个时间戳做了一些低效的事情,我生成了 KV,然后我逐出了它,然后我又生成了它,然后我又逐出了它。”如果你有这种模式,这表明你可能可以做得更好。

纳特: 所以所有这些,我的意思是,超级酷,显然大大提高了效率,但对我来说最不合理,但也可以说是最酷的一点是,本质上是智能路由(intelligent routing)到拥有你将要使用的 KV 缓存的机器。它是如何知道哪台机器拥有与你的查询相关的 KV 缓存的?

凯尔: 是的,这是一个很好的问题,因为我们刚才谈到了,我也暗示了,Dynamo 在与运行时集成时会生成这些事件(events)。这些事件基本上会告诉你,在某个时间戳,KV 已被逐出,或者 KV 已被添加到特定的机器或特定的实例中,比如说,因为我们有多节点模型。它会说:“嘿,这个特定的实例刚刚添加了这个 KV,或者这个特定的实例刚刚添加或刚刚移除了这个 KV。”我们实际上有一个组件,我们称之为路由器(router),它会追踪这些事件。它接收来自数千个实例的数百万个 KV 事件,然后基本上会说:“嘿,这是一个 KV 树的时间偏移视图,这里存储着所有东西。”当你选择将请求放在哪个解码节点或哪个预填充节点,或两者的组合上时,你实际上可以查看那棵树,然后说:“嘿,我可以最小化我的工作,因为 70% 的工作已经在这个解码节点上完成了。”你只需要计算最后一点上下文。你还可以使用路由器来识别需要移动 KV 的地方,如果某个东西负载过重或过热,你可能需要稍微分支(branch)一下这棵树,然后说:“嘿,这个节点拥有这个 KV,这个 KV 现在也应该在这个其他节点上。”因为如果它在那里,我们就能卸载(offload)并进行负载均衡(load balancing)。所以路由器负责这一点,并且能够同时最大化它,你不需要做更少的预填充工作,你拥有更多的前缀匹配(prefix match),或者说在你已经生成的 KV 上有上下文匹配,或者,你知道,它也可能选择不路由到那里,因为那个节点负载过重,并且可能会将请求路由到可能需要重新生成的地方,或者你可能会通过其他形式的传输来移动它,但它可能需要重新生成。但一旦它重新生成,那个新实例就能够处理通常会发送到这个过载实例的请求。所以路由器同时进行基于 KV 的路由,即前缀最大化,以及负载最小化(load minimizing),即避免过载给定实例。

纳特: 有趣。路由发生在哪里?

凯尔: 它在,所以当你进行部署时,它们是运行路由器的物理机器,它位于那个数据中心(data center)中,因为你希望最小化信息往返时间(round trips)。如果你增加了额外的延迟,那就不好了。你让它在同一个地方的某个 CPU 机器(CPU machine)或其他机器上运行,甚至在 GPU 上运行,而实际的预填充和解码工作节点也在那里。

纳特: 超级有趣。所以你说了 Dynamo 有三件事。我们做了解耦推理(disaggregated inference),路由(routing),KV 缓存管理(KV cache management),也就是卸载到层级的能力。

凯尔: 哦,我明白了。

纳特: 就像冷存储一样。

凯尔: 完全正确。是的,所以我们有一套连贯的东西,它们真正试图在解耦时,抱歉,需要明确的是,所有基于 KV 的东西,比如卸载和路由,也对聚合场景有帮助。你不需要使用解耦就能从 Dynamo 中获益。

纳特: 哦,有趣。就像你只使用 KV 缓存的偏移(offsetting)或卸载(offloading)和路由一样。

凯尔: 是的,因为聚合工作节点仍然在计算 KV 缓存,它们仍然在生成 KV 令牌。

纳特: 有趣。但它们只是以一种与正在进行的解码工作形成对比的方式进行,它有点像争夺同一组资源。我有一个问题,因为当我想到谁是像 Nvidia Dynamo 这样的东西的主要消费者时,它是那些本质上正在服务这些模型的人,这些模型可能被最终客户使用,而最终客户可能会以各种不同的方式使用这些模型。所以当我想到 KV 缓存时,在我脑海中,我不知道 KV 缓存有多独特,每个用户都会有自己的 KV 缓存吗?还是实际上有数百万用户大致使用相同的 KV 缓存?

凯尔: 是的,这是一个很好的问题。我认为,如果你看基于检索的东西,你可能会看到很多重叠,但最明显的重叠是系统提示(system prompt)。如果你有相同系统提示的东西,每个人都会有一些百分之百必要的东西。但这有点容易,即使在用户之间,你可能也会有一些共同的信息被检索,或者在智能体用例中,你可能有一个公司的上下文。假设你有一堆智能体在你的业务中运行不同的功能,这些智能体可能都有一组共享的上下文,你希望首先放置这些上下文,因为这样你就有了一个前缀匹配(prefix match)。然后,一旦你,你知道,可能会有特定于该智能体的额外上下文,但你仍然在这个东西上有一个前缀匹配。你可以把它想象成一棵树,上下文在这里分支,但可能有一些仍然共享的子分支,然后你最终到达完全独特的叶子。

纳特: 它们是我的应用程序中不同用例的不同线程吗?每个用户都有一个独特的 KV 缓存?

凯尔: 是的,或者说 KV 缓存是某种程度上共享的,但分支成特定于每个线程的不同事物。这是我以前没有直观了解的一件事,即 KV 缓存的概念,因为显然,解码本质上也在向其添加内容,所以它几乎就像你可以选择一个小的,就像一个任务的 50%,每个人都可能从同一个起点开始,然后它分支开来。我以为 KV 缓存几乎就像,它只是存在,一个 KV 缓存对应一个任务,但你可以在此基础上构建并创建不同的变体,这太酷了。

纳特: 是的,是的,是的。对于代码应用程序,这是一个非常困难的切线,如果你正在构建一个代码应用程序,确保你的代码以一种不抖动缓存(doesn't thrash the cache)的顺序返回实际上非常重要。如果你的代码抖动缓存,如果你的代码以不同的顺序返回,包含在上下文中的顺序不同,那么你就无法缓存它,因为它是基于前缀的。它从头开始查找,然后从头开始选择尽可能多的内容,因为 KV 是位置相关(positional)的,并且注意力是方向性(directional)的。

纳特: 有趣。所以,你知道,我正在考虑,你提到了常用信息(commonly retrieved information)。我的意思是,我认为你可能有很多关于像 ChatGPT 这样的东西最常见的问题类型的数据,在这种情况下,即使在许多不同的用户之间,它也可能会被命中,因为人们只是以非常相似的方式使用该应用程序。

凯尔: 是的,我认为在最初,在我查看统计数据之前,我曾想:“为什么我们要把它存储在树中?这没有意义。”如果你是唯一的原因,你会把它存储在树中,因为你认为会有重叠,并且可能会有从这些重叠中分裂出来的东西。而且,你知道,事实证明,实际上有很多重叠,一些长度的小片段,而不是整个前缀,它们会分裂开来,无论是系统提示还是在两个不同用户、两个不同智能体、任何不同事物之间共享的上下文,甚至就像你说的,对于常规聊天查询,有一些数据集你可以查看,它们基本上是代表性的 KV 缓存命中,即使在用户之间,它们的上下文也有中等大小的缓存命中,这太疯狂了。

纳特: 作为像我这样的应用程序所有者,利用这些 KV 缓存优化有多难?

凯尔: 实际上一点也不难。所以,在 Dynamo 中,我们将其集成到 TensorRT-LLM 中,作为 vLLM 的运行时。我们正在努力将这些事件上游(Upstream)到 vLLM 中,以便我们可以与它们集成,但我们有一个补丁(patch),你可以直接应用于 vLLM,希望它在不久的将来不会有任何冲突,它实际上被放置在 vLLM 中不常触及的地方。而且,你知道,从这个意义上说,如果你已经在运行推理,并且你正在使用 vLLM 或 TensorRT-LLM 来服务你的模型,你可以转移到基于 Dynamo 的工作节点(worker),它会封装该运行时,然后只需添加路由器(router),你就会立即获得好处。

纳特: 我认为,智能体式工作流(agentic style workflows)和智能体概念最近变得非常流行,我认为很多人还没有真正理解的是,这意味着地球上需要生成多少更多的令牌。因为我们不再为人类生成令牌,我们正在为计算机生成令牌。

凯尔: 是的,这完全与人类无关。输出不是令牌,而是一个任务(task)。就像:“好的,这个具身智能体(embodied agent)是否正确地喂了我的狗?”他们不关心它是否在思考:“我应该把碗放在这里还是放在那里?”它只是结果。对。随着你越来越多地看到智能体带来的自动化,将会有更多的令牌被生成。

纳特: 这没有上限。

凯尔: 不,真的没有。正如我所说,当你解锁更多应用程序时,突然间,如果你做了一些对人类非常有价值的事情,那么你愿意让它永远推理的成本基本上可以忽略不计。就像,如果你有一个能够设计出一种新的、更高效的汽车车身设计的东西,让我们用一个非常偏远的例子,那么它就像,你知道,燃油效率提高了 50%。突然间,你会觉得:“哇,我应该让这个东西永远运行。”你知道,如果你有能够思考的机器,而且思考产生的效果越好,它思考的时间就越长,最终可能会有一个终态,尽管我们可能还没有完全达到,对于那些非常重要的、能产生大量价值的任务,你可以并行地放置大量机器,因为你可以扩展机器。人类实际上更难扩展,特别是对于那些非常有才华或在各自领域非常深入的人。你可以想象这样一种情况,博士研究员(PHD researcher)与这些智能体合作,这些智能体为他们运行实验。研究员成为设计假设(hypothesis)的人,然后智能体执行该假设。或者,如果你有一些非常非常非常聪明的智能体,也许它设计了自己的假设,并与人类合作来完善它。

纳特: 这就是我的意思,我只是觉得我们正处在一个非常酷的时代,你正在有效地向前迈进,你刚才也提到了,但就像降低智能成本(driving the cost of intelligence down)。当智能成本下降时,我真的很喜欢我刚才视觉上想象的三维空间,有很多点,如果我们能把一个或多个维度推得更远,我们就能做更多的事情。

凯尔: 是的,完全正确。所以,我也是这样想的。应用程序基本上就像空间中的点,这里有一个,这里有一个,这里有一个。如果我们提高智能水平,那太棒了,它会移动到这里。但也有其他维度,比如:“嘿,我们移动了,你知道,成本变得更好了,因此我们把它推出去。”最疯狂的是,我喜欢 Dynamo 的一点是,我们已经提供并且也致力于未来继续研究的,基本上是这些技术,比如解耦(disaggregation)和其他技术,比如我提到的推测(speculation),它们允许你调整领域(tweak the domain)。就像推理一样,你可能只是生成大量的令牌,但你也可以做一些像最终共识(consensus at end)的事情,你生成一堆不同的字符串,然后选择最常见的那个。所以在这种情况下,如果你正在降低成本,你也可以利用你节省的成本来使模型更智能。所以,用这些技术和我们可用的工具箱来降低令牌成本,基本上也等同于在相同成本下使其更智能,或者在相同成本下使其更快,这太疯狂了。就像,如果我们能不断降低令牌成本,那么就会有越来越多的事情通过 AI 应用程序来完成。

纳特: 解锁。

凯尔: 是的,它只会解锁越来越多。

纳特: 我有一个关于延迟的问题。有首令牌时间(Time to First Token),然后是令牌间延迟(Inter-token latency),对吗?以及加载,本质上是加载已经预计算的 KV 缓存。

凯尔: 是的。

纳特: 似乎对首令牌时间有益。

凯尔: 是的,正如我所说,它有点取决于长度,因为它有点像两个图表,一个是线性的,另一个是二次方的,二次方线和线性线在某个点相交。

纳特: 我明白了。

凯尔: 所以有一个临界点(tipping point),在这个点上,如果检索是 KV 的一个选项,那么总是进行检索就完全有意义了。

纳特: 我明白了。Dynamo 中有多少优化会影响首令牌时间与令牌间延迟?

凯尔: 是的,正如我所说,解耦是一种技术,它将预填充和解码解耦,预填充和解码非常有趣,因为它们实际上将首令牌时间分开了,一台机器主要负责令牌间延迟,一台机器负责首令牌时间,这实际上是解耦的最大好处之一,我们还没有谈到,那就是上下文或预填充和解码需要不同的配置。所以,对于某些模型,你知道,上下文,我不能笼统地说所有模型,因为每个模型都不同,它们可能使用不同形式的注意力机制,它们可能使用专家混合(mixer of experts)或其他东西。但让我们谈谈 Jensen 在主题演讲中谈到的 DeepSeek R1。Jensen 在图表上指出了曲线中的不同点以及它们如何不同。关键在于,聚合线看起来像这样,而解耦线看起来像那样的一个原因,是当你推高更高的延迟,或者说更低的延迟时,当你变得越来越好时,因为 TPS 是 X 轴,所以更高的 TPS 意味着更低的延迟。当你推高用户 TPS 时,解码实际上,特别是在 DeepSeek 上,需要更多的 GPU 用于解码阶段。它希望进行大量的专家并行(expert parallelism),它希望有大量的 GPU 并行执行任务,因为这意味着你可以进行非常高的批处理大小(batch size),但仍然非常非常快速地计算新令牌。这就是为什么在 DeepSeek 曲线中,聚合有一个悬崖,然后解耦保持很高很长时间,这只是因为你仍然可以继续使用这些非常非常非常大规模并行的解码实例配置。但聚合的问题是它还必须处理上下文,而上下文的扩展方式不同。DeepSeek R1 的上下文或预填充阶段喜欢使用较少数量的 GPU。正因为如此,这意味着在聚合意义上,你得到的是,这样说很奇怪,但就像两害相权取其轻(worst of Both Worlds),你的上下文和你的解码都没有得到它们想要的配置,因为你必须找到一个折衷方案。

纳特: 这就是它对 R1 表现如此出色的原因吗?

凯尔: 是的,是的,R1 专门有 DeepSeek 自己设计到模型中的许多东西,使其非常适合解耦。

纳特: 解耦的性能提升是多少?

凯尔: 解耦的性能提升是,在每个用户每秒 300 个令牌左右,你可以生成多 30 倍的令牌。

纳特: 这是每个 GPU 标准化的吗?

凯尔: 是的,是的,这很疯狂。你拥有这些,抱歉,需要非常明确的是,这些是预测数字(projected numbers),那是你可以用软件做到的上限,这绝对令人惊叹。但是的,我的意思是,解耦确实允许你拥有这两种不同的配置,它还允许你在上下文和解码或预填充和解码工作节点之间进行不同的负载匹配(load match)。这基本上意味着:“嘿,我有很多非常长的 ISL,我将需要更多的预填充工作节点。”我可以单独扩展它们,而无需扩展解码。但如果你处于聚合场景中,如果你获得更多的预填充负载,你只需要扩展更多的工作节点,你无法精细控制预填充阶段和解码阶段之间的负载匹配。

纳特: 这太疯狂了。Nvidia Dynamo,我的意思是,我想从宏观角度来看,推理有如此多的不同杠杆,有不同类型的,我的意思是,这很有道理,因为有无限数量的应用程序空间可以构建,有利用上下文的方式,有你想要放入上下文的内容,有你使用特定模型进行持续任务的程度,而有了智能体,这个空间几乎会进一步扩散。鉴于所有这些不同的杠杆,我还没有听说过任何像 Nvidia Dynamo 这样的东西能够真正优化。我只是想说,最酷的部分是他们把它开源(open source)了。

凯尔: 哦,是的,它是开源的。我们甚至没有提到这一点。是的,它都是开源的。我的意思是,我们还有一些没有提到的东西,我们可以列举出来,如果我们觉得有趣,我们可以深入探讨。我们还在实现和发布一个用于 KV 传输的通信库(communication library),名为 NIXL(NIXL)。

纳特: 是的,它正在开源。NIXL 怎么拼?

凯尔: N-I-X-L。

纳特: 好的,是的。

凯尔: 所以 NIXL 正在发布。我们,你知道,它都是开源的,路由器是开源的,路线图(road map)一目了然。我们希望尽可能多地赋能更多的人,我们希望赋能更多的运行时(runtimes),我们希望赋能人们想要用推理做的任何事情。我们希望赋能人们想要做推理的任何方式,如果他们想做多模态模型(multimodal models),那是我们现在正在积极研究和积极思考的事情。如果你想做其他可能更喜欢解耦的形式,我们也想研究这些。

纳特: 我该如何开始使用它?它是开源的,我可以去仓库吗?

凯尔: 是的,所以你现在就可以去仓库。我们有一系列示例,将向你展示如何进行解耦。你知道,我们希望它尽可能开放和易于访问,所以我们实际上在 Dynamo 中有后端(backends),可以在你的笔记本电脑上运行。所以,如果你有一台 Mac 笔记本电脑,你就可以运行 Dynamo,即使你不能进行解耦,因为它是一个芯片。你仍然可以进行实验和运行,然后你可以非常轻松地将其从你的系统转移到更大的系统上。

纳特: 所以我只需要至少两个 GPU,就像在一个节点上一样?

凯尔: 是的,要开始进行解耦,你可以从两个 GPU 开始。但正如我所说,解耦的两个最大影响之一,所以总共有三个好处,让我们再回顾一下。一是调度(scheduling)变得更好,因为你只需要调度预填充和解码,你就不需要考虑:“嘿,我应该处理这个解码以完成它,还是应该处理这个新进来的预填充?”所以这是一件事。第二件事是,你可以进行不同的配置(configs)。所以如果你只有两个 GPU,你可能甚至看不到解耦的好处,因为你可能没有运行会产生这种好处的配置。我所说的配置是指,你使用多少 GPU 以及你使用这些 GPU 的并行策略(parallel strategy)来在解码或预填充阶段产生输出。然后第三件事是,对于预填充和解码工作节点,你可以进行负载匹配(load match)。所以如果你有两个 GPU,一个预填充和一个解码,你的任意负载匹配是 1:1。所以 GPU 越多越好。用 Jensen 的话来说,你知道,你买得越多,节省得越多。它会变得越来越好,你会有更好的负载,你更好地适应问题。

纳特: 但我的意思是,我认为这就是它的用途,对吧?这更像是一个规模问题(scale problem),就像规模法则一样,当你谈论数十亿用户的数十亿令牌时,30 倍的提升才真正重要。

凯尔: 是的,完全正确。如果你谈论的是工业应用(industrial applications)或企业应用(Enterprise applications),或服务大量用户的应用程序,你希望你的令牌尽可能高效,因为你可以,正如我们所说,不断回到这个成本,基本上意味着你可以启用更多的应用程序。如果你降低成本,你基本上只是让更多的应用程序能够使用 AI,更多的应用程序能够使用这些 LLM 的力量。而且你真的应该考虑开始使用其中一些技术,如果成本对你来说很敏感的话。现在,如果你是一名研究人员(researcher),你真的非常非常,你正在使用八个 GPU,并且你希望它对一个(stream)来说尽可能快,那么聚合可能仍然有意义,但你仍然可以使用 Dynamo 中的 KV 路由和其他技术。

纳特: 它们内置在运行时中。

凯尔: 是的,内置在运行时中。

纳特: 太棒了。所以 Dynamo 已经发布了,它是开源的。我们可以在 GitHub 上查看它,或者从 GitHub 上查看。我们是否托管它?我们有任何示例吗?对于想要尝试的人来说,最简单的方法是什么?

凯尔: 我们在 GitHub 仓库上有很多示例。你可以在你自己的机器上拉取这些示例。Nvidia 有 Brev 盒子(Brev boxes),你可以在上面运行工作负载,你可以将这些示例拉取到 Brev 盒子中并开始使用它们。还有一些教程(tutorials)。所以如果你现在就在 GTC 现场,我实际上是在我们总部发布后坐在这里说的,但如果你今天晚些时候在 GTC 现场,你可以去我们的教程环节或我们的专家问答(ask with experts)环节,它在当天晚些时候举行。我实际上不知道具体时间,我们希望能有一个横幅显示。你可以来和我们聊天,我会在那里,如果你看到我或,我们会在那里。是的,如果你在 GTC 现场看到我们,请过来和我们聊聊。我认为,你知道,我很高兴回答问题。但如果你想开始,并且你在线上,而且你没有 GTC 门票,你不会去那里,别担心,我们有一系列关于如何开始使用 Dynamo 的视频演示(video demos),以及将引导你确切了解它如何工作,我们提供的杠杆是什么,并且,你知道,希望为你提供一个良好的开箱体验。

解耦的3个好处:

  1. 调度优化:调度预填充和解码变得更简单,无需权衡处理新预填充还是现有解码。
  2. 配置灵活性:可以根据GPU数量和并行策略进行不同的配置,以优化性能。
  3. 负载匹配:能够更好地匹配预填充和解码工作节点的负载,提高资源利用率。

纳特: 太棒了。伙计,你和你的团队在将这一切整合在一起方面做得非常出色。我认为,你知道,这非常令人兴奋,你知道,2025 年是智能体之年,是所有这些不断涌现的新用例之年,是令牌之年。我的意思是,我只是觉得能成为这家公司的一员,看到我们如何不断突破极限,显然是在芯片本身以及它们的速度和内存方面,然后一直到推理,并突破推理的极限,这太酷了。而且,你知道,你正在有效地,你知道,推动智能(driving intelligence),再次,因为,我这样说并非夸大其词,它正在使智能对每个人都更易于访问。

凯尔: 这就是它。更易于访问,你知道,总体上更便宜。可访问性和廉价性是相关的,但对于大规模应用程序来说,它只是更便宜、更高效。正如你所说,它正在降低智能成本(pushing down the cost of intelligence),这基本上允许你用我们谈到的这些杠杆做很多不同的事情。所以这对我来说非常令人兴奋,我只是看到了降低生成成本(make gener cheaper)的巨大前景。这意味着生成更多的代码,加速更多的科学发现,更多的酷应用程序,或者,你知道,我希望最终能看到一个由 LLM 驱动的视频游戏。你知道,我们在这个前沿上推得越多,我们就越接近这些类型的应用程序,你知道,疯狂的、野生的涌现行为(emergent behavior)和新的应用程序,如果没有廉价的令牌,你永远不会看到。

纳特: 绝对。好的,太棒了。谢谢。

凯尔: 是的,谢谢。查看 Nvidia Dynamo。

纳特: 是的,我们会在这里放上链接。