作者归档:admin

AI Agent 的 Skill 和行业 Workflow

在 2025 年的 10 月份,Anthropic 发布了 Claude 模型的一项重大更新的 Agent Skills,它允许用户将专业知识、脚本和资源打包成模块化的“技能文件夹”(Skill folders),让 AI 能在特定工作场景中更专业地执行任务。

如果我们在做行业 Agent、内部 Copilot、或想把 Claude Code / API 用在业务里,那就需要我们在做之前把「Skill」和「行业 Workflow」这两件事想清楚,并知道从哪里下手。

1. Skill 和行业 Workflow 的概念

1.1 Skill 是什么?

简单说:

Skill = 给模型的一份可复用「操作说明书 + 流程模板」

在 Claude 体系里,Skill 是一个带 SKILL.md 的文件夹,它告诉模型:
“在什么情况下该用我、我要完成什么任务、要按什么步骤做、输出要长什么样。”

特点有几个:

  • 面向具体任务,不是一个抽象的「能力标签」
    例如:生成符合公司品牌规范的 PPT按照内部代码规范重构文件按财务模板做对账报告
  • 本质上是文字写清楚的 SOP + 可选的脚本
    主体就是 Markdown 文档,有需要时再绑上 Python 脚本去做确定性处理。
  • 可以被模型自动发现和按需加载
    模型不会一直把完整 Skill 塞在上下文里,只在命中时再读取详细内容。

它和我们平时说的「提示词」的区别在于:
提示词是一次性、散落的;Skill 是结构化、可版本化、可共享的。

1.2 行业 Workflow 是什么?

Workflow 可以理解为:

把行业中的业务流程,做成清晰的步骤编排和 IF-ELSE 逻辑。

过去这些东西已经存在于:

  • 各种脚本、RPA、BPM 系统
  • 系统之间的 API 调用编排
  • 内部运维 / 运营同学脑子里和文档里的 SOP

在 Agent 语境下,我们关心的是一件事:

怎么把这些已有流程封装成「可由 Agent 触发、可观测、可审计」的工作流节点。

行业 Workflow 的关键特征:

  • 强约束:
    对输入 / 输出有严格格式要求,执行过程里有清晰的分支、回滚、告警。
  • 强依赖业务 Know-how:
    为什么要这样分支,Why 在流程里,而不是在模型参数里。
  • 长期稳定运行:
    一旦跑到生产,就不希望被大模型的「心情」影响。

2. Claude Code 的 Skill

在 Claude 的整体设计里,Skills 是一个非常核心的扩展机制。它解决了两个问题:

  1. 如何在不撑爆上下文的前提下,给模型装很多垂直能力?
  2. 如何让业务团队通过“写文档”的方式,而不是“写模型”的方式扩展能力?

2.1 一个 Skill = 一个带 SKILL.md 的文件夹

Claude 官方定义里,一个 Skill 的最小单元就是:

  • 一个文件夹
  • 里面一个 SKILL.md
  • 也可以再带一些脚本、资源文件

官方给出的模板是这样的:

---
name: my-first-skill
description: 这是一个关于此 Skill 能做什么以及何时使用它的清晰描述。
---
# 我的第一个 Skill

[在这里添加您的指令,Claude 在激活此 Skill 时会遵循这些指令]

## 示例
用法示例 1
用法示例 2

几个要点:

  • name:唯一标识,最好跟任务直接相关。
  • description:非常重要,模型靠这个来判断「什么时候用你」。
  • 正文部分:
    写清楚目标、步骤、注意事项、输出格式、示例

只要这一个 Markdown 写好了,一个可用 Skill 就成立了,不需要额外的配置文件。

2.2 具体例子:PPT Skill

官方仓库里有一个 PPTX 相关的 Skill,SKILL.md 类似下面这种结构:

  • YAML Frontmatter:说明 Skill 名称、用途(处理 PPTX)
  • 正文:分章节写

    • 如何解析 PPTX
    • 如何修改版式
    • 如何保证品牌颜色与模板统一
    • 输入 / 输出约定
    • 示例调用方式

Claude 的做法是:

  1. 会话启动时,只把所有 Skill 的 name + description 读一遍,放到系统级提示里。
  2. 当用户输入与某个 Skill 的描述高度匹配时,Claude 再去把这个 Skill 的完整内容加载到上下文中。

这就是文档里提到的「渐进式披露(Progressive Disclosure)」机制。

2.3 渐进式披露

这个词其实有点装,但装得有点厉害,使用这种方式的原因很直接:Token 成本和性能。

  • 初始加载时,每个 Skill 只占用几十个 Token(元信息)。
  • 真正用到的时候,才把 SKILL.md 的主体搬进来。
  • 如果 Skill 还拆成多个文件,Claude 只会读当前任务需要的那部分。

结论:我们可以放心装很多 Skill,而不用太担心上下文被占满。

2.4 Skill 里可以带代码

文档里也提到,Skill 可以带 Python 脚本等可执行文件。

用途主要有两类:

  1. 做确定性计算 / 校验

    • 排序、过滤、格式校验
    • 比如:生成 GIF 之后检查文件大小是否符合 Slack 限制
  2. 做简单的集成调用

    • 调一个内部 API
    • 读取一个本地文件,然后把内容返给模型

设计上,有一条很实用的边界:

  • 流程和策略写在 SKILL.md
  • 需要 100% 确定性 的步骤写在脚本里

模型不负责「每次都从零写代码」,而是调用你已经写好、已经验证过的代码

3. Skill 和 Tool / MCP 的边界

很多人会把 Skill、Tool、MCP 混在一起,这里做个简单对比方便后面聊 Workflow。

3.1 Skill:教模型「怎么做」

  • 把我们的 SOP、套路、模板,变成模型可执行的说明书。
  • 适合:

    • 结构化写作
    • 格式转换
    • 合规校验
    • 数据清洗 / 整理
  • 优点:

    • 写文档就能做定制
    • Token 成本可控
    • 容易版本化和团队共享

3.2 MCP / Tools:帮模型「去做」

  • MCP 解决的是:如何以统一协议,让模型调用外部系统 / 数据源 / API。
  • 它关注的是:

    • 怎么查 GitHub
    • 怎么调 CI/CD
    • 怎么查数据库
    • 怎么发 Slack 消息

简要总结就是一句话:Skill 面向流程,MCP 面向集成。

3.3 Skill + MCP 的组合

