← 返回文章列表

Dify 企业级实验(05):Token 成本控制——AI 应用省钱改造怎么做?

1. 业务场景

先讲一个我们实际遇到的场景。

AI 应用上线后,老板每个月都要看一张账单:客服问答工作流每月 5 万次调用,单次平均消耗 2.8 万 Token——Token 是持续支出,不是一次性开发成本,调用量越大,账单越吓人。

我们第一次接这类需求时,第一反应也是「省钱不就是压缩 prompt 字数嘛」。真正动手才发现——prompt 省那 1000 Token,杯水车薪;真正烧钱的是「用错了工具」:规则能算的用 LLM 生成,分类器能判的用 LLM 生成。省钱不是抠门,是把每个环节放到它该在的位置上。

同一个结果,用 LLM 生成要花钱,用规则算一分钱不花;用 LLM 生成要花钱,用分类器判只要零头。区别只在于「这个环节值不值得用 LLM」。

这不是个例。任何「LLM 调用量大、跑在真实业务上」的应用都是这个模式:能省的环节没省,钱就在看不见的地方流走。

2. 场景痛点

这个流程的痛点,在每个月看账单的老板身上体现得最直接:

本质上,成本不是「省」出来的,是「这个环节值不值得用 LLM」设计出来的

3. 方案:为什么是逐节点审计 + 结构化替代

Dify 的 code 节点、Question Classifier 与 LLM 节点并存,正好支持「先审计、再分类改造」。选它的理由:

这篇文章我们就用它把客服问答工作流做一次「省钱改造」:从 3 次 LLM 调用压到 1 次。

4. 整体架构

graph TD subgraph sub_before["【改造前】"] b_start["开始"] b_senti["情感分析(LLM)"] b_intent["意图识别(LLM)"] b_answer["回答生成(LLM)"] b_end["结束"] b_start --> b_senti --> b_intent --> b_answer --> b_end end subgraph sub_after["【改造后】"] a_start["开始"] a_senti["情感分析(关键词规则 code,0 token)"] a_qc["意图识别(问题分类器 QC)"] a_answer["回答生成(LLM 精简)"] a_end["结束"] a_start --> a_senti & a_qc a_senti --> a_answer a_qc --> a_answer a_answer --> a_end end

改造前是 3 次 LLM 串行;改造后情感分析与意图识别并行执行,结果合并进回答生成的 prompt,全程只有 1 次 LLM 调用。

链路设计的要点:先把「值不值得用 LLM」的判断做在结构上,再谈 prompt 细节

5. 模块设计

5.1 开始变量

start 变量user_message(段落,必填,max_length 10000),两个应用完全一致,方便 A/B 对比。

5.2 改造前(基线)

改造前:三个 LLM 节点串行,各自带独立 system prompt(回答生成的 prompt 很长,每轮固定消耗 1000+ Token)。

5.3 改造后:三个核心节点

改造后核心节点

情感分析改为 code 节点,关键词评分表零成本:

def main(user_message: str) -> dict:

    text = user_message or ""

    pos = ["满意", "好", "赞", "喜欢", "感谢", "棒", "不错"]

    neg = ["差", "坏", "烂", "投诉", "生气", "失望", "退款", "垃圾"]

    score = sum(1 for k in pos if k in text) - sum(1 for k in neg if k in text)

    sentiment = "positive" if score > 0 else ("negative" if score < 0 else "neutral")

    return {"sentiment": sentiment, "score": str(score)}

意图识别改为问题分类器(Question Classifier),四类意图的指令必须带定义与示例:

instruction: |

  对用户消息的意图进行分类,只能归为以下四类之一:

  - 咨询(inquiry):产品咨询、价格、功能、使用方法

  - 购买(purchase):下单、购买、优惠、活动

  - 售后(after_sale):退换货、维修、保修、发票

  - 投诉(complaint):质量差、损坏、服务态度差、表达不满

  按意图判断,无法归入前三类的归入最接近的一类。

classes: [咨询 inquiry, 购买 purchase, 售后 after_sale, 投诉 complaint]

completion_params: {max_tokens: 2000, temperature: 0.1}

回答生成 LLM 精简 prompt,把前面两个节点的结果作为上下文喂进去,不再让模型重复理解原文:

prompt_template:

  - role: system

    text: 你是电商客服。基于已知信息直接回答,不超过 100 字。

  - role: user

    text: |

      用户消息:{{#start.user_message#}}

      情感:{{#cd_senti.sentiment#}}

      意图:{{#qc_intent.class_name#}}

6. 运行验证

输入 预期 结果
同一句含「差/投诉」的客服消息 改造后情感=negative、意图=售后、回答质量不降 sentiment=negative(规则命中「差/投诉」)、intent=售后(QC 分类正确),回答质量达标
四层回归用例(确定性/内容/语义/边界) 全部 PASS,分类准确率 ≥95% 全部 PASS,分类准确率保持
改造前单次约 2.8 万 Tokens 降本 40%+ 改造后 1 次 LLM + 2 个非 LLM 节点,约 1.5 万 Tokens/次(-46%)

环境:Dify 1.16.1(Docker Compose),模型 DeepSeek deepseek-v4-flash。两个 DSL 均导入发布通过,用 Service API 冒烟验证。

7. 实战坑

现象 修复
code 节点沙箱禁写文件 在 code 里写 /tmp 报 PermissionError 所有「文件模拟」存储改本机 KV 模拟服务(docker 容器 dify104-kv,172.19.0.50:8123),生产换 Redis/DB,拓扑不变
规则替代后质量下降 边界用例答错 改造后必须跑四层用例回归,不达标就补关键词/回退 LLM(实测边界用例兜底)
分类器指令无示例词表 准确率低 指令里给每类定义+示例,temperature 压到 0.1(实测 102-03 经验)
只优化 prompt 忽略结构性浪费 省 1000 Token 也省不出 46% 先做逐节点成本审计,把规则能算的(情感)和分类器能判的(意图)从 LLM 拆出去(实测 102 批量经验)

8. 实验文档及源码获取

文章聚焦核心配置与采坑点,完整分步操作与回归用例见实验文档原文。

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过