我手上攒了一批本地文档:内部手册、项目笔记,还有几十个没人愿意读的Markdown。同事们天天在群里问同一个问题,“那个审批流程在哪一篇里写的”。我烦了,想搞一个能直接回答这些问题的机器人。选开源项目时挑了半天,最后锁定Dify。理由不复杂:社区版免费,自带可视化编排界面,不用写前端,发布后还能直接生成一个Web链接。这个教程适合想搭私有知识库问答但一直没跑通的人。跟着做完,你能得到一个能回答文档内容的公网链接,也能搞清楚RAG里哪些参数真正影响回答质量。

先把服务跑起来

我的环境是一台16G内存的Linux服务器,之前没装过Docker。安装Docker和Compose插件的过程不展开,网上一搜一大把。之后拉Dify仓库,进docker目录,复制环境变量,启动。

git clone https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
docker compose up -d

第一次启动要拉十几个镜像,耗时完全看网速。我在“Waiting”状态卡了十几分钟,后来才发现是默认镜像源在国内太慢。给Docker配上国内镜像加速器,再重新拉一遍,很快就起来了。如果你看到某个镜像一直拉不下来,别干等,先去检查加速器。

端口是我踩的第一个坑。Dify默认监听80,但我那台服务器上已经跑了Nginx。改环境变量里的端口配置,再启动。

EXPOSE_NGINX_PORT=8080
EXPOSE_NGINX_HTTPS_PORT=8443

改完再执行一遍docker compose up -d,浏览器打开http://IP:8080,能看到初始化页,设置管理员账号和密码。界面很干净,左边是应用列表,中间是一块空画布。但先别急着创建应用,还有一件更重要的事要做。

接一个模型,别用默认的

Dify本身不包含任何大模型,它只负责编排和调度。你需要在“设置”里接入一家模型API。我手里有OpenRouter的Key,就在模型供应商里选了OpenRouter,填上Key,它会自动拉取可用模型列表。免费模型看着很多,实际用起来有两个坑:每分钟请求次数限制很死;上下文长度标得很大,但多轮对话一多,照样被截断。

我最后选了一个免费的、上下文特别长的模型先用着。后来发现长上下文不等于准,它只是能塞更多东西,回答质量还是得看模型本身。没有OpenRouter账号的话,也可以用国内几家服务商,填Key的入口大同小异,但有个细节容易出错:Base URL必须带/v1结尾,少一个斜杠,保存时就会提示地址无效。我为了这个斜杠来回折腾了十几分钟,最后对着官方文档才反应过来。

建知识库,分段参数真的会要命

模型配好,开始传资料。点“知识库”,创建,名字随意。上传格式支持Markdown、PDF、TXT、DOCX,我传了十几篇Markdown笔记。上传完,Dify会自动做清洗、分段和向量化。这个阶段最大的坑,就是分段参数。

Dify默认按空行和换行切分,块大小和重叠长度都有默认值。我以前觉得这东西无所谓,后来发现它直接决定检索质量。

举个例子。我的文档里有一节讲“固定资产采购审批”,中间是一个表格。默认分段把表格从中间切开了,检索返回的片段里只有表头,没有“金额超过五万需要总经理审批”这一行。我问它超过五万怎么办,它回答不出来,因为喂给模型的上下文里压根没有那一行。

我试了两组配置做对比:

配置响应速度召回效果Token消耗
小块大小200+重叠20短文档够用,长文档靠后内容经常漏
块大小500+重叠50稍慢长文档内容完整不少明显多

这两组数据是我在自己笔记上跑出来的,换个文档不一定适用。但方向是稳的:文档结构越复杂,越需要用大分段和重叠去保留语义。调完参数记得点“重新处理”,否则测试时用的还是旧索引。我一开始就忘了这一步,改了参数但回答没变,还以为Dify坏了。

画布编排,提示词才是灵魂

创建应用时选“聊天助手”。打开画布,左侧有一排节点:开始、大模型、知识库检索、问题分类等等。我需要的流程很简单:用户问句进来,先检索知识库,把结果拼给大模型,大模型按提示词回答。

把“知识库检索”节点拖到画布上,连到“大模型”节点。然后写提示词:

你是内部资料问答助手。
只依据上下文内容回答。
上下文没有的内容,直接回答“资料里没写”。
禁止编造。