在一个典型任务里:

  • MCP 负责:拿到需要的数据、执行动作
  • Skill 负责:拿到这些结果后怎么分析、怎么生成符合规范的输出

这其实已经非常接近我们后面要讲的「Workflow + Agent」的拆分思路。

4. 行业 Workflow:Skill 落地的载体

前面讲的是 Skill 这一颗颗能力点”,接下来要看它们怎么和行业 Workflow 结合。

4.1 Agent 是交互方式,不是业务本身

再强调一次我们之前文章中的观点:Agent 是交互方式,不是业务本身

在行业里,Agent 更适合作为:

  • 自然语言入口
  • 意图理解与参数提取
  • 初步判断和分发

真正的行业壁垒在于:

  • 我们内部沉淀的 SOP
  • 历史案例和边缘场景处理方式
  • 审批链路和风控规则

这些东西,应该放在:

  • Workflow 编排系统
  • 规则引擎
  • Skill + MCP 的组合

而不是「指望一个通用 Agent 自己学出来」。

4.2 为什么不能纯 Agent?

  1. 幻觉和确定性冲突

    • 行业里很多流程(财务、生产、安全)对错误零容忍。
    • 1% 的错误率,对于 Demo 可以接受,对生产不行。
  2. 过程黑盒,难以审计

    • Agent 的推理链路在模型内部
    • 出现问题难以复盘和归责
    • 很难满足合规和审计要求
  3. 成本和延迟

    • 让模型去规划每个按钮、每个 if-else,是在烧算力
    • 这些确定性逻辑用传统代码 / Workflow 做更合适

所以,在企业 / 行业场景里,更现实的模式是:

Workflow + Agent
Agent 做理解和决策,Workflow 做执行和兜底。

5. 「Workflow + Agent」的混合架构

把前面的点合起来,可以得到一个常见的分层结构。

5.1 顶层:意图理解与路由(Agent)

职责只有三件:

  1. 理解用户在说什么(意图识别)
  2. 把需要的参数补齐(参数提取 + 追问)
  3. 决定触发哪个 Workflow / Skill 组合(路由)

流程可以简单画成:

用户 → 自然语言
→ Agent:识别意图 + 提取参数
→ 选中对应 Workflow(或再转给某个二级 Agent)
→ Workflow 执行
→ 结果交给 Agent 格式化给用户

这一步里,Skill 可以怎么用?

  • 把「意图分类规范」「参数提取规则」「话术模板」写成一个 Skill
  • 在 Skill 里明确:

    • 出现哪些关键词 / 条件时,对应什么意图
    • 提取不到关键信息时,按怎样的模板向用户追问

5.2 中间层:RAG + 决策

有些问题不能直接映射到单个 Workflow,需要先查知识再决定走哪条路。

典型例子:

“设备报警 E03,我该怎么办?”

步骤一般是:

  1. Agent 调用 RAG,在知识库中检索 E03 的说明和处理方案。
  2. Skill 里定义好:

    • 如何解释错误码
    • 不同错误码对应的处理选项
  3. 根据检索结果和规则,决定触发:

    • 远程重启流程
    • 提交工单流程
    • 安排现场工程师流程

这里的组合关系是:

  • RAG:提供上下文知识
  • Skill:约束「如何做决策、如何提问、如何输出」
  • Workflow:完成最终执行动作

5.3 底层:确定性执行(Workflow / RPA / 脚本)

这一层的唯一要求:

不要信模型,要信代码和流程编排。

包括:

  • API 调用链
  • BPM 流程
  • RPA 机器人
  • 定时任务
  • 数据库操作事务

这里非常适合做成「Skill + MCP + Workflow」的组合:

  • Workflow 把一串 API / RPC / 脚本串起来
  • MCP 把外部系统包装成标准工具
  • Skill 负责:

    • 输入规范
    • 输出规范
    • 错误处理策略
    • 不同状态码的解释

最后返回给 Agent 的应该是:

  • 清晰的状态(成功 / 失败 / 部分成功)
  • 明确的字段(JSON 等结构化结果)
  • 标准错误码和错误信息

5.4 最后一层:结果转述(Agent)

Agent 的工作只是:

  • 把结构化结果翻译成人能看懂的话
  • 必要时附上详细解释和后续建议
  • 避免「编故事」,严格按返回字段说话

在这一步也可以挂一个简单 Skill:

  • 统一输出口吻
  • 统一敏感信息处理方式
  • 统一错误提示文案

6. Skill 在行业 Workflow 里的落地方式

回到文章标题里的核心问题:行业 Workflow 如何通过 Skill 落地?

可以拆成三步。

6.1 把「人做事的方式」变成 Skill

先不碰系统,先去梳理:

  • 关键流程当前是怎么执行的?
  • 有哪些「资深同事会说:新人容易犯错的点」?
  • 有没有文档 / 模板 / 复盘材料?

然后做的事情是:

  1. 挑出重复度高、流程相对固定的一批任务。
  2. 每个任务建一个 Skill 文件夹,写 SKILL.md

    • 场景描述:什么时候应该用这个 Skill?
    • 输入要求:有哪些字段,格式是什么?
    • 处理步骤:拆成 1、2、3…
    • 输出规范:JSON 字段 + 人类可读的模板
    • 示例:2~3 个高频真实例子

第一轮不用追求覆盖所有流程,重点是把写 Skill 这件事本身跑顺

6.2 把「系统做事的方式」变成 Workflow + MCP

接下来梳理现有系统资产:

  • 哪些已有 API / 脚本 / RPA 可以直接复用?
  • 哪些流程现在是人工填表 + 审批 + 抄数?
  • 哪些操作有合规 / 风控要求,必须严格走系统?

然后做:

  1. 把可复用的系统能力包装成 MCP / 内部 API。
  2. 用我们熟悉的方式编排成 Workflow(BPM / 编排平台 / 自写 Orchestrator)。
  3. 明确每个 Workflow 的:

    • 输入结构
    • 输出结构
    • 错误码定义
    • 审计日志

这一步的原则是:

尽量少改存量系统,尽量通过「外面包一层」的方式让它变成可调用的 Workflow 组件。

6.3 用 Skill 把「人」和「系统」连起来

最后一步,把 Skill 作为桥梁:

  • 上游:Agent 与用户对话
  • 中游:Skill 指导 Agent 该怎么理解、怎么提问、怎么路由
  • 下游:Workflow/MCP 真正执行动作

