<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; comfyUI</title>
	<atom:link href="https://www.phppan.com/tag/comfyui/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 25 Apr 2026 00:56:17 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>ComfyUI 的缓存架构和实现</title>
		<link>https://www.phppan.com/2025/11/comfyui-cache/</link>
		<comments>https://www.phppan.com/2025/11/comfyui-cache/#comments</comments>
		<pubDate>Sat, 29 Nov 2025 01:27:49 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AI架构]]></category>
		<category><![CDATA[comfyUI]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2439</guid>
		<description><![CDATA[围绕 ComfyUI，大家讨论最多的是节点、工作流、算力这些，真正去看缓存细节的人其实不多。但只要你开始在一台 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">围绕 ComfyUI，大家讨论最多的是节点、工作流、算力这些，真正去看缓存细节的人其实不多。但只要你开始在一台机器上堆多个模型、多个 LoRA、多个 workflow，缓存策略就会直接决定这几件事：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">你是算力浪费，还是把显存 / 内存用在刀刃上；</section>
</li>
<li>
<section style="color: #010101;">容器会不会莫名其妙 OOM；</section>
</li>
<li>
<section style="color: #010101;">工作流切换时，是“秒级热身”还是“从头再来”。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这篇文章只做一件事：把 ComfyUI 当前的缓存架构和实现讲清楚，重点是三类策略（CLASSIC / LRU / RAM_PRESSURE）在「键怎么算、什么时候命中、什么时候过期」上的差异，以及在多模型、多 LoRA、多 workflow 场景下应该怎么选择。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">1. 整体架构：两路缓存 + 四种策略</span></h1>
<p data-tool="mdnice编辑器">先把整体结构捋清楚。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">1.1 两类缓存：outputs 和 objects</span></h2>
<p data-tool="mdnice编辑器">在执行一个工作流的时候，ComfyUI 维护了两类缓存：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">outputs</code>：<br />
存中间结果和 UI 输出。命中时可以直接跳过节点执行，这部分是真正省计算的地方。</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">objects</code>：<br />
存节点对象实例（类实例），避免每次重新构造节点对象。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">这两类缓存由一个统一的集合类 <code style="color: #0e8aeb;">CacheSet</code> 来管理。核心结构在 <code style="color: #0e8aeb;">execution.py</code> 中：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">CacheType</span><span class="hljs-params">(Enum)</span>:</span>
    CLASSIC = <span class="hljs-number" style="color: #d19a66;">0</span>
    LRU = <span class="hljs-number" style="color: #d19a66;">1</span>
    NONE = <span class="hljs-number" style="color: #d19a66;">2</span>
    RAM_PRESSURE = <span class="hljs-number" style="color: #d19a66;">3</span>

<span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">CacheSet</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">__init__</span><span class="hljs-params">(self, cache_type=None, cache_args={})</span>:</span>
        <span class="hljs-keyword" style="color: #c678dd;">if</span> cache_type == CacheType.NONE:
            self.init_null_cache()
        <span class="hljs-keyword" style="color: #c678dd;">elif</span> cache_type == CacheType.RAM_PRESSURE:
            cache_ram = cache_args.get(<span class="hljs-string" style="color: #98c379;">"ram"</span>, <span class="hljs-number" style="color: #d19a66;">16.0</span>)
            self.init_ram_cache(cache_ram)
        <span class="hljs-keyword" style="color: #c678dd;">elif</span> cache_type == CacheType.LRU:
            cache_size = cache_args.get(<span class="hljs-string" style="color: #98c379;">"lru"</span>, <span class="hljs-number" style="color: #d19a66;">0</span>)
            self.init_lru_cache(cache_size)
        <span class="hljs-keyword" style="color: #c678dd;">else</span>:
            self.init_classic_cache()
        self.all = [self.outputs, self.objects]
</code></pre>
<p data-tool="mdnice编辑器">不管是哪种策略，结构都是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">outputs</code>：<strong style="color: #0e88eb;">按输入签名做 key</strong>；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">objects</code>：<strong style="color: #0e88eb;">按 (node_id, class_type) 做 key</strong>。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">1.2 四种策略：CLASSIC / LRU / RAM_PRESSURE / NONE</span></h2>
<p data-tool="mdnice编辑器">从启动参数到缓存策略的选择，大致是这样的优先级（在 <code style="color: #0e8aeb;">main.py</code> 中）：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">指定 <code style="color: #0e8aeb;">--cache-lru</code> → 用 LRU；</section>
</li>
<li>
<section style="color: #010101;">否则指定 <code style="color: #0e8aeb;">--cache-ram</code> → 用 RAM_PRESSURE；</section>
</li>
<li>
<section style="color: #010101;">否则指定 <code style="color: #0e8aeb;">--cache-none</code> → 完全关闭缓存；</section>
</li>
<li>
<section style="color: #010101;">都没指定 → 默认 CLASSIC。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">CacheType</code> 的定义前面已经贴了。不同策略只决定 <strong style="color: #0e88eb;">outputs 的实现方式</strong>，而 objects 基本始终使用层级缓存 <code style="color: #0e8aeb;">HierarchicalCache(CacheKeySetID)</code>，后面细讲。</p>
<hr data-tool="mdnice编辑器" />
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2. 缓存键：输入签名 + 变化指纹</span></h1>
<p data-tool="mdnice编辑器">缓存是否命中，首先取决于“key 算得是否合理”。ComfyUI 的设计核心是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">输出缓存（outputs）</strong>：用“输入签名 + is_changed 指纹（+ 某些情况下的 node_id）”作为 key；</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">对象缓存（objects）</strong>：用 <code style="color: #0e8aeb;">(node_id, class_type)</code> 作为 key。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.1 输出缓存：输入签名是怎么来的</span></h2>
<p data-tool="mdnice编辑器">输出键的生成由 <code style="color: #0e8aeb;">CacheKeySetInputSignature</code> 负责，核心逻辑在 <code style="color: #0e8aeb;">comfy_execution/caching.py:100-126</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">get_node_signature</span><span class="hljs-params">(self, dynprompt, node_id)</span>:</span>
    signature = []
    ancestors, order_mapping = self.get_ordered_ancestry(dynprompt, node_id)
    signature.append(<span class="hljs-keyword" style="color: #c678dd;">await</span> self.get_immediate_node_signature(dynprompt, node_id,
    order_mapping))
    <span class="hljs-keyword" style="color: #c678dd;">for</span> ancestor_id <span class="hljs-keyword" style="color: #c678dd;">in</span> ancestors:
        signature.append(<span class="hljs-keyword" style="color: #c678dd;">await</span> self.get_immediate_node_signature(dynprompt,
        ancestor_id, order_mapping))
    <span class="hljs-keyword" style="color: #c678dd;">return</span> to_hashable(signature)
</code></pre>
<p data-tool="mdnice编辑器">这里有几个点：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">不只看当前节点</strong>：<br />
它会按拓扑顺序把“当前节点 + 所有祖先”的签名拼在一起。这保证了只要整个子图的拓扑和输入一致，输出就能命中缓存。</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">immediate 签名包含什么</strong>：<br />
<code style="color: #0e8aeb;">get_immediate_node_signature</code> 会把这些信息打包进去（位置见同文件 108–126）：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">class_type</code>：节点类型；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">is_changed</code> 指纹（通过 <code style="color: #0e8aeb;">fingerprint_inputs/IS_CHANGED</code>）；</section>
</li>
<li>
<section style="color: #010101;">必要时的 <code style="color: #0e8aeb;">node_id</code>；</section>
</li>
<li>
<section style="color: #010101;">有序的输入值/链接。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">什么时候把 node_id 也算进 key</strong>：<br />
当节点被声明为非幂等，或者内部有 <code style="color: #0e8aeb;">UNIQUE_ID</code> 这种隐含输入时，会把 <code style="color: #0e8aeb;">node_id</code> 加进签名（见 <code style="color: #0e8aeb;">comfy_execution/caching.py:18-23</code>, <code style="color: #0e8aeb;">116</code>），避免“看起来一样”的节点被错误复用。</p>
</section>
</li>
</ol>
<p data-tool="mdnice编辑器">最后通过 <code style="color: #0e8aeb;">to_hashable</code> 转成可哈希结构（<code style="color: #0e8aeb;">tuple/frozenset</code> 等），作为最终键值。</p>
<p data-tool="mdnice编辑器">结果是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">输入完全相同 → key 相同 → 直接命中；</section>
</li>
<li>
<section style="color: #010101;">任意一个上游节点输入或参数变化 → 指纹变了 → key 不同 → 不会复用旧结果。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.2 对象缓存：用 (node_id, class_type)</span></h2>
<p data-tool="mdnice编辑器">节点对象的键由 <code style="color: #0e8aeb;">CacheKeySetID</code> 构造（<code style="color: #0e8aeb;">comfy_execution/caching.py:66-80</code>），逻辑简单：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">key = <code style="color: #0e8aeb;">(node_id, class_type)</code>。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">读取时：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">obj = caches.objects.get(unique_id)
<span class="hljs-keyword" style="color: #c678dd;">if</span> obj <span class="hljs-keyword" style="color: #c678dd;">is</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>:
    obj = class_def()
    caches.objects.set(unique_id, obj)
</code></pre>
<p data-tool="mdnice编辑器">对象缓存存在的目的只有一个：同一个 workflow 执行过程中，不要重复 new 节点实例。</p>
<hr data-tool="mdnice编辑器" />
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3. 缓存容器：Basic / Hierarchical / LRU / RAM / Null</span></h1>
<p data-tool="mdnice编辑器">有了 key，还需要一个合理的「容器」和「驱逐策略」。</p>
<p data-tool="mdnice编辑器">主要类在 <code style="color: #0e8aeb;">comfy_execution/caching.py</code>：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">BasicCache</code>：基础容器，提供 <code style="color: #0e8aeb;">set_prompt / clean_unused / get / set</code> 等；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">HierarchicalCache</code>：按“父节点 → 子图”构建层级缓存；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">LRUCache</code>：在 Basic + Hierarchical 的基础上增加代际 LRU；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">RAMPressureCache</code>：在 LRU 的基础上增加 RAM 压力驱逐；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">NullCache</code>：空实现（禁用缓存）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">核心接口统一在 <code style="color: #0e8aeb;">BasicCache</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">clean_unused</span><span class="hljs-params">(self)</span>:</span>
    self._clean_cache()
    self._clean_subcaches()
</code></pre>
<p data-tool="mdnice编辑器">层级相关逻辑在 <code style="color: #0e8aeb;">HierarchicalCache</code>，用来支持“子图单独分区缓存”。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.1 层级缓存：怎么定位到某个节点的分区</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">HierarchicalCache</code> 通过 parent 链定位子缓存，代码在 <code style="color: #0e8aeb;">comfy_execution/caching.py:242-269</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">_get_cache_for</span><span class="hljs-params">(self, node_id)</span>:</span>
    parent_id = self.dynprompt.get_parent_node_id(node_id)
    ...
    <span class="hljs-keyword" style="color: #c678dd;">for</span> parent_id <span class="hljs-keyword" style="color: #c678dd;">in</span> reversed(hierarchy):
        cache = cache._get_subcache(parent_id)
        <span class="hljs-keyword" style="color: #c678dd;">if</span> cache <span class="hljs-keyword" style="color: #c678dd;">is</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>: <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>
    <span class="hljs-keyword" style="color: #c678dd;">return</span> cache

<span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">get</span><span class="hljs-params">(self, node_id)</span>:</span>
    cache = self._get_cache_for(node_id)
    <span class="hljs-keyword" style="color: #c678dd;">if</span> cache <span class="hljs-keyword" style="color: #c678dd;">is</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>: <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>
    <span class="hljs-keyword" style="color: #c678dd;">return</span> cache._get_immediate(node_id)

<span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">set</span><span class="hljs-params">(self, node_id, value)</span>:</span>
    cache = self._get_cache_for(node_id)
    <span class="hljs-keyword" style="color: #c678dd;">assert</span> cache <span class="hljs-keyword" style="color: #c678dd;">is</span> <span class="hljs-keyword" style="color: #c678dd;">not</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>
    cache._set_immediate(node_id, value)
</code></pre>
<p data-tool="mdnice编辑器">含义很直接：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">根节点存在当前 cache；</section>
</li>
<li>
<section style="color: #010101;">某些节点生成子图，会在该节点下挂一个子 cache；</section>
</li>
<li>
<section style="color: #010101;">读写时，先通过 parent 链找到对应的子 cache 再读写。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">clean_unused()</code> 里除了清除不用的 key，还会删除没用到的子 cache 分区（<code style="color: #0e8aeb;">_clean_subcaches()</code>）。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4. 执行循环中的使用路径</span></h1>
<p data-tool="mdnice编辑器">缓存不是“挂在那就完事了”，它在执行循环中有比较明确的调用点。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.1 设置 prompt + 清理</span></h2>
<p data-tool="mdnice编辑器">启动一次执行时（<code style="color: #0e8aeb;">execution.py:681-685</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">is_changed_cache = IsChangedCache(prompt_id, dynamic_prompt, self.caches.outputs)
<span class="hljs-keyword" style="color: #c678dd;">for</span> cache <span class="hljs-keyword" style="color: #c678dd;">in</span> self.caches.all:
    <span class="hljs-keyword" style="color: #c678dd;">await</span> cache.set_prompt(dynamic_prompt, prompt.keys(), is_changed_cache)
    cache.clean_unused()
</code></pre>
<p data-tool="mdnice编辑器">这里做了三件事：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">给当前 prompt 生成一个 <code style="color: #0e8aeb;">IsChangedCache</code>，为 key 计算提供 is_changed 结果；</section>
</li>
<li>
<section style="color: #010101;">对 outputs / objects 各自执行一次 <code style="color: #0e8aeb;">set_prompt</code>（不同策略实现不同）；</section>
</li>
<li>
<section style="color: #010101;">紧接着执行 <code style="color: #0e8aeb;">clean_unused()</code>，做一次基于“当前 prompt 键集合”的清理。</section>
</li>
</ol>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.2 节点执行前后：cache 命中与写入</span></h2>
<p data-tool="mdnice编辑器">在节点执行路径中：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">执行前：优先尝试从 <code style="color: #0e8aeb;">outputs</code> 命中（<code style="color: #0e8aeb;">execution.py:686-701</code>）；</section>
</li>
<li>
<section style="color: #010101;">执行后：将 <code style="color: #0e8aeb;">(ui, outputs)</code> 作为 <code style="color: #0e8aeb;">CacheEntry</code> 写入 <code style="color: #0e8aeb;">outputs</code>（<code style="color: #0e8aeb;">execution.py:568-571</code>）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">为了简化，这里只看抽象行为：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">命中 → 跳过计算，直接拿值；</section>
</li>
<li>
<section style="color: #010101;">未命中 → 正常跑一遍，将结果塞回缓存。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4.3 每个节点之后的 RAM 轮询</span></h2>
<p data-tool="mdnice编辑器">如果是 RAM_PRESSURE 模式，执行完每个节点都会触发一次内存检查（<code style="color: #0e8aeb;">execution.py:720</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">self.caches.outputs.poll(ram_headroom=self.cache_args[<span class="hljs-string" style="color: #98c379;">"ram"</span>])
</code></pre>
<p data-tool="mdnice编辑器">只有 RAMPressureCache 实现了 <code style="color: #0e8aeb;">poll</code>，其他模式下这个调用等同空操作。</p>
<hr data-tool="mdnice编辑器" />
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5. CLASSIC：默认层级缓存</span></h1>
<p data-tool="mdnice编辑器">先看默认策略：CLASSIC。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.1 初始化与结构</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">CacheSet.init_classic_cache</code>（<code style="color: #0e8aeb;">execution.py:97-126</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">CacheType</span><span class="hljs-params">(Enum)</span>:</span>
    CLASSIC = <span class="hljs-number" style="color: #d19a66;">0</span>
    LRU = <span class="hljs-number" style="color: #d19a66;">1</span>
    NONE = <span class="hljs-number" style="color: #d19a66;">2</span>
    RAM_PRESSURE = <span class="hljs-number" style="color: #d19a66;">3</span>

<span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">CacheSet</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">init_classic_cache</span><span class="hljs-params">(self)</span>:</span>
        self.outputs = HierarchicalCache(CacheKeySetInputSignature)
        self.objects = HierarchicalCache(CacheKeySetID)
</code></pre>
<p data-tool="mdnice编辑器">可以看到：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">outputs</code> 用层级缓存 + 输入签名做 key；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">objects</code> 也用层级缓存 + (node_id, class_type) 做 key；</section>
</li>
<li>
<section style="color: #010101;">不涉及 LRU 或 RAM 驱逐。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.2 CLASSIC 的过期机制：完全由「当前 prompt」驱动</span></h2>
<p data-tool="mdnice编辑器">CLASSIC 模式不做容量和时间管理，它只有两种“失效”方式：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">提示切换 / 执行前清理</strong></section>
</li>
</ol>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">clean_unused()</code> 的核心逻辑（<code style="color: #0e8aeb;">comfy_execution/caching.py:172-195</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">clean_unused</span><span class="hljs-params">(self)</span>:</span>
    self._clean_cache()
    self._clean_subcaches()
</code></pre>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">_clean_cache()</code>：把不在“当前 prompt 键集合”的项删掉；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">_clean_subcaches()</code>：把不再需要的子缓存分区删掉。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">在 <code style="color: #0e8aeb;">execution.py:681-685</code> 每次绑定新 prompt 时，都会执行这一步。结果是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">换了一个新的 workflow / prompt，旧 workflow 的 outputs / objects 都会被视为“未使用”，被清理掉；</section>
</li>
<li>
<section style="color: #010101;">CLASSIC 不会跨不同 prompt 保留旧 workflow 的缓存。</section>
</li>
</ul>
<ol start="2" data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">键不命中（指纹失效）</strong></section>
</li>
</ol>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">is_changed</code> 的计算在 <code style="color: #0e8aeb;">execution.py:48-89</code>，当节点输入更新时，指纹会变化；<code style="color: #0e8aeb;">CacheKeySetInputSignature</code> 在构造键时会把这个指纹带进去（<code style="color: #0e8aeb;">comfy_execution/caching.py:115-127</code>）。因此只要：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">参数 / 输入 / 上游节点的任一变化 → key 改变 → 旧值自然不命中。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.3 CLASSIC 明确不会做的事</span></h2>
<p data-tool="mdnice编辑器">在 CLASSIC 下：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">不做 LRU 容量控制：没有 max_size，也没有代际淘汰逻辑；</section>
</li>
<li>
<section style="color: #010101;">不做 RAM 压力驱逐：<code style="color: #0e8aeb;">poll()</code> 是空的，执行循环里即使调用了也什么都不干；</section>
</li>
<li>
<section style="color: #010101;">不做 TTL：不看时间，只看 prompt 键集合。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">对应的注释已经在参考内容中点得很清楚，这里就不重复堆代码了。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">5.4 在频繁切换 workflow / 模型时的表现</span></h2>
<p data-tool="mdnice编辑器">结合上面的机制，总结一下 CLASSIC 在多 workflow 场景下的行为：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">执行新工作流时：<br />
<code style="color: #0e8aeb;">set_prompt + clean_unused</code> 会直接把“不在新 prompt 键集合里”的缓存项（包括对象子缓存）全部清掉；</section>
</li>
<li>
<section style="color: #010101;">模型 / LoRA 变化：<br />
即便节点 ID 不变，输入签名和 is_changed 指纹不同，也会生成新键；旧条目先不命中，随后在下一次 prompt 绑定时被清空；</section>
</li>
<li>
<section style="color: #010101;">回切旧 workflow：<br />
因为在上一次切换时已经把旧 workflow 相关缓存清干净了，所以基本等于重新计算。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">适用场景</strong>：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">workflow 比较固定；</section>
</li>
<li>
<section style="color: #010101;">主要想在“一次执行当中”复用中间结果，不在意跨 prompt 的持久化。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6. LRU：代际 LRU 控制 outputs 尺寸</span></h1>
<p data-tool="mdnice编辑器">第二种策略是 LRU，主要解决的问题是：<strong style="color: #0e88eb;">在允许跨 prompt 复用输出的前提下，限制缓存总量，避免无限膨胀</strong>。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.1 初始化：只作用于 outputs</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">CacheSet.init_lru_cache</code>（<code style="color: #0e8aeb;">execution.py:127-135</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">init_lru_cache</span><span class="hljs-params">(self, cache_size)</span>:</span>
    self.outputs = LRUCache(CacheKeySetInputSignature, max_size=cache_size)
    self.objects = HierarchicalCache(CacheKeySetID)
</code></pre>
<p data-tool="mdnice编辑器">注意几点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">LRU <strong style="color: #0e88eb;">只作用于 outputs</strong>；</section>
</li>
<li>
<section style="color: #010101;">objects 仍然用 <code style="color: #0e8aeb;">HierarchicalCache</code>，不受 LRU 驱逐；</section>
</li>
<li>
<section style="color: #010101;">启动方式：<code style="color: #0e8aeb;">--cache-lru N</code>，且 <code style="color: #0e8aeb;">N &gt; 0</code>。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.2 LRU 的代际设计</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">LRUCache</code> 的骨架逻辑在 <code style="color: #0e8aeb;">comfy_execution/caching.py:299-337</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">LRUCache</span><span class="hljs-params">(BasicCache)</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">__init__</span><span class="hljs-params">(self, key_class, max_size=<span class="hljs-number" style="color: #d19a66;">100</span>)</span>:</span>
        self.max_size = max_size
        self.min_generation = <span class="hljs-number" style="color: #d19a66;">0</span>
        self.generation = <span class="hljs-number" style="color: #d19a66;">0</span>
        self.used_generation = {}
        self.children = {}

    <span class="hljs-keyword" style="color: #c678dd;">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">set_prompt</span><span class="hljs-params">(...)</span>:</span>
        self.generation += <span class="hljs-number" style="color: #d19a66;">1</span>
        <span class="hljs-keyword" style="color: #c678dd;">for</span> node_id <span class="hljs-keyword" style="color: #c678dd;">in</span> node_ids:
            self._mark_used(node_id)

    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">get</span><span class="hljs-params">(self, node_id)</span>:</span>
        self._mark_used(node_id)
        <span class="hljs-keyword" style="color: #c678dd;">return</span> self._get_immediate(node_id)

    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">_mark_used</span><span class="hljs-params">(self, node_id)</span>:</span>
        cache_key = self.cache_key_set.get_data_key(node_id)
        <span class="hljs-keyword" style="color: #c678dd;">if</span> cache_key <span class="hljs-keyword" style="color: #c678dd;">is</span> <span class="hljs-keyword" style="color: #c678dd;">not</span> <span class="hljs-literal" style="color: #56b6c2;">None</span>:
            self.used_generation[cache_key] = self.generation
</code></pre>
<p data-tool="mdnice编辑器">含义：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">有一个全局代数 <code style="color: #0e8aeb;">generation</code>，每次绑定新 prompt <code style="color: #0e8aeb;">generation += 1</code>；</section>
</li>
<li>
<section style="color: #010101;">每次：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">绑定 prompt 时，会把该 prompt 内所有节点标记为“在当前代被使用”；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">get</code> / <code style="color: #0e8aeb;">set</code> 时更新条目的 <code style="color: #0e8aeb;">used_generation[key]</code> 为当前代。</section>
</li>
</ul>
</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.3 容量驱逐：按「最老代」逐步清理</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">clean_unused()</code> 的一部分逻辑在 <code style="color: #0e8aeb;">comfy_execution/caching.py:314-323</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">clean_unused</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-keyword" style="color: #c678dd;">while</span> len(self.cache) &gt; self.max_size <span class="hljs-keyword" style="color: #c678dd;">and</span> self.min_generation &lt; self.
    generation:
        self.min_generation += <span class="hljs-number" style="color: #d19a66;">1</span>
        to_remove = [key <span class="hljs-keyword" style="color: #c678dd;">for</span> key <span class="hljs-keyword" style="color: #c678dd;">in</span> self.cache <span class="hljs-keyword" style="color: #c678dd;">if</span> self.used_generation[key] &lt; self.
        min_generation]
        <span class="hljs-keyword" style="color: #c678dd;">for</span> key <span class="hljs-keyword" style="color: #c678dd;">in</span> to_remove:
            <span class="hljs-keyword" style="color: #c678dd;">del</span> self.cache[key]
            <span class="hljs-keyword" style="color: #c678dd;">del</span> self.used_generation[key]
            <span class="hljs-keyword" style="color: #c678dd;">if</span> key <span class="hljs-keyword" style="color: #c678dd;">in</span> self.children:
                <span class="hljs-keyword" style="color: #c678dd;">del</span> self.children[key]
    self._clean_subcaches()
</code></pre>
<p data-tool="mdnice编辑器">简单归纳一下：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">只要 <code style="color: #0e8aeb;">len(cache) &gt; max_size</code>，就逐步提升 <code style="color: #0e8aeb;">min_generation</code>；</section>
</li>
<li>
<section style="color: #010101;">每提升一代，就删除“最近使用代 &lt; min_generation”的条目；</section>
</li>
<li>
<section style="color: #010101;">同步清除掉和这些 key 绑定的子缓存引用；</section>
</li>
<li>
<section style="color: #010101;">清理完再执行 <code style="color: #0e8aeb;">_clean_subcaches()</code> 做层级清扫。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.4 子图分区与代际配合</span></h2>
<p data-tool="mdnice编辑器">子缓存创建时会显式标记父节点和子节点“被使用”，避免刚刚生成的子图被误删。代码在 <code style="color: #0e8aeb;">comfy_execution/caching.py:338-349</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">ensure_subcache_for</span><span class="hljs-params">(self, node_id, children_ids)</span>:</span>
    <span class="hljs-keyword" style="color: #c678dd;">await</span> super()._ensure_subcache(node_id, children_ids)
    <span class="hljs-keyword" style="color: #c678dd;">await</span> self.cache_key_set.add_keys(children_ids)
    self._mark_used(node_id)
    cache_key = self.cache_key_set.get_data_key(node_id)
    self.children[cache_key] = []
    <span class="hljs-keyword" style="color: #c678dd;">for</span> child_id <span class="hljs-keyword" style="color: #c678dd;">in</span> children_ids:
        self._mark_used(child_id)
        self.children[cache_key].append(self.cache_key_set.get_data_key(child_id))
    <span class="hljs-keyword" style="color: #c678dd;">return</span> self
</code></pre>
<p data-tool="mdnice编辑器">配合前面提到的层级结构，就形成了“按 workflow 子图分区 + LRU 按代际清理”的整体行为。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">6.5 触发时机和行为总结</span></h2>
<p data-tool="mdnice编辑器">在 LRU 模式下：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">每次绑定 prompt：<br />
<code style="color: #0e8aeb;">generation += 1</code>，标记当前 prompt 的节点使用代为当前代；</section>
</li>
<li>
<section style="color: #010101;">每次 <code style="color: #0e8aeb;">get/set</code>：<br />
更新条目的 <code style="color: #0e8aeb;">used_generation</code> 为当前代；</section>
</li>
<li>
<section style="color: #010101;">每次 <code style="color: #0e8aeb;">clean_unused()</code>：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">当 <code style="color: #0e8aeb;">len(cache) &gt; max_size</code> 时，通过提升 <code style="color: #0e8aeb;">min_generation</code> 清除旧代条目；</section>
</li>
<li>
<section style="color: #010101;">额外清理无用子缓存。</section>
</li>
</ul>
</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">特点</strong>：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">可以跨 prompt 保留一部分中间结果；</section>
</li>
<li>
<section style="color: #010101;">由 <code style="color: #0e8aeb;">max_size</code> 控制缓存上限；</section>
</li>
<li>
<section style="color: #010101;">没有 RAM 压力感知：<code style="color: #0e8aeb;">poll()</code> 依然不做事。</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">适用场景</strong>：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">希望在多 workflow 之间部分复用缓存；</section>
</li>
<li>
<section style="color: #010101;">但机器内存有限，需要给 outputs 一个明确的容量上限；</section>
</li>
<li>
<section style="color: #010101;">对 RAM 细粒度控制没有强需求，或使用的是物理机 / 内存足够的环境。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7. RAM_PRESSURE：按可用内存压力驱逐</span></h1>
<p data-tool="mdnice编辑器">第三种策略是 RAM_PRESSURE，对应类是 <code style="color: #0e8aeb;">RAMPressureCache</code>。它继承自 <code style="color: #0e8aeb;">LRUCache</code>，但不按 max_size 做驱逐，而是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">通过 <code style="color: #0e8aeb;">poll(ram_headroom)</code>，在可用内存不足时按“OOM 评分”驱逐条目。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7.1 初始化：objects 仍然是层级缓存</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">CacheSet.init_ram_cache</code>（<code style="color: #0e8aeb;">execution.py:131-133</code>）：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">init_ram_cache</span><span class="hljs-params">(self, min_headroom)</span>:</span>
    self.outputs = RAMPressureCache(CacheKeySetInputSignature)
    self.objects = HierarchicalCache(CacheKeySetID)
</code></pre>
<p data-tool="mdnice编辑器">注意两个点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">RAM 模式下，<strong style="color: #0e88eb;">只有 outputs 会按 RAM 压力驱逐</strong>；</section>
</li>
<li>
<section style="color: #010101;">objects 不参与 RAM 驱逐，逻辑完全和 CLASSIC/LRU 下相同。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7.2 poll：可用 RAM 检测 + OOM 评分驱逐</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">poll</code> 的主逻辑在 <code style="color: #0e8aeb;">comfy_execution/caching.py:384-454</code>：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">poll</span><span class="hljs-params">(self, ram_headroom)</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">_ram_gb</span><span class="hljs-params">()</span>:</span>
        <span class="hljs-comment" style="font-style: italic; color: #5c6370;"># 优先 cgroup v2/v1，失败回退 psutil</span>
        ...
    <span class="hljs-keyword" style="color: #c678dd;">if</span> _ram_gb() &gt; ram_headroom: <span class="hljs-keyword" style="color: #c678dd;">return</span>
    gc.collect()
    <span class="hljs-keyword" style="color: #c678dd;">if</span> _ram_gb() &gt; ram_headroom: <span class="hljs-keyword" style="color: #c678dd;">return</span>
    clean_list = []
    <span class="hljs-keyword" style="color: #c678dd;">for</span> key, (outputs, _), <span class="hljs-keyword" style="color: #c678dd;">in</span> self.cache.items():
        oom_score = RAM_CACHE_OLD_WORKFLOW_OOM_MULTIPLIER ** (self.generation -
        self.used_generation[key])
        ram_usage = RAM_CACHE_DEFAULT_RAM_USAGE
        <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">scan_list_for_ram_usage</span><span class="hljs-params">(outputs)</span>:</span>
            <span class="hljs-keyword" style="color: #c678dd;">nonlocal</span> ram_usage
            ...
        scan_list_for_ram_usage(outputs)
        oom_score *= ram_usage
        bisect.insort(clean_list, (oom_score, self.timestamps[key], key))
    <span class="hljs-keyword" style="color: #c678dd;">while</span> _ram_gb() &lt; ram_headroom * RAM_CACHE_HYSTERESIS <span class="hljs-keyword" style="color: #c678dd;">and</span> clean_list:
        _, _, key = clean_list.pop()
        <span class="hljs-keyword" style="color: #c678dd;">del</span> self.cache[key]
        gc.collect()
</code></pre>
<p data-tool="mdnice编辑器">流程拆一下：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">获取可用 RAM</strong></p>
<p style="color: #000000;"><code style="color: #0e8aeb;">_ram_gb()</code> 的实现优先读取 cgroup 的限制：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">cgroup v2：<code style="color: #0e8aeb;">memory.max</code> / <code style="color: #0e8aeb;">memory.current</code>；</section>
</li>
<li>
<section style="color: #010101;">cgroup v1：<code style="color: #0e8aeb;">memory.limit_in_bytes</code> / <code style="color: #0e8aeb;">memory.usage_in_bytes</code>；</section>
</li>
<li>
<section style="color: #010101;">都失败才回退 <code style="color: #0e8aeb;">psutil.virtual_memory().available</code>。</section>
</li>
</ul>
<p style="color: #000000;">这解决了容器环境下“宿主机内存大，容器实际被限制”的常见问题。</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">阈值和 GC</strong></p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">如果可用 RAM &gt; <code style="color: #0e8aeb;">ram_headroom</code>，直接返回；</section>
</li>
<li>
<section style="color: #010101;">否则先跑一次 <code style="color: #0e8aeb;">gc.collect()</code>；</section>
</li>
<li>
<section style="color: #010101;">再测一次 RAM，如果还是不足，进入驱逐流程。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">为每个条目计算 OOM 评分</strong></p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">初始 <code style="color: #0e8aeb;">oom_score = RAM_CACHE_OLD_WORKFLOW_OOM_MULTIPLIER ** (generation - used_generation[key])</code>：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">大概意思是：越久没被用，分数指数级放大（默认倍数为 1.3，见 <code style="color: #0e8aeb;">comfy_execution/caching.py:365</code>）；</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">初始 <code style="color: #0e8aeb;">ram_usage = RAM_CACHE_DEFAULT_RAM_USAGE</code>（0.1，见 <code style="color: #0e8aeb;">360</code>）；</section>
</li>
<li>
<section style="color: #010101;">递归遍历 <code style="color: #0e8aeb;">outputs</code> 列表：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">CPU tensor：<code style="color: #0e8aeb;">numel * element_size * 0.5</code>（认为 CPU 上的 tensor 价值更高，折半）；</section>
</li>
<li>
<section style="color: #010101;">自定义对象：如果实现了 <code style="color: #0e8aeb;">get_ram_usage()</code> 就加上它；</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">最后 <code style="color: #0e8aeb;">oom_score *= ram_usage</code>，得到综合评分。</section>
</li>
</ul>
<p style="color: #000000;">所有条目按 <code style="color: #0e8aeb;">(oom_score, timestamp, key)</code> 排序，放入 <code style="color: #0e8aeb;">clean_list</code>。</p>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">按迟滞阈值逐个删除</strong></p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">只要 <code style="color: #0e8aeb;">_ram_gb() &lt; ram_headroom * RAM_CACHE_HYSTERESIS</code>，就从 <code style="color: #0e8aeb;">clean_list</code> 末尾 pop 一个 key 并删除；</section>
</li>
<li>
<section style="color: #010101;">每删一个都跑一次 <code style="color: #0e8aeb;">gc.collect()</code>；</section>
</li>
<li>
<section style="color: #010101;">迟滞倍数 <code style="color: #0e8aeb;">RAM_CACHE_HYSTERESIS</code> 默认 1.1，避免“刚删完又马上触发清理”的抖动。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;"><strong style="color: #0e88eb;">访问时间戳</strong></p>
<p style="color: #000000;">访问时会更新 timestamps（<code style="color: #0e8aeb;">comfy_execution/caching.py:376-382</code>）：</p>
</section>
</li>
</ol>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">   <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">set</span><span class="hljs-params">(self, node_id, value)</span>:</span>
       self.timestamps[self.cache_key_set.get_data_key(node_id)] = time.time()
       super().set(node_id, value)

   <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">def</span> <span class="hljs-title" style="color: #61aeee;">get</span><span class="hljs-params">(self, node_id)</span>:</span>
       self.timestamps[self.cache_key_set.get_data_key(node_id)] = time.time()
       <span class="hljs-keyword" style="color: #c678dd;">return</span> super().get(node_id)
</code></pre>
<p data-tool="mdnice编辑器">在 oom_score 一样时，timestamp 起到“最近访问优先保留”的作用。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7.3 提示绑定下的行为：不清理 outputs</span></h2>
<p data-tool="mdnice编辑器">RAM 模式下，<code style="color: #0e8aeb;">clean_unused()</code> 的行为与 CLASSIC 不同（见参考说明）：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">RAM 模式：<br />
<code style="color: #0e8aeb;">clean_unused()</code> 只做子缓存分区清理，不会删掉“当前 prompt 未使用”的 outputs 条目；</section>
</li>
<li>
<section style="color: #010101;">CLASSIC 模式：<br />
<code style="color: #0e8aeb;">clean_unused()</code> 会同时删掉当前 prompt 未用到的 outputs 条目。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">结果是：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">RAM 模式可以跨多个 workflow 长时间保留中间结果；</section>
</li>
<li>
<section style="color: #010101;">只有在 RAM 不够时才做清退。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">7.4 容器环境下需要注意的点</span></h2>
<p data-tool="mdnice编辑器">的 AutoDL 中，用 <code style="color: #0e8aeb;">psutil.virtual_memory().available</code>，在容器里看到的是宿主机内存，而不是容器的限额，导致永远“不触发回收”，最后 OOM。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">适用场景</strong>：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">多 workflow / 多模型 / 多 LoRA 同时存在，且希望尽可能长时间复用输出结果；</section>
</li>
<li>
<section style="color: #010101;">机器内存有限，但更关心“不 OOM”，而不是一个固定的 <code style="color: #0e8aeb;">max_size</code>；</section>
</li>
<li>
<section style="color: #010101;">特别适合容器环境（K8s / AutoDL 等），配合 <code style="color: #0e8aeb;">--cache-ram &lt;GB&gt;</code>。</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8. 一些落地建议</span></h1>
<p data-tool="mdnice编辑器">最后，用一段比较直接的建议收尾。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8.1 单模型 / workflow 稳定：用 CLASSIC 即可</span></h2>
<p data-tool="mdnice编辑器">特点：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">工作流基本不换；</section>
</li>
<li>
<section style="color: #010101;">主要希望避免一次执行中的重复计算（比如多次 preview）。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">用默认 CLASSIC：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">结构简单；</section>
</li>
<li>
<section style="color: #010101;">不参与复杂的跨 prompt 保留；</section>
</li>
<li>
<section style="color: #010101;">不需要担心 LRU 尺寸和 RAM 阈值调参。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8.2 多 workflow + 控制缓存尺寸：用 LRU</span></h2>
<p data-tool="mdnice编辑器">场景：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">有多套 workflow，在它们之间来回切；</section>
</li>
<li>
<section style="color: #010101;">机器内存不是特别大，希望 outputs 不要无限膨胀；</section>
</li>
<li>
<section style="color: #010101;">又希望某些常用子图能被复用。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">做法：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">启动时加 <code style="color: #0e8aeb;">--cache-lru N</code>（N 先给一个相对保守的值，比如几百到几千条，看内存曲线再调）；</section>
</li>
<li>
<section style="color: #010101;">让 <code style="color: #0e8aeb;">LRUCache</code> 用代际 + max_size 帮你自动做“近期常用保留、早期冷门清理”。</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8.3 多模型 / 多 LoRA / 容器环境：优先 RAM_PRESSURE</span></h2>
<p data-tool="mdnice编辑器">场景：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">容器 / 云平台（K8s、AutoDL 等）；</section>
</li>
<li>
<section style="color: #010101;">有较多模型、LoRA 和 workflow 混用；</section>
</li>
<li>
<section style="color: #010101;">内存被容器限制，容易因爆 RAM 掉进 OOM。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">做法：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">启动时用 <code style="color: #0e8aeb;">--cache-ram &lt;GB&gt;</code> 配一个“希望保留的 RAM 余量”；</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">比如容器给了 32GB，就设在 16–24GB 看情况；</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">让 <code style="color: #0e8aeb;">RAMPressureCache</code>：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">最大程度保留各个 workflow 的中间结果（包括应用 LoRA 后的模型对象等）；</section>
</li>
<li>
<section style="color: #010101;">在内存不足时，根据 OOM 评分优先清旧代、大内存条目。</section>
</li>
</ul>
</section>
</li>
</ul>
<p data-tool="mdnice编辑器">注意一点：即使有容器优化，如果平台本身的 cgroup 挂得不标准，<code style="color: #0e8aeb;">_ram_gb()</code> 的结果还是有可能偏离实际，这一点要结合平台文档确认。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">8.4 完全不想折腾：直接关缓存</span></h2>
<p data-tool="mdnice编辑器">如果你：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">环境受限；</section>
</li>
<li>
<section style="color: #010101;">或者调试阶段不想被缓存影响行为理解；</section>
</li>
</ul>
<p data-tool="mdnice编辑器">可以直接用：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">--cache-none</code><br />
对应 <code style="color: #0e8aeb;">CacheType.NONE</code>，<code style="color: #0e8aeb;">CacheSet.init_null_cache()</code> 走 <code style="color: #0e8aeb;">NullCache</code>，所有 get/set 都是 no-op。</section>
</li>
</ul>
<p data-tool="mdnice编辑器">代价就是：每次执行都完全重新算一遍。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">9. 小结一下</span></h1>
<p data-tool="mdnice编辑器">把上面的内容压缩成几句话：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">
<p style="color: #000000;">ComfyUI 有两路缓存：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">outputs</code> 存中间结果，真正用来省算力；</section>
</li>
<li>
<section style="color: #010101;"><code style="color: #0e8aeb;">objects</code> 存节点实例，只减少 Python 对象构造开销。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">键体系：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">输出：<strong style="color: #0e88eb;">输入签名 + is_changed 指纹（+ 条件下的 node_id）</strong>；</section>
</li>
<li>
<section style="color: #010101;">对象：**(node_id, class_type)**。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">四种策略：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">CLASSIC：默认层级缓存，按当前 prompt 键集合清理，不做 LRU / RAM 驱逐；</section>
</li>
<li>
<section style="color: #010101;">LRU：只对 outputs 做代际 LRU，配合 <code style="color: #0e8aeb;">max_size</code> 控制容量；</section>
</li>
<li>
<section style="color: #010101;">RAM_PRESSURE：在 LRU 基础上加 RAM 压力驱逐，在内存不足时按 OOM 评分清理；</section>
</li>
<li>
<section style="color: #010101;">NONE：彻底关掉缓存。</section>
</li>
</ul>
</section>
</li>
<li>
<section style="color: #010101;">
<p style="color: #000000;">在多模型 / 多 workflow / 多 LoRA 场景下：</p>
<ul style="color: #000000;">
<li>
<section style="color: #010101;">对象缓存 <code style="color: #0e8aeb;">objects</code> 始终是层级缓存，不参与 LRU / RAM 驱逐，在每次绑定 prompt 时按键集合清理；</section>
</li>
<li>
<section style="color: #010101;">模型权重 / LoRA 的真实驻留由模型管理层控制；</section>
</li>
<li>
<section style="color: #010101;">真正要关心的是：如何选择 outputs 的策略，让中间结果既能有效复用，又不会把内存打爆。</section>
</li>
</ul>
</section>
</li>
</ul>
<p data-tool="mdnice编辑器">如果我们在一个复杂的工作流环境里跑 ComfyUI，建议先搞清楚自己处于哪种场景，再结合上面的策略选项，把缓存调成我们能控制的状态，而不是让它在后台「自动长草」。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/11/comfyui-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在 AWS 的 EC2 上搭建换装模型 ComfyUI OOTDiffusion</title>
		<link>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/</link>
		<comments>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/#comments</comments>
		<pubDate>Wed, 26 Jun 2024 09:03:43 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[comfyUI]]></category>
		<category><![CDATA[OOTDiffusion]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=2246</guid>
		<description><![CDATA[项目地址：https://github.com/AuroBit/ComfyUI-OOTDiffusion 在  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>项目地址：https://github.com/AuroBit/ComfyUI-OOTDiffusion</p>
<p>在 AWS 上购买 <span style="color: #16191f;">g4dn.xlarge ，带有一个 gpu  nvidia 显卡</span></p>
<p>初始化机器，如果没有选带 nvidia 驱动的操作系统，需要自己安装 nvidia 的显卡</p>
<p><!--?xml version="1.0" encoding="UTF-8"?--></p>
<div></div>
<div></div>
<div style="padding-left: 30px;">sudo apt-get update #更新软件列表</div>
<div style="padding-left: 30px;">sudo apt-get install g++</div>
<div style="padding-left: 30px;">sudo apt-get install gcc</div>
<div style="padding-left: 30px;">sudo apt-get install make ubuntu-drivers</div>
<div style="padding-left: 30px;">sudo vim /etc/modprobe.d/blacklist-nouveau.conf</div>
<div style="padding-left: 30px;">blacklist nouveau</div>
<div style="padding-left: 30px;">options nouveau modeset=0</div>
<div style="padding-left: 30px;">sudo update-initramfs -u #更新系统</div>
<div style="padding-left: 30px;">sudo reboot # 重启</div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;">lsmod | grep nouveau</div>
<div style="padding-left: 30px;">sudo apt-get remove &#8211;purge nvidia*</div>
<div style="padding-left: 30px;">ubuntu-drivers devices</div>
<div style="padding-left: 30px;">sudo add-apt-repository ppa:graphics-drivers/ppa</div>
<div style="padding-left: 30px;">sudo apt-get update</div>
<div style="padding-left: 30px;">sudo apt-get install nvidia-driver-535 #此处数字要对应上面查询到的版本号</div>
<div style="padding-left: 30px;">sudo apt-get install mesa-common-dev</div>
<div style="padding-left: 30px;">sudo reboot # 重启</div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;">nvidia-smi</div>
<div></div>
<div></div>
<p>Clone 项目并安装相关依赖：</p>
<pre style="color: #1f2328;">conda create -n ootd python=3.10
conda activate ootd

conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia

<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Install nvcc compiler for torch cpp extensions</span>
conda install cuda-nvcc -c nvidia

#  Clone ComfyUI
git clone https://github.com/comfyanonymous/ComfyUI.git
<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Clone to custom_nodes</span>
git clone https://github.com/AuroBit/ComfyUI-OOTDiffusion.git custom_nodes/ComfyUI-OOTDiffusion

<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Install dependencies</span>
pip install -r custom_nodes/ComfyUI-OOTDiffusion/requirements.txt</pre>
<p>如果想外网访问，在启动的时候带上 &#8211;listen，如： python main.py &#8211;listen 0.0.0.0</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AI 大时代要懂的 2 种「编程范式」</title>
		<link>https://www.phppan.com/2024/06/2-programming-paradigms-you-need-to-know-in-the-ai-era/</link>
		<comments>https://www.phppan.com/2024/06/2-programming-paradigms-you-need-to-know-in-the-ai-era/#comments</comments>
		<pubDate>Sun, 23 Jun 2024 00:19:01 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[comfyUI]]></category>
		<category><![CDATA[LangChain]]></category>
		<category><![CDATA[编程范式]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=2243</guid>
		<description><![CDATA[2023 年 AI 新突破导出不穷，隔两个月就会有一个爆点出来，AI 迎来了一个新的里程碑时刻。从 OpenA [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="color: #424b5d;" data-tool="mdnice编辑器">2023 年 AI 新突破导出不穷，隔两个月就会有一个爆点出来，AI 迎来了一个新的里程碑时刻。从 OpenAI 推出的 ChatGPT 到 Midjourney 发布的 V5/V6 版本，AI 在各个领域都取得了突破性的进展。随着 AI 技术的日益成熟，我们也开始思考如何更好地利用 AI 工具来提高工作效率。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">到 2024 年， AI 应用、AI 配套的框架和工具如雨后春笋。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在这个 AI 大时代，有两种「编程范式」值得我们关注。为什么要打上引号呢？</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">因为这里所说的编程，已经不再局限于传统意义上的写代码，而是<strong>泛指利用各种工具和模型，将它们灵活组合，创造出新的应用和价值。</strong></p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">第一种范式是<strong>基于 ComfyUI 的编程</strong>。ComfyUI 是一个模块化的 AI 图像生成工具，它允许用户通过拖拽和连接不同的节点，轻松创建出令人惊艳的 AI 艺术作品。在 ComfyUI 上编程，你无需掌握复杂的代码知识，只需要理解每个模块的功能，并将它们以合理的方式组合在一起，就能得到理想的结果。这种直观、易用的特点，使得 ComfyUI 成为了平面设计、插画创作等领域的得力助手。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">第二种范式则是<strong>基于 LangChain 的编程</strong>。与 ComfyUI 注重视觉创作不同，LangChain 的重点在于自然语言处理。通过 LangChain，开发者可以将语言模型与外部数据源相结合，快速构建功能丰富的聊天机器人、智能文档助手等应用。LangChain 提供了一系列现成的模块和接口，使得即使是非 NLP 专业的程序员，也能在短时间内上手并应用到实际项目中。从客户服务到数据分析，LangChain 正在各行各业发挥着重要作用。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">相比传统的编程范式，<strong>AI 编程更加注重模块化和灵活组合</strong>。我们无需从零开始构建一个完整的系统，而是站在巨人的肩膀上，将现有的模型和工具进行拼装和优化。这种方式不仅大大降低了开发门槛，也极大地提高了开发效率。当然，AI 编程范式也并非完美无缺。对于一些需要高度定制化的场景，传统的代码编程仍然不可或缺。此外，AI 工具的使用也对开发者提出了更高的要求，需要我们对各种模型和算法有更全面的理解和把控。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">接下来，简单聊一聊这两种「编程范式」，</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">comfyUI</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 是一个基于 Stable Diffusion 的开源 AI 绘图工具，采用了<strong>模块化的节点式工作流设计</strong>。它通过将 Stable Diffusion 的各个组件和处理步骤抽象为独立的节点，使得用户可以通过直观的拖拽、连接操作来构建复杂的图像生成流程。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 解决了传统 AI 绘图工具易用性差、扩展性低的问题。其模块化设计和直观的 Web 界面大大降低了用户的使用门槛，无需深入了解底层技术细节，即可快速构建和调整工作流。同时，ComfyUI 还提供了强大的自定义节点机制，允许开发者轻松扩展新的功能和模型，使其能够适应不断发展的AI绘图领域。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 最初由开发者 Comfyanonymous 在 2022 年末发起，旨在提供一个简单、直观的 Stable Diffusion Web UI。<strong>早期版本实现了基本的节点类型和 Web 界面</strong>，展示了其模块化设计的优势，吸引了一批 AI 绘图爱好者的关注。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在 2023 年春夏，ComfyUI 进入了快速发展阶段。项目不断增加新的节点类型，如 ControlNet、Inpaint、Upscale等，支持更多的图像控制和后处理功能。同时，ComfyUI 引入了自定义节点机制，大大扩展了其功能和适用范围。项目也集成了更多 Stable Diffusion 衍生模型，为用户提供了更多选择。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">随着用户社区的不断壮大，ComfyUI 的生态也日益丰富。社区成员积极贡献工作流、节点脚本、训练模型等资源，推动项目的发展。ComfyUI 举办了一系列社区活动，促进了用户间的交流和创作。项目代码库也迎来了更多贡献者，社区力量成为 ComfyUI 发展的重要推动力。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">2023 年冬至今，ComfyUI 开始着眼于生态融合和应用拓展。项目与其他 AI 绘图工具建立了联系，支持工作流的导入导出和 API 集成。ComfyUI 也开始探索更多应用场景，如虚拟主播、游戏 mod 等，拓宽了 AI绘图的应用范围。越来越多的开发者和公司开始关注和使用 ComfyUI，其发展前景备受看好。未来，ComfyUI 将继续完善节点系统，引入更先进的 AI 技术，并加强生态建设，有望成为 AI 绘图领域的重要基础设施。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 中节点之间的关联是通过连接节点的输入和输出端口来实现的。每个节点都有预定义的输入和输出端口，用户可以在 UI 界面上将一个节点的输出端口连接到另一个节点的输入端口，从而建立节点之间的数据流和执行顺序。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在 ComfyUI 的后端实现中，这种节点关联是通过一个有向无环图来表示的。DAG 是一种常用的数据结构，用于描述一组节点之间的依赖关系和执行顺序。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">当用户在 UI 界面上连接两个节点时，实际上是在 DAG 中添加一条边，表示数据从源节点流向目标节点。ComfyUI 会根据 DAG 的拓扑结构，确定节点的执行顺序，并在运行时将数据在节点之间传递。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 中节点关联有一些关键实现细节：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>端口类型匹配</strong>：每个节点的输入和输出端口都有预定义的数据类型。在连接节点时，<strong>只有类型匹配的端口才能建立连接</strong>。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>数据传递</strong>：当一个节点执行完毕后，它会将结果数据发送到所有连接到其输出端口的节点的输入端口。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>执行调度</strong>：ComfyUI 会根据 DAG 的拓扑顺序，确定节点的执行顺序。当一个节点的所有输入数据都准备好时，该节点就可以开始执行。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>并行执行</strong>：无依赖关系的节点可以并行执行，提高执行效率。ComfyUI 会自动分析 DAG，找出可以并行执行的节点。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>缓存优化</strong>：对于某些计算量大的节点，ComfyUI 会缓存其计算结果，避免重复计算。当节点的输入数据没有变化时，就可以直接使用缓存的结果。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">ComfyUI 通过将节点组织成 DAG 的方式，实现了节点之间的关联和数据流控制。这种设计使得用户能够以可视化的方式创建复杂的图像处理工作流，同时也为并行优化和缓存优化提供了便利。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">comfyUI 中核心工作都是围绕其节点，其主要节点如下。</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>Text Prompt(文本提示)节点</strong>：提供文本描述，指导图像生成，输入是用户输入的文本提示如&#8221;1girl， brown hair， smile&#8221;；输出是编码后的文本向量(tokens)。几乎所有的绘图工作流都需要文本提示节点，它是指定图像内容的主要方式。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>Latent Image(潜在图像)节点</strong>：表示潜在空间中的图像，可以是随机初始化的噪音，也可以来自其他节点的输出。输入是噪音参数(如seed、尺寸等)，或其他节点传递的潜在图像。输出是潜在空间中的图像表示。作为采样起点(初始噪音)或中间结果(如图像修补、图生图等)。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>Sampler(采样器)节点</strong>：根据条件迭代优化潜在图像，使其解码后符合要求。输入是潜在图像、文本向量、其他条件(如 ControlNet 输出等)、采样步数、采样方法等参数。输出是优化后的潜在图像。采样是图像生成的核心，不同的采样器节点可以权衡生成质量和多样性。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>ControlNet 节点</strong>：根据附加条件(如边缘、姿态、深度等)控制生成图像。输入是潜在图像、条件图(如 Canny 边缘图)、ControlNet 模型参数等。输出是融合条件控制的潜在图像。用于生成满足特定结构、布局或属性要求的图像，如人像、动漫线稿上色等。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>VAE Encode(VAE编码)节点</strong>：将 RGB 图像编码为潜在空间表示。输入是 RGB 图像，如用户上传的图片。输出是潜在空间中的图像表示。用于图生图、图像修补、图像融合等需要以图像为起点的任务。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>VAE Decode(VAE解码)节点</strong>：将潜在空间表示解码为 RGB 图像。输入是潜在空间中的图像表示，通常来自采样器节点。输出是 RGB 图像。用于生成最终可见的图像结果。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>Upscale(放大)节点</strong>：增加图像分辨率，提高细节。输入是 RGB 图像，放大方法和倍数等参数。输出是放大后的 RGB 图像。用于生成高分辨率图像，常在VAE解码后使用。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>Inpaint(图像修补)节点</strong>：根据 mask 和提示，对图像的指定区域进行编辑。输入是原始图像、mask 图像、修改区域的文本提示等。输出是修改后的图像。用于对生成图像进行局部编辑，如去除伪影、修改细节等。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">除了以上常用节点，comfyUI 还有许多其他节点，如图像保存、剪裁、格式转换等，用于图像的后处理和输出。不同节点可以灵活组合，构建多种多样的绘图工作流，满足各类需求。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">举个例子，一个常见的工作流是：文本提示节点 -&gt; 潜在图像节点(初始噪音) -&gt; ControlNet节点(添加结构条件) -&gt; 采样器节点(优化潜在图像) -&gt; VAE解码节点(生成RGB图像) -&gt; 放大节点(提高分辨率) -&gt; 图像保存节点(输出最终结果)。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">如下图所示：</p>
<pre style="color: #000000;" data-tool="mdnice编辑器"><code style="color: #abb2bf;">+-------------------+     +------------------+     +------------------+
|                   |     |                  |     |                  |
|  Load Model Node  |----&gt;|  Text Encode Node|----&gt;|  Latent Image Node |
|                   |     |                  |     |                  |
+-------------------+     +------------------+     +-------------------+
                                                             |
                                                             |
                                                             v
                                                   +-------------------+
                                                   |                   |
                                                   |  ControlNet Node  |
                                                   |                   |
                                                   +-------------------+
                                                             |
                                                             |
                                                             v
+------------------+     +------------------+     +------------------+
|                  |     |                  |     |                  |
|  Upscale Node    |&lt;----|  VAE Decode Node |&lt;----|  Sampler Node    |
|                  |     |                  |     |                  |
+------------------+     +------------------+     +------------------+
         |
         |
         v
+------------------+
|                  |
|  Output Image    |
|                  |
+------------------+
</code></pre>
<p style="color: #424b5d;" data-tool="mdnice编辑器">通过对节点的连接配置和参数调整，用户可以精细控制每个步骤，实现理想的 AI 绘图效果。同时，comfyUI 也鼓励用户开发和分享自定义节点，不断扩展其功能和应用领域。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在理解 ComfyUI 的原理时，其核心还是理解 SD 的原理，简单来讲是通过大模型、CLIP 和 VAE 编码器以及采样器的协同工作，将文本提示转换为特征马赛克，再通过 VAE 解码器还原成图像，从而实现基于文本描述生成图像的功能。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">LangChain</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">什么是 LangChain？</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 是一个开源的 Python 框架，旨在帮助开发者更容易地构建基于大语言模型(LLM)的应用。它提供了一系列工具和组件，可以方便地与各种 LLM 模型集成，如OpenAI GPT、Anthropic Claude、Google PaLM等，而无需从头开始构建或进行大量的微调。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 旨在简化和统一语言模型与外部数据和应用程序的集成过程。它为开发者提供了一套灵活的工具和组件，可以轻松地将 OpenAI、Hugging Face 等流行的语言模型与知识库、API 等数据源相结合，从而快速构建功能强大的自然语言处理应用，如聊天机器人、智能文档助手、问答系统等。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 解决了语言模型应用开发中的诸多痛点。在 LangChain 出现之前，开发者需要编写大量的胶水代码来处理不同模型和数据源之间的交互，这不仅耗时耗力，也容易引入错误。LangChain 通过提供一致的接口和预构建的组件，大大简化了这一过程。它还引入了 Prompt Engineering 的理念，允许开发者通过设计优化的提示模板来引导模型生成更准确、更符合需求的输出。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 的发展历程可以追溯到 2021 年底。最初，它只是一个简单的概念验证项目，旨在探索如何将语言模型与外部数据集成。随着 ChatGPT 等大语言模型的出现和 NLP 技术的快速发展，LangChain 的潜力开始受到关注。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">2022 年，LangChain 迎来了重大更新和扩展。它引入了更多的集成选项，支持了更多种类的数据源和下游应用。同时，LangChain 的社区也在不断壮大，越来越多的开发者开始贡献代码和分享经验。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">2023 年，伴随着 AI 的大爆发，LangChain 迎来了爆发式增长。它成为了开发 AI 应用的必备工具之一，在各大技术论坛和社交平台上频频被提及。LangChain 也加速了版本迭代和功能更新，引入了更多高级特性，如 Agent 和 Memory，进一步增强了其适用性和性能。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">如今，LangChain 已经发展成为一个成熟而强大的 NLP 应用开发框架。它不仅帮助开发者大幅提高了开发效率，也为各行各业带来了前所未有的智能化应用。展望未来，LangChain 还将持续演进，与最新的 AI 模型和技术保持同步，为开发者和用户带来更多惊喜。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 的核心是将 LLM 与外部数据源连接，并通过 prompt engineering 技术来优化 LLM 的输入输出，从而生成更加准确、相关的结果。它的主要组件和功能如下：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>模型输入输出（Model I/O）</strong>：对接各种 LLM 模型的 API，提供统一的接口。支持 OpenAI、Anthropic、Hugging Face 等主流 LLM 服务商。</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">语言模型（Language Models）：支持大型语言模型（LLM）和聊天模型（ChatModel）的交互接口。</section>
</li>
<li>
<section style="color: #424b5d;">提示模板（Prompt Templates）：用于生成模型输入的预定义配方，支持两种主要类型是：PromptTemplate：生成字符串提示 和 ChatPromptTemplate：生成聊天消息列表提示。</section>
</li>
<li>
<section style="color: #424b5d;">示例选择器（Example Selectors）：提供训练、调优、测试和控制模型行为的示例输入输出。</section>
</li>
<li>
<section style="color: #424b5d;">输出解析器（Output Parsers）：用于将模型返回的文本结果格式化为目标对象、JSON 或数组等。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>数据连接（Data Connection）</strong>：在许多 LLM 应用程序中，用户特定的数据不在模型的训练集中，这可能是通过检索增强生成（RAG）实现的。RAG 的主要方法是检索外部数据，并在生成步骤中传递给 LLM。这样，LLM 就可以使用外部数据来增强生成的结果，从而提高应用程序的性能和准确性。</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">文档加载器（Document Loaders）：将不同数据源的非结构化文本加载为文档对象，并支持延迟加载。</section>
</li>
<li>
<section style="color: #424b5d;">文档转换器（Document Transformers）：对加载的文档进行处理，包括文本拆分、冗余过滤、元数据提取等。</section>
</li>
<li>
<section style="color: #424b5d;">文本嵌入模型（Text Embedding Models）：将文本转换为向量表示，用于文本检索、信息推荐、知识挖掘等。</section>
</li>
<li>
<section style="color: #424b5d;">矢量存储（Vector Stores）：负责存储嵌入数据并执行矢量搜索。</section>
</li>
<li>
<section style="color: #424b5d;">检索器（Retrievers）：从大规模文本库中检索与查询相关的文本段落，提供问答系统的额外上下文支持。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>链（Chains）</strong>：组件化的方式将一系列操作连接在一起形成数据处理的工作流，如数据检索、内容生成、翻译等可复用的任务执行流程。常见的链包括 LLMChain、SequentialChain、RouterChain 等。</section>
<ul class="list-paddingleft-1">
<li>
<section style="color: #424b5d;">基础链（LLMChain）：围绕语言模型的简单链。由提示模板和语言模型组成，用于格式化提示并返回 LLM 输出。</section>
</li>
<li>
<section style="color: #424b5d;">路由链（RouterChain）：可以动态选择下一条链，包括 LLMRouterChain 和 EmbeddingRouterChain。</section>
</li>
<li>
<section style="color: #424b5d;">顺序链（SequentialChain）：将多个链顺序连接，支持 SimpleSequentialChain 和更通用的 SequentialChain。</section>
</li>
<li>
<section style="color: #424b5d;">转换链（TransformChain）：在链之间添加自定义转换函数。</section>
</li>
<li>
<section style="color: #424b5d;">文档链（DocumentsChain）：处理多个文档输入。</section>
</li>
</ul>
</li>
<li>
<section style="color: #424b5d;"><strong>记忆（Memory）</strong>：为 Chains 和 Agents 提供对话状态记忆能力，用于在链之间存储和传递信息，实现上下文感知。常见的包括 ConversationBufferMemory、ChatMessageHistory 等。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>代理（Agents）</strong>：一种特殊的 Chain，可根据目标进行工具选择、动作规划和迭代求精。使用 LLM 作为大脑自动思考和决策，执行动作完成任务。包括 ZeroShotAgent、ReAct、Self Ask With Search 等。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>回调（Callbacks）</strong>：连接到 LLM 申请的各个阶段，用于日志记录、监控等。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 主要解决了以下问题：</p>
<ul class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">简化了与不同语言模型的交互。</section>
</li>
<li>
<section style="color: #424b5d;">提供了标准化的方法来生成和管理提示。</section>
</li>
<li>
<section style="color: #424b5d;">允许用户为模型提供示例输入输出，以优化模型性能。</section>
</li>
<li>
<section style="color: #424b5d;">支持非结构化文本的加载、转换和处理。</section>
</li>
<li>
<section style="color: #424b5d;">使文本嵌入和检索更加方便，支持向量空间中的各种运算。</section>
</li>
<li>
<section style="color: #424b5d;">通过链的概念，允许将多个组件组合成复杂的工作流程。</section>
</li>
<li>
<section style="color: #424b5d;">实现了对话的上下文感知能力。</section>
</li>
<li>
<section style="color: #424b5d;">通过代理自动执行任务，提高了应用的智能化水平。</section>
</li>
</ul>
<p style="color: #424b5d;" data-tool="mdnice编辑器">那如何使用呢？</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">使用 LangChain 的一般步骤可能包括：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;">根据需求选择合适的组件，如语言模型、提示模板等。</section>
</li>
<li>
<section style="color: #424b5d;">使用文档加载器加载数据，并利用文档转换器进行预处理。</section>
</li>
<li>
<section style="color: #424b5d;">配置链，将不同的组件组合起来，创建工作流程。</section>
</li>
<li>
<section style="color: #424b5d;">利用记忆组件在链之间传递上下文信息。</section>
</li>
<li>
<section style="color: #424b5d;">定义代理，使用 LLM 进行自动决策和执行。</section>
</li>
<li>
<section style="color: #424b5d;">设置回调，进行日志记录和监控。</section>
</li>
<li>
<section style="color: #424b5d;">根据具体应用场景进行调整和优化。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在使用过程中有一些注意的事项：</p>
<ol class="list-paddingleft-1" style="color: #000000;" data-tool="mdnice编辑器">
<li>
<section style="color: #424b5d;"><strong>根据任务选择合适的 LLM</strong>：不同的 LLM 适用不同任务，并有不同的使用成本，需要根据实际情况权衡。在使用 LangChain 前，需要明确了解自己的需求和应用场景，选择和配置合适的组件和模型。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>注意提示工程</strong>：LLM 的效果很大程度取决于提示的设计，需要遵循最佳实践，多进行实验和迭代。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>数据的隐私和安全</strong>：在涉及用户数据时，注意数据的安全性和隐私保护，遵循相关法律法规和最佳实践。特别是在 fine-tuning 或者数据索引时，要注意数据的隐私和版权问题。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>模型的公平性和伦理性</strong>：LLM 可能会放大数据中的偏见，产生有害或者不道德的内容，需要谨慎使用。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>应用的可解释性</strong>：端到端的 LLM pipeline 往往是个黑盒，要考虑如何向用户解释其工作原理和局限性。</section>
</li>
<li>
<section style="color: #424b5d;"><strong>成本和效率优化</strong>：要密切关注 LLM 调用次数、向量检索等环节的耗时和费用，权衡精度和成本。对于大规模数据处理或高吞吐量的应用，需关注性能优化，可能需要并行处理、缓存机制、硬件加速等手段。</section>
</li>
</ol>
<p style="color: #424b5d;" data-tool="mdnice编辑器">通过合理的使用和配置，LangChain 可以极大地简化复杂 AI 应用的开发流程，提高开发效率和应用性能。LangChain 的设计使得开发者可以灵活地构建和定制 AI 应用程序，以适应不同的业务需求。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">LangChain 适用于构建各种 LLM 驱动的应用，比如智能对话助手、知识库问答、数据分析、文案创作等。LangChain 正在成为 LLM 应用开发领域的生产力工具，促进更多创新产品和服务的诞生。</p>
<h1 style="color: #000000;" data-tool="mdnice编辑器"><span style="font-weight: bold; color: #e7642b;">小结</span></h1>
<p style="color: #424b5d;" data-tool="mdnice编辑器">AI 编程范式正在为我们开启一个全新的创作空间。ComfyUI 让设计变得前所未有的简单，LangChain 则让智能对话唾手可得。站在时代的十字路口，拥抱 AI，学习新的编程范式，我们就能更从容地迎接未来的挑战与机遇。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">但，纸上得来终觉浅，绝知此事要躬行。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">在实际的工作中，这些要用起来，才能有更深刻的体会。</p>
<p style="color: #424b5d;" data-tool="mdnice编辑器">以上。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2024/06/2-programming-paradigms-you-need-to-know-in-the-ai-era/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