这段提示词是灵魂。最开始我没写,问了一个文档里不存在的问题,模型自己脑补了一个完整流程,看着还挺像那么回事。加上这几句之后,它才肯承认不知道。在RAG场景下,提示词不约束,幻觉马上就来,别指望模型自己变老实。

大模型节点里有个“记忆”开关,默认是打开的。多轮对话时,Dify会把历史记录一起塞进上下文。这功能看着方便,但我的知识库问答里,历史对话只会干扰后续问题。第二个问题问“那超过八万呢”,模型拿着上一轮的检索结果去猜,答案经常跑偏。解决办法是在记忆配置里把轮数设为1,或者直接关掉。关了之后,每个问题都独立检索,准确率反而更高。

调试时先看检索到了什么

别急着发布,先用画布右侧的调试框试几轮。我输入“新的请假流程在哪”,然后打开“上下文”面板,看它到底检索了哪些文档片段。这个习惯帮我解决了一大半问题。

有一次回答特别啰嗦,打开上下文一看,模型拿到了五六个无关的文档片段,还把它们全用上了。我把检索数量从5改成2,再把召回分数阈值调高一点,回答立刻干净。阈值这个参数没有统一标准,不同向量模型算出来的分数范围不一样,可以先用0.4附近,再看检索结果中的实际分数分布,往高调或往低调。

另外要留心对话测试时的缓存。同一个问题在调试框里问第二次,Dify可能直接用上一次的答案。要么清空会话,要么换个问法,免得重复测试时被缓存误导。

发布之后,麻烦才刚开始

一切调好,点右上角“发布”,选“Web App”,Dify会生成一个公网链接。把链接发到群里,同事就能直接在浏览器里聊。我顺手改了应用外观,换了个标题和头像。自定义域名没配,服务器没备案,在公网上用自定义域名会碰到麻烦,干脆用Dify给的固定地址。

但发布后我立刻发现了几个新问题。

第一个是限流。我用的免费模型,群里五个人同时问了大概十分钟,请求就开始不响应。日志里能看到429状态码,但Dify界面只显示“供应商调用失败”,不直接说被限流。排查方法是看模型供应商的调用记录,或者在服务端日志里搜状态码。临时解法是在应用设置里加缓存,再把超时时间放宽。长期来看,还是换一个稳定的付费API更省心。

第二个是知识库更新。某天我改了一篇文档里的错误描述,重新上传覆盖,结果问出来的答案还是旧的。原因前面提过,Dify不会自动监控文件变化。文件覆盖之后,必须去知识库文档详情页,勾选该文档,点“重新处理”,它才会重新分段和向量化。只要忘了这一步,线上应用就一直用旧数据回答,特别坑。

第三个是引用来源。纯RAG模式下,模型并不知道自己用的片段来自哪个文件。你问它“哪个文档里写的”,它基本靠猜。想在回答里带上文档名,可以在提示词里加一句“回答时注明出处”。但效果不稳定,因为分段后的元数据不一定保留完整。真要精确到页码和标题,得改文档结构,做更细的元数据管理,那是进阶玩法。

安全设置别偷懒

生成公网链接后,默认是所有人都能访问的。如果你传的是内部资料,这个风险有点大。Dify在Web App设置里可以加访问凭据,打开后必须输入密码才能进入对话。一开始我嫌麻烦没开,后来被提醒,才发现链接已经发给过几个人,回收渠道都没有。赶紧补上密码,又清掉了旧的会话记录。内部工具,权限还是早点管起来比较好。

还有一个容易被忽略的点是升级。Dify迭代速度很快,我跑了一周,界面上就提示有新版本。升级前一定要备份数据目录,尤其是PostgreSQL和Redis的卷,否则应用配置、知识库索引都可能丢。我那次升级没做备份,差点把一个建好的知识库弄没,最后靠容器残留的数据才恢复。从那以后我养成了习惯,升级前先停服务,把docker volumes里的东西打包存一份。

要不要开Agent

说句实话,Agent在工作流里确实好看,能接搜索、能调API、能自己拆任务。但对我的内部文档问答场景,Agent一点忙都帮不上。它会让每次回答多出好几轮模型调用,延迟明显变大,还可能自己调了个无关工具,把回答带偏。我给自己的建议是:纯信息检索就用普通RAG,操作型任务比如“帮我建个工单”再考虑Agent。别一上来就开,不然你会在调试里浪费时间。

最后整理一下我的最终配置:文档分段用中等偏大的块大小,重叠长度50左右;模型换成