一个典型链路会变成:

  1. 用户输入需求
  2. Agent 用「意图 Skill」判断任务类型
  3. 分发给对应领域 Agent
  4. 领域 Agent 读取对应 Skill:

    • 补齐参数
    • 调用 RAG 查规则
    • 决定调用哪个 Workflow
  5. Workflow 执行,通过 MCP / API 触达系统
  6. 返回结果由领域 Agent 按「输出 Skill」转成年类可读结果
  7. Agent 统一封装成对用户的话术

所有「经验」、「SOP」、「注意事项」,尽量沉淀在 Skill 里:

  • 方便以后版本升级
  • 方便新业务线复用
  • 方便做变更审计(Skill 本身可以版本控制)

7. 实施过程中的几个注意点

7.1 先把 Skill 写“够细”,再考虑自动化程度

很多团队上来就想着「全自动」,结果 Agent 兜不住,Workflow 无法覆盖异常,最后完全不敢放生产。

相对稳妥的节奏是:

  1. 用 Skill 把流程写细写透,先跑一段时间「人机协同」:

    • Agent 给出建议
    • 人来点确认 / 修改
  2. 统计哪些环节几乎没有人工干预
  3. 把这些环节下沉成 Workflow,逐步提高自动化比例

这样做有一个副作用:整个流程的「隐性知识」会被 Skill 强制写出来,对组织本身也是一种梳理。

7.2 旧系统是企业的「来时路」

很多看起来陈旧的:

  • 定时脚本
  • 报文接口
  • Excel 宏

从「Workflow + Agent」的角度看都是资产。
Skill 负责解释「什么时候、为什么、怎么用它」,MCP / Workflow 负责「怎么安全调用它」。

相比完全重构,一个实用的策略是:

给旧系统加一个 AI 适配层,而不是要求旧系统「变成 AI 原生」。

7.3 结构化数据回流

Agent 与用户的对话里,有大量可以反哺业务的信息:

  • 用户真实需求
  • 高频异常
  • 流程里的瓶颈点
  • 新出现的边缘场景

建议在设计时就准备好:

  • 把关键字段结构化写入日志 / 数据库
  • 定期用这些数据更新:

    • Skill 内容
    • RAG 知识库
    • 流程设计(Workflow)

不要只留下聊天记录,要留下可分析的行为数据

8. 小结

把前面的内容合在一起,其实可以简化为三条:

  1. Skill 把「怎么做事」固化下来

    • 它是 Agent 的“操作手册”
    • 它让流程可以被描述、复用、版本化
  2. Workflow 把「谁来做、何时做、按什么顺序做」编排起来

    • 它对接真实系统和资源
    • 它保证执行的确定性和审计能力
  3. Agent 把「人类模糊的需求」翻译成「可以被 Skill + Workflow 执行的指令」

    • 它是交互层和调度层
    • 它不是行业壁垒本身

在这个结构下,“降本增效”不再是一个抽象口号,而是一个比较直观的路径:

  • 过去那些无法自动化的非结构化需求(邮件、沟通、模糊指令),
  • 通过 Agent + Skill 变成可结构化的任务描述,
  • 再通过 Workflow + MCP 交给稳定的代码和系统去执行。

从研发团队视角看,这套东西真正改变的是工作方式

  • 从「写提示」变成「设计并维护一套可执行流程」;
  • 从「做一次性 Demo」变成「搭一套能长期演进的智能基础设施」。

如果你正在做行业 Agent,或者准备在内部推一个 AI 助手,可以先从一件事开始:

挑一个你们团队最常做、步骤最清晰、但最浪费时间的任务,把它完整写成一个 Skill,再把现有系统封装成一个 Workflow。
这两个拼起来,基本就是你们自己的第一个「行业 Workflow + Agent」原型。

以上。

ComfyUI 的缓存架构和实现

围绕 ComfyUI,大家讨论最多的是节点、工作流、算力这些,真正去看缓存细节的人其实不多。但只要你开始在一台机器上堆多个模型、多个 LoRA、多个 workflow,缓存策略就会直接决定这几件事:

  • 你是算力浪费,还是把显存 / 内存用在刀刃上;
  • 容器会不会莫名其妙 OOM;
  • 工作流切换时,是“秒级热身”还是“从头再来”。

这篇文章只做一件事:把 ComfyUI 当前的缓存架构和实现讲清楚,重点是三类策略(CLASSIC / LRU / RAM_PRESSURE)在「键怎么算、什么时候命中、什么时候过期」上的差异,以及在多模型、多 LoRA、多 workflow 场景下应该怎么选择。

1. 整体架构:两路缓存 + 四种策略

先把整体结构捋清楚。

1.1 两类缓存:outputs 和 objects

在执行一个工作流的时候,ComfyUI 维护了两类缓存:

  • outputs
    存中间结果和 UI 输出。命中时可以直接跳过节点执行,这部分是真正省计算的地方。
  • objects
    存节点对象实例(类实例),避免每次重新构造节点对象。

这两类缓存由一个统一的集合类 CacheSet 来管理。核心结构在 execution.py 中:

class CacheType(Enum):
    CLASSIC = 0
    LRU = 1
    NONE = 2
    RAM_PRESSURE = 3

class CacheSet:
    def __init__(self, cache_type=None, cache_args={}):
        if cache_type == CacheType.NONE:
            self.init_null_cache()
        elif cache_type == CacheType.RAM_PRESSURE:
            cache_ram = cache_args.get("ram"16.0)
            self.init_ram_cache(cache_ram)
        elif cache_type == CacheType.LRU:
            cache_size = cache_args.get("lru"0)
            self.init_lru_cache(cache_size)
        else:
            self.init_classic_cache()
        self.all = [self.outputs, self.objects]

不管是哪种策略,结构都是:

  • outputs按输入签名做 key
  • objects按 (node_id, class_type) 做 key

1.2 四种策略:CLASSIC / LRU / RAM_PRESSURE / NONE

从启动参数到缓存策略的选择,大致是这样的优先级(在 main.py 中):

  • 指定 --cache-lru → 用 LRU;
  • 否则指定 --cache-ram → 用 RAM_PRESSURE;
  • 否则指定 --cache-none → 完全关闭缓存;
  • 都没指定 → 默认 CLASSIC。

CacheType 的定义前面已经贴了。不同策略只决定 outputs 的实现方式,而 objects 基本始终使用层级缓存 HierarchicalCache(CacheKeySetID),后面细讲。


