为什么我们不无脑用 Dify / LangChain
先说结论
Dify 和 LangChain 都是很好的工具。但它们是通用工具,而我们做的是非标场景。
通用工具的设计目标是覆盖 80% 的场景。但如果你想做到 90% 甚至 95%,你就得自己动手。
这不是说 Dify/LangChain 不好,而是说它们的抽象层次和我们的场景不匹配。
什么是非标场景
标准化场景是指那些有明确流程、明确输入、明确输出的场景。比如:
- 客服机器人:用户问一个问题,系统给一个回答
- 文档问答:用户问一个文档相关的问题,系统从文档里找答案
- 内容生成:用户给一个 prompt,系统生成一段文本
这些场景,Dify 和 LangChain 都能很好地处理。因为它们的抽象层次刚好匹配这些场景的复杂度。
非标场景是指那些边界模糊、流程不确定、需求经常变的场景。 比如:
- 智能授权:用户说一句话,系统要判断他是不是要改权限,要提取出角色、权限、对象,要做二次确认,要调用 API,要返回结果。这个流程里有很多分支和异常情况。
- 智能整合:用户问一个问题,系统要从多个文档里找信息,要判断哪些信息相关,要整合在一起,要处理冲突和矛盾。这个流程里有很多判断和决策。
- 智能推文:用户要生成一段话术,系统要理解产品特点、客户画像、销售场景,要生成合适的话术,要处理不同风格和语气。这个流程里有很多创意和变通。
这些场景,用 Dify/LangChain 的标准流程来处理,会遇到很多问题。
问题一:抽象层次不匹配
Dify 和 LangChain 的抽象层次是「Chain」——把一系列步骤串起来,形成一个流程。
这个抽象对于简单场景是合适的。比如:
用户输入 → 检索文档 → 生成回答 → 输出
但对于复杂场景,这个抽象就不够用了。比如智能授权的流程:
用户输入 → 意图识别 → [是权限操作] → 参数提取 → 二次确认 → [确认] → 调用 API → [成功] → 返回结果 → 二次确认 → [确认] → 完成
↓ ↓ ↓ ↓
[不是] → 普通对话 [拒绝] → 取消 [失败] → 错误处理 [拒绝] → 回滚
这个流程里有很多分支和循环,用 Chain 来表达会非常复杂。而且,每个分支的逻辑都不一样,有的需要调用 API,有的需要和用户交互,有的需要做错误处理。
用 Dify/LangChain 的 Chain 来实现这个流程,代码会非常复杂,而且很难维护。
问题二:预设成为约束
Dify 和 LangChain 有很多预设:
- 预设了模型的调用方式(通常是 OpenAI API)
- 预设了 Prompt 的模板
- 预设了工具的接口
- 预设了流程的结构
这些预设在大多数场景下是合理的。但在非标场景下,这些预设会成为约束。
举个例子:
我们的客户用的是 DeepSeek V3,不是 OpenAI。DeepSeek V3 的工具调用能力和 OpenAI 不一样,我们需要针对 DeepSeek V3 的特点做专门的优化。
如果用 LangChain,我们要么得写一个适配层,把 DeepSeek V3 的接口转换成 OpenAI 的接口;要么得修改 LangChain 的源码,让它支持 DeepSeek V3。
这两种方式都很麻烦。写适配层会增加复杂度和延迟;修改源码会导致后续升级困难。
我们选择直接调用 DeepSeek V3 的 API,针对它的特点做优化。 这样更直接、更高效、更可控。
问题三:灵活性不足
非标场景的需求是经常变的。今天客户说要加一个功能,明天说要改一个逻辑,后天说要换一个模型。
用 Dify/LangChain,每次需求变更都要修改 Chain 的定义、修改 Prompt 的模板、修改工具的接口。这个过程很繁琐,而且容易出错。
我们选择用更灵活的方式来实现:
- 模块化设计: 把每个功能做成独立的模块,模块之间通过接口通信。需求变更时,只需要修改对应的模块,不影响其他模块。
- 配置化管理: 把可变的部分(比如 Prompt、模型参数、工具接口)做成配置,修改配置就能改变行为,不用改代码。
- 渐进式开发: 先做一个最小可用的版本,然后根据反馈逐步迭代。每次迭代只改一小部分,风险可控。
这种方式更灵活,更适合非标场景。
问题四:调试困难
Dify 和 LangChain 的抽象层次很高,这意味着当你遇到问题时,很难定位问题出在哪里。
比如,用户说「系统给的回答不对」。这个问题可能出在:
- 检索阶段:检索到的文档不相关
- 生成阶段:LLM 的理解有误
- 工具阶段:工具的返回值有问题
- 流程阶段:流程的逻辑有 bug
用 Dify/LangChain,你要一层一层地排查,才能找到问题的根源。而且,很多中间过程是不可见的,你只能看到最终输出。
我们选择更透明的方式来实现:
- 日志记录: 每个步骤都记录详细的日志,包括输入、输出、耗时、错误等。
- 可视化追踪: 把流程的执行过程可视化,方便定位问题。
- 单元测试: 每个模块都有独立的单元测试,确保模块本身是正确的。
这种方式更容易调试,更适合复杂场景。
什么时候用 Dify/LangChain
说了这么多,是不是 Dify/LangChain 就没用了?
当然不是。
Dify/LangChain 适合以下场景:
- 标准化场景: 客服、问答、内容生成等有明确流程的场景
- 快速原型: 需要快速验证一个想法,不需要太高的完成度
- 团队能力有限: 没有足够的技术能力自己实现,需要借助框架的能力
- 预算有限: 没有足够的预算定制开发,需要使用开源方案
如果你的场景符合以上条件,用 Dify/LangChain 是一个很好的选择。
但如果你的场景是非标的、复杂的、需要高度定制的,那你就得自己动手了。
我们的做法
我们的做法是:用 Dify/LangChain 的思想,但不用它们的代码。
具体来说:
- 借鉴思想: 学习 Dify/LangChain 的设计理念,比如 Chain 的抽象、工具的封装、Prompt 的管理等。
- 使用组件: 使用 Dify/LangChain 的某些组件,比如向量数据库、Embedding 模型、解析器等。
- 自己实现: 核心逻辑自己实现,不依赖框架。
这样做的好处是:
- 有框架的灵活性,没有框架的约束
- 有组件的复用性,没有组件的限制
- 有思想的指导性,没有思想的教条
一个例子
我们做智能客服的时候,一开始用的是 RAGFlow。RAGFlow 是一个很好的 RAG 框架,开箱即用,效果也不错。
但用了一段时间后,我们发现一些问题:
- 检索不够精准: RAGFlow 的检索策略是固定的,不能根据我们的场景做定制。
- 生成不够灵活: RAGFlow 的生成策略也是固定的,不能根据我们的需求做调整。
- 扩展不够方便: RAGFlow 的架构是封闭的,不方便添加新的功能。
所以我们决定自己实现一套 RAG 系统。
我们借鉴了 RAGFlow 的思想,但重新设计了架构:
- 检索层: 支持多种检索策略,包括语义检索、关键词检索、混合检索等。可以根据场景选择最合适的策略。
- 生成层: 支持多种生成策略,包括直接生成、引用生成、多轮对话等。可以根据需求选择最合适的策略。
- 扩展层: 支持添加新的功能模块,比如知识图谱、多模态处理等。可以根据发展逐步扩展。
效果: 准确率从 75% 提升到 92%,响应时间从 3 秒降低到 1.5 秒。
写在最后
工具只是手段,不是目的。
我们做 AI 落地,不是为了用某个框架,而是为了解决某个问题。框架只是工具,问题是目的。
当工具能解决问题时,我们用工具。当工具解决不了问题时,我们自己造工具。
这不是技术洁癖,这是务实。
如果你有一个非标场景想找人做,欢迎来找我们聊聊。我们不一定用 Dify/LangChain,但我们一定会用最合适的方式来解决你的问题。