2. 缓存键:输入签名 + 变化指纹

缓存是否命中,首先取决于“key 算得是否合理”。ComfyUI 的设计核心是:

  • 输出缓存(outputs):用“输入签名 + is_changed 指纹(+ 某些情况下的 node_id)”作为 key;
  • 对象缓存(objects):用 (node_id, class_type) 作为 key。

2.1 输出缓存:输入签名是怎么来的

输出键的生成由 CacheKeySetInputSignature 负责,核心逻辑在 comfy_execution/caching.py:100-126

async def get_node_signature(self, dynprompt, node_id):
    signature = []
    ancestors, order_mapping = self.get_ordered_ancestry(dynprompt, node_id)
    signature.append(await self.get_immediate_node_signature(dynprompt, node_id,
    order_mapping))
    for ancestor_id in ancestors:
        signature.append(await self.get_immediate_node_signature(dynprompt,
        ancestor_id, order_mapping))
    return to_hashable(signature)

这里有几个点:

  1. 不只看当前节点
    它会按拓扑顺序把“当前节点 + 所有祖先”的签名拼在一起。这保证了只要整个子图的拓扑和输入一致,输出就能命中缓存。

  2. immediate 签名包含什么
    get_immediate_node_signature 会把这些信息打包进去(位置见同文件 108–126):

    • class_type:节点类型;
    • is_changed 指纹(通过 fingerprint_inputs/IS_CHANGED);
    • 必要时的 node_id
    • 有序的输入值/链接。
  3. 什么时候把 node_id 也算进 key
    当节点被声明为非幂等,或者内部有 UNIQUE_ID 这种隐含输入时,会把 node_id 加进签名(见 comfy_execution/caching.py:18-23, 116),避免“看起来一样”的节点被错误复用。

最后通过 to_hashable 转成可哈希结构(tuple/frozenset 等),作为最终键值。

结果是:

  • 输入完全相同 → key 相同 → 直接命中;
  • 任意一个上游节点输入或参数变化 → 指纹变了 → key 不同 → 不会复用旧结果。

2.2 对象缓存:用 (node_id, class_type)

节点对象的键由 CacheKeySetID 构造(comfy_execution/caching.py:66-80),逻辑简单:

  • key = (node_id, class_type)

读取时:

obj = caches.objects.get(unique_id)
if obj is None:
    obj = class_def()
    caches.objects.set(unique_id, obj)

对象缓存存在的目的只有一个:同一个 workflow 执行过程中,不要重复 new 节点实例。


3. 缓存容器:Basic / Hierarchical / LRU / RAM / Null

有了 key,还需要一个合理的「容器」和「驱逐策略」。

主要类在 comfy_execution/caching.py

  • BasicCache:基础容器,提供 set_prompt / clean_unused / get / set 等;
  • HierarchicalCache:按“父节点 → 子图”构建层级缓存;
  • LRUCache:在 Basic + Hierarchical 的基础上增加代际 LRU;
  • RAMPressureCache:在 LRU 的基础上增加 RAM 压力驱逐;
  • NullCache:空实现(禁用缓存)。

核心接口统一在 BasicCache

def clean_unused(self):
    self._clean_cache()
    self._clean_subcaches()

层级相关逻辑在 HierarchicalCache,用来支持“子图单独分区缓存”。

3.1 层级缓存:怎么定位到某个节点的分区

HierarchicalCache 通过 parent 链定位子缓存,代码在 comfy_execution/caching.py:242-269

def _get_cache_for(self, node_id):
    parent_id = self.dynprompt.get_parent_node_id(node_id)
    ...
    for parent_id in reversed(hierarchy):
        cache = cache._get_subcache(parent_id)
        if cache is Nonereturn None
    return cache

def get(self, node_id):
    cache = self._get_cache_for(node_id)
    if cache is Nonereturn None
    return cache._get_immediate(node_id)

def set(self, node_id, value):
    cache = self._get_cache_for(node_id)
    assert cache is not None
    cache._set_immediate(node_id, value)

含义很直接:

  • 根节点存在当前 cache;
  • 某些节点生成子图,会在该节点下挂一个子 cache;
  • 读写时,先通过 parent 链找到对应的子 cache 再读写。

clean_unused() 里除了清除不用的 key,还会删除没用到的子 cache 分区(_clean_subcaches())。

4. 执行循环中的使用路径

缓存不是“挂在那就完事了”,它在执行循环中有比较明确的调用点。

4.1 设置 prompt + 清理

启动一次执行时(execution.py:681-685):

is_changed_cache = IsChangedCache(prompt_id, dynamic_prompt, self.caches.outputs)
for cache in self.caches.all:
    await cache.set_prompt(dynamic_prompt, prompt.keys(), is_changed_cache)
    cache.clean_unused()

这里做了三件事:

  1. 给当前 prompt 生成一个 IsChangedCache,为 key 计算提供 is_changed 结果;
  2. 对 outputs / objects 各自执行一次 set_prompt(不同策略实现不同);
  3. 紧接着执行 clean_unused(),做一次基于“当前 prompt 键集合”的清理。

4.2 节点执行前后:cache 命中与写入

在节点执行路径中:

  • 执行前:优先尝试从 outputs 命中(execution.py:686-701);
  • 执行后:将 (ui, outputs) 作为 CacheEntry 写入 outputsexecution.py:568-571)。

为了简化,这里只看抽象行为:

  • 命中 → 跳过计算,直接拿值;
  • 未命中 → 正常跑一遍,将结果塞回缓存。

4.3 每个节点之后的 RAM 轮询

如果是 RAM_PRESSURE 模式,执行完每个节点都会触发一次内存检查(execution.py:720):

self.caches.outputs.poll(ram_headroom=self.cache_args["ram"])

只有 RAMPressureCache 实现了 poll,其他模式下这个调用等同空操作。


5. CLASSIC:默认层级缓存

先看默认策略:CLASSIC。

5.1 初始化与结构

CacheSet.init_classic_cacheexecution.py:97-126):

class CacheType(Enum):
    CLASSIC = 0
    LRU = 1
    NONE = 2
    RAM_PRESSURE = 3

class CacheSet:
    def init_classic_cache(self):
        self.outputs = HierarchicalCache(CacheKeySetInputSignature)
        self.objects = HierarchicalCache(CacheKeySetID)

可以看到:

  • outputs 用层级缓存 + 输入签名做 key;
  • objects 也用层级缓存 + (node_id, class_type) 做 key;
  • 不涉及 LRU 或 RAM 驱逐。

5.2 CLASSIC 的过期机制:完全由「当前 prompt」驱动

CLASSIC 模式不做容量和时间管理,它只有两种“失效”方式:

  1. 提示切换 / 执行前清理

clean_unused() 的核心逻辑(comfy_execution/caching.py:172-195):

def clean_unused(self):
    self._clean_cache()
    self._clean_subcaches()
  • _clean_cache():把不在“当前 prompt 键集合”的项删掉;
  • _clean_subcaches():把不再需要的子缓存分区删掉。

execution.py:681-685 每次绑定新 prompt 时,都会执行这一步。结果是:

  • 换了一个新的 workflow / prompt,旧 workflow 的 outputs / objects 都会被视为“未使用”,被清理掉;
  • CLASSIC 不会跨不同 prompt 保留旧 workflow 的缓存。
  1. 键不命中(指纹失效)

is_changed 的计算在 execution.py:48-89,当节点输入更新时,指纹会变化;CacheKeySetInputSignature 在构造键时会把这个指纹带进去(comfy_execution/caching.py:115-127)。因此只要:

  • 参数 / 输入 / 上游节点的任一变化 → key 改变 → 旧值自然不命中。

5.3 CLASSIC 明确不会做的事

在 CLASSIC 下:

  • 不做 LRU 容量控制:没有 max_size,也没有代际淘汰逻辑;
  • 不做 RAM 压力驱逐:poll() 是空的,执行循环里即使调用了也什么都不干;
  • 不做 TTL:不看时间,只看 prompt 键集合。

对应的注释已经在参考内容中点得很清楚,这里就不重复堆代码了。

5.4 在频繁切换 workflow / 模型时的表现

结合上面的机制,总结一下 CLASSIC 在多 workflow 场景下的行为:

  • 执行新工作流时:
    set_prompt + clean_unused 会直接把“不在新 prompt 键集合里”的缓存项(包括对象子缓存)全部清掉;
  • 模型 / LoRA 变化:
    即便节点 ID 不变,输入签名和 is_changed 指纹不同,也会生成新键;旧条目先不命中,随后在下一次 prompt 绑定时被清空;
  • 回切旧 workflow:
    因为在上一次切换时已经把旧 workflow 相关缓存清干净了,所以基本等于重新计算。

适用场景

  • workflow 比较固定;
  • 主要想在“一次执行当中”复用中间结果,不在意跨 prompt 的持久化。

6. LRU:代际 LRU 控制 outputs 尺寸

第二种策略是 LRU,主要解决的问题是:在允许跨 prompt 复用输出的前提下,限制缓存总量,避免无限膨胀

6.1 初始化:只作用于 outputs

CacheSet.init_lru_cacheexecution.py:127-135):

def init_lru_cache(self, cache_size):
    self.outputs = LRUCache(CacheKeySetInputSignature, max_size=cache_size)
    self.objects = HierarchicalCache(CacheKeySetID)

注意几点:

  • LRU 只作用于 outputs
  • objects 仍然用 HierarchicalCache,不受 LRU 驱逐;
  • 启动方式:--cache-lru N,且 N > 0

6.2 LRU 的代际设计

LRUCache 的骨架逻辑在 comfy_execution/caching.py:299-337

class LRUCache(BasicCache):
    def __init__(self, key_class, max_size=100):
        self.max_size = max_size
        self.min_generation = 0
        self.generation = 0
        self.used_generation = {}
        self.children = {}

    async def set_prompt(...):
        self.generation += 1
        for node_id in node_ids:
            self._mark_used(node_id)

    def get(self, node_id):
        self._mark_used(node_id)
        return self._get_immediate(node_id)

    def _mark_used(self, node_id):
        cache_key = self.cache_key_set.get_data_key(node_id)
        if cache_key is not None:
            self.used_generation[cache_key] = self.generation

含义:

  • 有一个全局代数 generation,每次绑定新 prompt generation += 1
  • 每次:

    • 绑定 prompt 时,会把该 prompt 内所有节点标记为“在当前代被使用”;
    • get / set 时更新条目的 used_generation[key] 为当前代。

6.3 容量驱逐:按「最老代」逐步清理

clean_unused() 的一部分逻辑在 comfy_execution/caching.py:314-323

def clean_unused(self):
    while len(self.cache) > self.max_size and self.min_generation < self.
    generation:
        self.min_generation += 1
        to_remove = [key for key in self.cache if self.used_generation[key] < self.
        min_generation]
        for key in to_remove:
            del self.cache[key]
            del self.used_generation[key]
            if key in self.children:
                del self.children[key]
    self._clean_subcaches()

简单归纳一下:

  • 只要 len(cache) > max_size,就逐步提升 min_generation
  • 每提升一代,就删除“最近使用代 < min_generation”的条目;
  • 同步清除掉和这些 key 绑定的子缓存引用;
  • 清理完再执行 _clean_subcaches() 做层级清扫。

6.4 子图分区与代际配合

子缓存创建时会显式标记父节点和子节点“被使用”,避免刚刚生成的子图被误删。代码在 comfy_execution/caching.py:338-349

async def ensure_subcache_for(self, node_id, children_ids):
    await super()._ensure_subcache(node_id, children_ids)
    await 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] = []
    for child_id in children_ids:
        self._mark_used(child_id)
        self.children[cache_key].append(self.cache_key_set.get_data_key(child_id))
    return self

配合前面提到的层级结构,就形成了“按 workflow 子图分区 + LRU 按代际清理”的整体行为。

6.5 触发时机和行为总结

在 LRU 模式下:

  • 每次绑定 prompt:
    generation += 1,标记当前 prompt 的节点使用代为当前代;
  • 每次 get/set
    更新条目的 used_generation 为当前代;
  • 每次 clean_unused()

    • len(cache) > max_size 时,通过提升 min_generation 清除旧代条目;
    • 额外清理无用子缓存。

特点

  • 可以跨 prompt 保留一部分中间结果;
  • max_size 控制缓存上限;
  • 没有 RAM 压力感知:poll() 依然不做事。

适用场景

  • 希望在多 workflow 之间部分复用缓存;
  • 但机器内存有限,需要给 outputs 一个明确的容量上限;
  • 对 RAM 细粒度控制没有强需求,或使用的是物理机 / 内存足够的环境。

7. RAM_PRESSURE:按可用内存压力驱逐

第三种策略是 RAM_PRESSURE,对应类是 RAMPressureCache。它继承自 LRUCache,但不按 max_size 做驱逐,而是:

  • 通过 poll(ram_headroom),在可用内存不足时按“OOM 评分”驱逐条目。

7.1 初始化:objects 仍然是层级缓存

CacheSet.init_ram_cacheexecution.py:131-133):

def init_ram_cache(self, min_headroom):
    self.outputs = RAMPressureCache(CacheKeySetInputSignature)
    self.objects = HierarchicalCache(CacheKeySetID)

注意两个点:

  • RAM 模式下,只有 outputs 会按 RAM 压力驱逐
  • objects 不参与 RAM 驱逐,逻辑完全和 CLASSIC/LRU 下相同。

7.2 poll:可用 RAM 检测 + OOM 评分驱逐

poll 的主逻辑在 comfy_execution/caching.py:384-454

def poll(self, ram_headroom):
    def _ram_gb():
        # 优先 cgroup v2/v1,失败回退 psutil
        ...
    if _ram_gb() > ram_headroom: return
    gc.collect()
    if _ram_gb() > ram_headroom: return
    clean_list = []
    for key, (outputs, _), in self.cache.items():
        oom_score = RAM_CACHE_OLD_WORKFLOW_OOM_MULTIPLIER ** (self.generation -
        self.used_generation[key])
        ram_usage = RAM_CACHE_DEFAULT_RAM_USAGE
        def scan_list_for_ram_usage(outputs):
            nonlocal ram_usage
            ...
        scan_list_for_ram_usage(outputs)
        oom_score *= ram_usage
        bisect.insort(clean_list, (oom_score, self.timestamps[key], key))
    while _ram_gb() < ram_headroom * RAM_CACHE_HYSTERESIS and clean_list:
        _, _, key = clean_list.pop()
        del self.cache[key]
        gc.collect()

流程拆一下:

  1. 获取可用 RAM

    _ram_gb() 的实现优先读取 cgroup 的限制:

    • cgroup v2:memory.max / memory.current
    • cgroup v1:memory.limit_in_bytes / memory.usage_in_bytes
    • 都失败才回退 psutil.virtual_memory().available

    这解决了容器环境下“宿主机内存大,容器实际被限制”的常见问题。

  2. 阈值和 GC

    • 如果可用 RAM > ram_headroom,直接返回;
    • 否则先跑一次 gc.collect()
    • 再测一次 RAM,如果还是不足,进入驱逐流程。
  3. 为每个条目计算 OOM 评分

    • 初始 oom_score = RAM_CACHE_OLD_WORKFLOW_OOM_MULTIPLIER ** (generation - used_generation[key])

      • 大概意思是:越久没被用,分数指数级放大(默认倍数为 1.3,见 comfy_execution/caching.py:365);
    • 初始 ram_usage = RAM_CACHE_DEFAULT_RAM_USAGE(0.1,见 360);
    • 递归遍历 outputs 列表:

      • CPU tensor:numel * element_size * 0.5(认为 CPU 上的 tensor 价值更高,折半);
      • 自定义对象:如果实现了 get_ram_usage() 就加上它;
    • 最后 oom_score *= ram_usage,得到综合评分。

    所有条目按 (oom_score, timestamp, key) 排序,放入 clean_list

  4. 按迟滞阈值逐个删除

    • 只要 _ram_gb() < ram_headroom * RAM_CACHE_HYSTERESIS,就从 clean_list 末尾 pop 一个 key 并删除;
    • 每删一个都跑一次 gc.collect()
    • 迟滞倍数 RAM_CACHE_HYSTERESIS 默认 1.1,避免“刚删完又马上触发清理”的抖动。
  5. 访问时间戳

    访问时会更新 timestamps(comfy_execution/caching.py:376-382):

   def set(self, node_id, value):
       self.timestamps[self.cache_key_set.get_data_key(node_id)] = time.time()
       super().set(node_id, value)

   def get(self, node_id):
       self.timestamps[self.cache_key_set.get_data_key(node_id)] = time.time()
       return super().get(node_id)

在 oom_score 一样时,timestamp 起到“最近访问优先保留”的作用。

7.3 提示绑定下的行为:不清理 outputs

RAM 模式下,clean_unused() 的行为与 CLASSIC 不同(见参考说明):

  • RAM 模式:
    clean_unused() 只做子缓存分区清理,不会删掉“当前 prompt 未使用”的 outputs 条目;
  • CLASSIC 模式:
    clean_unused() 会同时删掉当前 prompt 未用到的 outputs 条目。

结果是:

  • RAM 模式可以跨多个 workflow 长时间保留中间结果;
  • 只有在 RAM 不够时才做清退。

7.4 容器环境下需要注意的点

的 AutoDL 中,用 psutil.virtual_memory().available,在容器里看到的是宿主机内存,而不是容器的限额,导致永远“不触发回收”,最后 OOM。

适用场景

  • 多 workflow / 多模型 / 多 LoRA 同时存在,且希望尽可能长时间复用输出结果;
  • 机器内存有限,但更关心“不 OOM”,而不是一个固定的 max_size
  • 特别适合容器环境(K8s / AutoDL 等),配合 --cache-ram <GB>

8. 一些落地建议

最后,用一段比较直接的建议收尾。

8.1 单模型 / workflow 稳定:用 CLASSIC 即可

特点:

  • 工作流基本不换;
  • 主要希望避免一次执行中的重复计算(比如多次 preview)。

用默认 CLASSIC:

  • 结构简单;
  • 不参与复杂的跨 prompt 保留;
  • 不需要担心 LRU 尺寸和 RAM 阈值调参。

8.2 多 workflow + 控制缓存尺寸:用 LRU

场景:

  • 有多套 workflow,在它们之间来回切;
  • 机器内存不是特别大,希望 outputs 不要无限膨胀;
  • 又希望某些常用子图能被复用。

做法:

  • 启动时加 --cache-lru N(N 先给一个相对保守的值,比如几百到几千条,看内存曲线再调);
  • LRUCache 用代际 + max_size 帮你自动做“近期常用保留、早期冷门清理”。

8.3 多模型 / 多 LoRA / 容器环境:优先 RAM_PRESSURE

场景:

  • 容器 / 云平台(K8s、AutoDL 等);
  • 有较多模型、LoRA 和 workflow 混用;
  • 内存被容器限制,容易因爆 RAM 掉进 OOM。

做法:

  • 启动时用 --cache-ram <GB> 配一个“希望保留的 RAM 余量”;

    • 比如容器给了 32GB,就设在 16–24GB 看情况;
  • RAMPressureCache

    • 最大程度保留各个 workflow 的中间结果(包括应用 LoRA 后的模型对象等);
    • 在内存不足时,根据 OOM 评分优先清旧代、大内存条目。

注意一点:即使有容器优化,如果平台本身的 cgroup 挂得不标准,_ram_gb() 的结果还是有可能偏离实际,这一点要结合平台文档确认。

8.4 完全不想折腾:直接关缓存

如果你:

  • 环境受限;
  • 或者调试阶段不想被缓存影响行为理解;

可以直接用:

  • --cache-none
    对应 CacheType.NONECacheSet.init_null_cache()NullCache,所有 get/set 都是 no-op。

代价就是:每次执行都完全重新算一遍。

9. 小结一下

把上面的内容压缩成几句话:

  • ComfyUI 有两路缓存:

    • outputs 存中间结果,真正用来省算力;
    • objects 存节点实例,只减少 Python 对象构造开销。
  • 键体系:

    • 输出:输入签名 + is_changed 指纹(+ 条件下的 node_id)
    • 对象:**(node_id, class_type)**。
  • 四种策略:

    • CLASSIC:默认层级缓存,按当前 prompt 键集合清理,不做 LRU / RAM 驱逐;
    • LRU:只对 outputs 做代际 LRU,配合 max_size 控制容量;
    • RAM_PRESSURE:在 LRU 基础上加 RAM 压力驱逐,在内存不足时按 OOM 评分清理;
    • NONE:彻底关掉缓存。
  • 在多模型 / 多 workflow / 多 LoRA 场景下:

    • 对象缓存 objects 始终是层级缓存,不参与 LRU / RAM 驱逐,在每次绑定 prompt 时按键集合清理;
    • 模型权重 / LoRA 的真实驻留由模型管理层控制;
    • 真正要关心的是:如何选择 outputs 的策略,让中间结果既能有效复用,又不会把内存打爆。

如果我们在一个复杂的工作流环境里跑 ComfyUI,建议先搞清楚自己处于哪种场景,再结合上面的策略选项,把缓存调成我们能控制的状态,而不是让它在后台「自动长草」。

以上。

关于行业 Agent 的思考:「行业 Workflow + Agent」的混合模式

过去一年,AI Agent 从狂热逐渐回归理性。在企业级应用和垂直行业落地中,我们看到了一个趋势:在行业中,纯粹依靠 Agent 自主决策的构想,正在被「Workflow + Agent」的混合模式所取代。

对于我们一线的同学来说,最重要的是要去解决实际问题。

当前我们能看到的行业 Agent 大多数实际落地的逻辑是:行业 Agent 的壁垒在于行业 Know-how,而落地的最佳路径是利用 Agent 做交互与分发,利用 Workflow 做执行与兜底。

1. 行业 Agent 是什么

很多人把 Agent 想象成一个全能的「超级员工」,指望给它一个模糊的目标(比如“帮我提升下季度销售额”),它就能自动拆解任务、调用工具、完成工作。在通用领域或简单场景下(如订机票、写周报),这或许可行。但在垂直行业(金融、制造、医疗、物流等),这种纯 Agent 模式目前是行不通的。

1.1 Agent 是交互方式,不是业务本身

Agent 在行业应用中的本质,是入口交互

它改变了人与系统的互动方式。以前我们需要点击菜单、填写表单、通过 SQL 查询数据库;现在我们可以通过自然语言表达意图。Agent 的核心价值在于它能“听懂”用户的意图,并将其转化为系统能理解的指令。

1.2. 真正的壁垒是行业 Know-how

大模型本身是通用的。GPT-5 或者是 Claude 4.5,它们具备的是通用的逻辑推理能力和语言能力,但它们不懂你们公司的复杂的审批流程,不懂某个特定设备的维修手册,也不懂行业内潜规则式的业务逻辑。

行业 Agent 的「行业」二字,才是重点。

  • 什么是 Know-how? 是我们沉淀了十年的 SOP,是数据库里积累的边缘案例,是针对特定业务场景的异常处理机制。
  • Agent 的角色: 它是这些 Know-how 的「调度员」,而不是「创造者」。

如果脱离了行业 Know-how,Agent 就是一个会说话但办不成事的空壳。

2. 为什么「纯 Agent」模式在企业端走不通?

在 Demo 阶段,我们经常看到这样的演示:用户说一句话,Agent 自动规划了五个步骤,调用了三个 API,完美解决了问题。

但在生产环境中,这种全自动的「纯 Agent」模式面临三个无法回避的死结:

2.1 幻觉与确定性的冲突

企业级应用,尤其是涉及到资金、生产安全、合规的场景,稳定压倒一切。 大模型的本质是概率预测,这意味着它永远存在「幻觉」的可能性。哪怕准确率做到 99%,那剩下的 1% 的不可控对于企业核心流程来说也是灾难。

你无法向审计部门解释,为什么系统批准了一笔违规报销,仅仅因为 Agent 觉得「这看起来没问题」。

2.2 流程的黑盒化

纯 Agent 模式下,决策过程往往隐藏在模型的推理链中。当出现问题时,很难复盘和追责。企业需要的是可审计、可监控、可干预的流程。

2.3 成本与延迟

让大模型去规划每一个微小的步骤(比如“点击确认按钮”、“校验手机号格式”),是对算力的巨大浪费。这些确定性的逻辑,用传统的代码实现既快又准,用 LLM 去推理则是大炮打蚊子,且增加了响应延迟。

3. Workflow + Agent 的混合模式

既然大模型的幻觉无法根除,而传统软件的确定性又是刚需,最务实的方案就是将两者结合:Workflow + Agent

这是一个“动静结合”的架构。

  • Workflow(工作流/RPA): 负责“静”。它是骨架,是肌肉。它包含固定的业务逻辑、SOP、API 调用序列。它保证了核心流程的确定性可靠性
  • Agent(大模型): 负责“动”。它是大脑,是神经。它负责理解非结构化的输入(自然语言),进行意图识别,然后决策应该触发哪一条 Workflow。

3.1 核心逻辑

Agent 不直接去操作底层数据库或核心系统,Agent 的输出对象是 Workflow。

  • 用户 -> 对话 -> Agent (理解意图/参数提取) -> 触发 -> Workflow (执行/校验) -> 返回结果 -> Agent (格式化输出) -> 用户

3.2 这种模式解决了什么问题?

  1. 复用历史沉淀: 企业过去十年建设的 ERP、CRM、以及各种自动化脚本(RPA),不需要推倒重来。它们被封装成一个个 Workflow,成为 Agent 的「工具箱」。
  2. 控制风险: 所有的执行动作(写库、转账、发货)都由 Workflow 控制,Workflow 内部包含严格的校验逻辑(If-Else),这是大模型无法绕过的硬规则。
  3. 降低成本: 只有在需要理解和决策的环节才消耗 Token,大量的执行环节由低成本的代码完成。

4. 如何设计混合模式

在具体落地时,我们需要构建一个分层的架构体系。

4.1 意图理解与分发

这是系统的入口。用户输入的往往是模糊的、非结构化的自然语言。 这一层的核心任务不是「解决问题」,而是「定义问题」。

  • 意图识别: 判断用户是想「查询库存」、「发起退款」还是「投诉建议」。
  • 参数提取: 从对话中提取执行 Workflow 所需的关键参数(如订单号、日期、金额)。如果参数缺失,Agent 需要反问用户进行补全。
  • 路由分发: 基于意图,将任务指派给具体的 Workflow 或下一级更专业的 Agent。

关键点: 这一层需要极强的语义理解能力,通常需要配合 RAG 来理解特定领域的术语。

4.2 动态决策与 RAG

在某些复杂场景下,直接映射到 Workflow 是不够的。 比如用户问:“我的设备报警代码是 E03,我该怎么办?”

这里不能直接触发一个“维修流程”,因为 Agent 首先需要知道 E03 代表什么。

  • RAG 的介入: Agent 调用知识库,检索 E03 对应的故障原因和处理手册。
  • 初步决策: 基于检索到的 Know-how,Agent 判断是建议用户重启(触发“重启指引 Workflow”),还是必须派人维修(触发“工单提交 Workflow”)。

关键点: RAG 在这里不仅仅是用来回答问题的,更是用来辅助 Agent 做路由决策的。

4.3 确定性执行(Workflow / RPA)

这是系统的执行层,也是“行业 Know-how”固化最深的地方。 这一层严禁幻觉

  • 形式: 它可以是一个 API 接口,一个 Python 脚本,或者是一个复杂的 BPM(业务流程管理)实例,甚至是一个 RPA 机器人。
  • 逻辑: 这里面充满了 If-ElseTry-Catch 和数据库事务。
  • 反馈: Workflow 执行完毕后,必须返回明确的状态码和结果数据(JSON 格式),而不是一段模糊的文本。

4.4 结果综合与反馈

Workflow 返回的是结构化数据(例如:{"status": "success", "order_id": "12345", "delivery_date": "2023-12-01"})。 Agent 的最后一步工作,是将这些冷冰冰的数据,转化为符合人类阅读习惯的自然语言,反馈给用户。

5. 多级 Agent 与 RAG 的协同

在简单的场景下,一个 Agent 配合几个 Workflow 就够了。但在复杂的行业场景(如供应链管理、大型设备运维)中,我们需要更复杂的拓扑结构。

5.1 多级 Agent 架构

不要试图训练一个全知全能的上帝 Agent。应该采用“主帅-将军-士兵”的层级结构。

  • L1 调度 Agent(主帅): 只负责宏观分类。例如,判断是“售前咨询”还是“售后维修”。
  • L2 领域 Agent(将军): 专注于特定领域。例如,“售后 Agent” 拥有查询保修、解读故障码、预约工程师的能力。
  • L3 执行单元(士兵): 具体的 Workflow 或特定的单一功能 Agent。

这种结构的好处是解耦。当售后流程发生变化时,只需要调整 L2 Agent 和对应的 Workflow,不会影响到售前部分。

5.2 RAG 的逻辑化应用

传统的 RAG 主要是为了解决“回答知识性问题”。在混合模式中,RAG 的作用被放大了。

  • 动态 Prompt 注入: 在执行 Workflow 之前,系统可以根据当前的上下文,利用 RAG 从知识库中检索出相关的规则或注意事项,动态注入到 Agent 的 Prompt 中。

    • 例子: 在处理一笔“退款”请求时,RAG 检索到“该用户是 VIP 且信用极好”,将此信息注入 Prompt,Agent 可能会选择触发“极速退款 Workflow”而不是“常规审核 Workflow”。

6. 落地实战中的思考

在实施“行业 Workflow + Agent”模式时,有几个非技术性的坑需要注意。

6.1 人机协同

在很长一段时间内,Agent 不会完全取代人,而是成为人的 Copilot 在设计 Workflow 时,必须预留人工介入的节点。 当 Agent 的置信度低于某个阈值,或者 Workflow 执行遇到异常时,系统应自动升级为人工服务,并将之前的上下文完整传递给人工客服。

6.2 存量资产的价值

很多技术团队在做 AI 转型时,倾向于重构一切。这是错误的。 你们公司遗留的那些看起来陈旧的 API、跑了五年的定时脚本、甚至 Excel 里的宏,都是宝贵的资产。 Agent 的落地应当是「局部改造」而非「推倒重来」。 我们要做的,是给这些老旧的系统加上一个 AI 适配层,让 Agent 能够调用它们,而不是替换它们。

6.3 结构化数据的回流

Agent 与用户的交互过程,产生了大量高质量的数据。 不要让这些数据只停留在对话日志里。需要设计机制,将 Agent 收集到的信息(如用户的新需求、报错的高频词、Workflow 的执行结果)结构化地回流到业务系统中,用于优化 SOP 和微调模型。

7. 小结

行业 Agent 的未来,不是科幻电影里的全自动机器人,而是严谨的工程化实践

我们不需要一个会写诗的 AI,我们需要的是一个能准确理解工单意图,并由后台的 Workflow 准确执行的系统。

  • Agent 是面子:提供极简的交互,理解复杂的意图。
  • Workflow 是里子:承载行业壁垒,保证执行的绝对可靠。
  • RAG 是底子:提供动态的上下文和知识支撑。

降本增效不是靠引入一个昂贵的大模型来实现的,而是靠大模型把过去那些难以被自动化的“非结构化需求”,转化为了可以被低成本代码执行的“结构化指令”。

这才是行业 Agent 的落地。