ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Dify+Oracle+MCP打造企业级RAG智能问答应用实战

Dify+Oracle+MCP打造企业级RAG智能问答应用实战 说实话做 RAG 应用最让我头疼的往往不是模型本身而是“业务数据怎么跟大模型打通”。最近一个项目里客户的数据全在 Oracle 老库里十几年的表结构、各种存储过程、敏感字段还不少。我最后拼出来的方案是Dify 负责应用编排和知识库Oracle 继续承担业务数据存储MCP 把 Oracle 的查询、存储过程、甚至运维操作包装成标准工具交给 Agent 调用。这套 Dify Oracle MCP 组合既满足了客户“不动数据库”的底线又让大模型真的能查数、能干活。这篇就把我从环境准备到联调通过的全过程写出来包括 Dify 社区版部署、Oracle 安装与监听排查、MCP Server 编写、RAG 知识库流水线、以及 Oracle MCP Agent 的完整实现。适合正在做企业知识库、智能客服、内部数据问答这类项目的朋友参考。你会看到完整的架构选型思路、关键代码、提示词模板还有我踩过的坑。1. 项目概述为什么要拼这一套1.1 一个很现实的场景很多人一开始会问直接用 LangChain 写个 RAG 不就行了吗问题在于真实企业环境里“数据在哪里”比“模型怎么调”难得多。客户告诉我他们的产品资料分散在 Word、PDF 和 Oracle 业务系统里以前的智能问答只能回答文档里的内容一涉及到“上个月华东区交了多少货”这种要查库的问题就歇菜。于是需求就变成既要能基于文档回答问题又要能实时查 Oracle 数据库还不能让 AI 直接操作生产库造成风险。Dify 社区版在这个项目里承担的事很清晰知识库管理、检索、工作流编排、对外 API 和前端应用界面。Oracle 就是那个“真实数据源”业务数据不搬迁、不复制还在原库。MCP 则是中间的“协议桥梁”Agent 不会直接拼 SQL 字符串连库而是通过标准化的 MCP 工具去调用 Oracle 能力。三个角色各管一摊互不越权这个边界非常重要。这个组合适合谁如果你手头正好有以下几种情况之一可以参考这套方案企业内部已经有 Oracle并且短期内不可能迁移需要做一个能“查文档 查库”的智能助手团队不想从零开发 RAG 全链路希望用 Dify 这种平台快速落地同时保留一定的代码扩展能力。1.2 选型取舍为什么不是全部自研决定用 Dify 而不是纯代码方案我纠结过一阵。LangGraph FastAPI pgvector 这套热词组合我其实也搭过灵活性高但知识库管理、分段、召回评估、用户界面这些都要自己写。项目周期两周我不想把时间花在重复造轮子上。Dify 把知识库流水线、检索、工作流、Agent 节点都做好了我只需要把 Oracle MCP Server 写好然后拖几个节点串起来。Oracle 的选择更不需要纠结客户资产在那里数据库不能动那就让技术栈去适配数据库。很多人一听到 Oracle 就觉得老、重、不好搞但实际上它承载的是企业最核心的交易数据。做一个数据问答系统最大的价值恰恰来自这些历史数据。MCP 的出现让 Oracle 对接大模型这件事变得标准化了——不需要为每个客户端单独写 API 适配写一个 MCP ServerDify、Claude Desktop 这类支持 MCP 的客户端都能直接用。1.3 Agentic RAG 与传统 RAG 的区别这里必须区分一个概念。传统 RAG 是一次固定的“检索 - 拼接 - 生成”流程模型本身不决定要不要检索只是被动接收 Top K 片段。而 Agentic RAG 让模型变成“决策者”它可以根据问题判断是走知识库检索还是调数据库工具还是先查库再结合文档回答甚至可以多轮调用工具直到信息足够。在 Dify 里实现 agentic RAG 并不难工作流本身就是一个有限状态机。LLM 节点负责意图判断知识检索节点负责找文档工具节点负责调 Oracle MCP最后再让 LLM 汇总。真正难的是让模型知道“什么时候该查库什么时候该翻知识库”这就需要在提示词里把工具边界写清楚。后面第四节我会给出具体的提示词模板。2. 环境准备从零搭好三件套2.1 Dify 社区版部署与升级要点Dify 社区版部署是我每次都要强调“别想太多先跑起来”的环节。官方文档给的是 Docker Compose 方式拉到项目目录后执行 docker compose up -d 就能起一套完整环境包含了 API 服务、Worker、Web 前端、PostgreSQL、Redis、以及默认的向量数据库 Weaviate。我使用的版本正好赶上 Dify 1.17.1 的更新。这个版本里工作流节点编排更顺手Agent 节点对工具调用的控制也更细还带了多租户方面的改进。社区版本来就有多租户的概念只是不同工作空间的数据是隔离的1.17.1 在权限和成员管理上做了增强。如果你在 Windows 上部署建议直接用 Docker Desktop然后注意文件挂载路径权限尤其是 docker-compose.yaml 里的 volumes 目录Windows 下经常因为权限问题导致容器启动后又退出。部署中最容易翻车的是拉取镜像失败。Docker Hub 在国内网络环境下经常超时解决方案是给 Docker 配置 registry mirror或者干脆多试几次。注意docker compose pull 之后一定要 docker compose up -d 而不是 docker-compose up有些旧脚本版本会忽略新镜像。启动完成后浏览器访问 http://localhost/apps 初始化管理员账号就能看到 Dify 的控制台了。2.2 Oracle 安装与监听服务排查Oracle 数据库的安装是个体力活。我这次是在一台 CentOS 服务器上装的 Oracle 19c基本步骤是创建 oracle 用户、配置内核参数、设置环境变量 ORACLE_HOME、用 OUI 图形化安装、然后跑 DBCA 建库。如果你用官方的容器镜像 oracle/database:19c可以跳过一大半步骤环境变量设好监听和实例会自动起来。但“监听服务无法启动”是热词里出现频率极高的问题我也没逃过。常见原因有三个第一listener.ora 里的 HOST 写的是主机名但 DNS 解析不了改成服务器的 IP 地址或 localhost 立刻就好第二1521 端口被别的进程占用用 netstat -tlnp | grep 1521 查一下杀掉冲突进程或者改 LISTENER_PORT第三防火墙拦了端口firewall-cmd 里放行 1521 即可。排查顺序建议lsnrctl status 看报错信息再看 listener.ora再看网络。如果你想进入 Oracle ASM 实例管理需要用 grid 用户执行 asmcmd而且要确保环境变量 ORACLE_SIDASM 已经设置。新建用户和授权别忘了几件事默认表空间、临时表空间、connect/resource 角色以及给后续 MCP Server 使用的只读账号一定不要授 DBA 权限。数据库层面我还顺手整理了分页查询的写法Oracle 12c 之前只能拼 ROWNUM12c 之后有 OFFSET FETCHAgent 生成的 SQL 里如果涉及分页我会刻意提示模型用 OFFSET FETCH。2.3 MCP Server 快速搭建MCP 的全称是 Model Context Protocol它解决的是“大模型如何安全地调用外部工具”这个问题。你可以把它理解成 USB-C 接口——以前每个设备都有自己的充电线现在统一成一个标准接口任何支持 MCP 的客户端都能插上即用。对一个开发者来说写 MCP Server 比想象中简单。我建议直接用 Python 的 FastMCP 库它对标 FastAPI 的开发体验注册工具就是装饰器的事。一个最小的 Oracle MCP Server 大概长这样from mcp.server.fastmcp import FastMCP import oracledb mcp FastMCP(oracle-agent) DB_DSN localhost:1521/ORCLPDB1 DB_USER app_ro DB_PASSWORD your_password mcp.tool() def query_oracle(sql: str, limit: int 50) - str: 执行只读 SQL 查询并返回结果集仅允许 SELECT 语句。 sql sql.strip().rstrip(;) if not sql.lower().startswith(select): return Error: only SELECT statements are allowed. with oracledb.connect(userDB_USER, passwordDB_PASSWORD, dsnDB_DSN) as conn: cur conn.cursor() cur.execute(sql) cols [d[0] for d in cur.description] rows cur.fetchmany(limit) return \t.join(cols) \n \n.join( \t.join(str(c) if c is not None else NULL for c in row) for row in rows ) if __name__ __main__: mcp.run(transportstreamable-http)注意 FastMCP 的默认传输方式有很多Dify 调用 MCP Server 一般走 HTTP 方式也就是启动后提供 /mcp 端点。如果你只是本地调试也可以改成 transportstdio这样 Claude Desktop 或命令行客户端可以直接通过标准输入输出交互。工具函数的 docstring 一定要写清楚用途因为 MCP 协议会把函数名、参数描述、docstring 一起暴露给大模型模型靠这些描述来决定是否调用工具。工具描述写得越具体Agent 的意图识别准确率越高。3. RAG 应用核心实现3.1 知识库流水线设计知识库的质量直接决定 RAG 应用的天花板。我遇到很多项目模型明明很强但回答还是稀碎原因多半是文档没处理好。这次客户给了一批产品手册和业务规则文档格式五花八门有 PDF、Word、Markdown。Dify 知识库支持直接上传但直接传 PDF 的效果往往不好因为 PDF 里可能是扫描件或者复杂表格。我的流水线是这样设计的先把所有文档统一转成 Markdown 或纯文本用脚本做清洗去掉页眉页脚、目录、重复空行。然后按章节和段落拆分Dify 里可以设置分段标识符和最大分段长度。分段长度我一般设 500 到 800 个字符重叠量设 50这样既能保留上下文又不会让向量检索粒度太粗。分段之后Dify 会自动调用嵌入模型把每个分段向量化然后写入向量数据库。有一点需要特别提醒Dify 的知识库支持为每个分段打元数据比如文档来源、更新时间、产品线。这些元数据可以在检索时用来过滤效果非常明显。比如用户只问 A 产品线的问题那检索时就带上 metadata filter避免 B 产品线的片段干扰回答。如果你是自动同步 Oracle 里的文本字段到知识库也建议把主键和更新时间记录下来方便增量更新。3.2 嵌入模型与检索策略嵌入模型的选择很关键。如果你所在的项目环境允许调用外部大模型 API那直接让 Dify 接入 OpenAI 兼容接口就行如果数据敏感必须本地化我建议部署一个开源嵌入模型Dify 支持通过 Ollama 或 Xinference 接入本地模型。嵌入模型的维度、语言能力会直接影响召回效果中文场景下用国产中文嵌入模型往往比通用英文模型好一截。检索策略不要一上来就堆高级功能。先把基础召回调通再逐步加过滤和重排。我的实践步骤是先设置 top_k 为 5相似度阈值 0.25 左右不同模型分数分布不一样要实测调整跑几个典型问题看召回效果。如果发现检索结果里混了很多不相关片段就调高阈值如果漏检就降低阈值。Dify 的检索设置里还有“多路召回”能力可以同时命中关键词和向量再通过 Rerank 模型重新排序。实测下来加了 Rerank 之后答案准确率提升非常明显代价只是多一次模型调用延迟。3.3 Oracle 与向量存储的取舍这里要面对一个现实问题Oracle 老版本没有原生向量检索能力。Oracle 23ai 推出了 AI Vector Search支持 VECTOR 数据类型和向量索引但客户的生产库还是 19c不可能为了这个项目升级。我的方案是让 Oracle 继续承担业务数据存储向量数据放到单独的向量数据库里二者通过定时任务或实时接口同步。具体布局是这样的Oracle 表里的核心业务字段通过同步任务抽取到 Dify 知识库的文档分段里或者直接通过接口写入向量库用户问文档类问题走知识库检索问数据类问题走 Oracle MCP 查询。如果 Oracle 表本身就是文档型数据比如存了大量文本描述那么可以写一个定时任务把新增记录转成文档分段更新到 Dify 知识库。这样 Oracle 和向量库的边界就清晰了Oracle 是源、是事实向量库是检索加速层。如果你就是想少维护一套系统另一个可行方案是用 Oracle 23ai 免费版做验证把 VECTOR 字段建在业务表旁边用 SQL 直接做相似度检索。但我不建议在现有生产 19c 上强行做数据库大版本升级的风险远大于引入一个外部向量库的风险。生产环境求稳技术选型上不必追求“一个库干所有事”。4. Oracle MCP Agent 实现细节4.1 工具定义与安全设计Agent 要能操作 Oracle首先得把 Oracle 的能力封装成一个个 MCP 工具。我封装了四个工具query_oracle 负责只读查询get_tables 负责列出用户有权限的表get_schema 负责获取某张表的结构call_procedure 负责调用经过白名单的存储过程。每个工具的描述、入参都要经过精心设计这直接关系到模型会不会用、用得好不好。安全设计是我在这篇文章里最想强调的部分。Agent 自动生成 SQL 去查库这件事本身就带着风险。我的做法是给 MCP Server 单独创建一个 Oracle 只读账号只授予 SELECT 权限query_oracle 工具内部会先判断 SQL 是否以 SELECT 或 WITH 开头不是就拒绝执行同时设置 SQL 超时和返回行数上限防止模型生成笛卡尔积或者超大查询把数据库拖垮。对于存储过程不是所有过程都能调用只在 call_procedure 里维护一个白名单列表白名单之外的一律拒绝。还有一个容易被忽略的点返回数据里的敏感字段要脱敏。客户的库里身份证号、手机号是明文的Agent 查询结果如果直接返回给大模型再生成回答敏感信息就泄露了。我在 MCP Server 里加了一层字段脱敏对 column name 里的 id_card、phone 这类字段在返回前做掩码处理只露出前几位和后几位。4.2 在 Dify 编排 Agent 工作流Dify 里实现这个 Agent我推荐用工作流而不是直接选“Agent 应用”因为工作流能看清楚每一步在做什么出问题也好排查。工作流的骨架是开始节点接收用户问题LLM 节点做意图判断输出 JSON包含 needs_db、needs_kb、db_topic 等字段条件分支如果 needs_db 为 true走 MCP 工具节点传入从意图节点提取的表名、条件如果 needs_kb 为 true走知识检索节点把检索结果存入变量把工具结果和知识片段都传给最终的 LLM 节点让它整合答案输出。Dify 的 HTTP 工具节点可以直接配置成调用我本地起的 MCP Server 的 HTTP 端点也可以直接用工具节点里的 MCP 类型填写 endpoint 和协议。1.17.1 版本对工具的入参映射做了改进工作流里可以把用户问题变量直接传给工具参数。这里要注意MCP 工具入参最好用字符串模板拼接比如把用户问题放进“请根据问题生成 SQL”的提示词里再让一个专门写 SQL 的 LLM 节点输出 SQL最后把 SQL 字符串传给 query_oracle。不要把原始问题直接塞给工具。4.3 实战一次完整的对话式数据库查询拿一个真实问题来走一遍完整链路。用户问“上个月 A 类产品销量前五的是哪些顺便说说趋势。”第一步Dify 工作流开始节点拿到问题传给意图识别 LLM。我的提示词模板大概是这样你是数据库问答助手。请判断以下问题是否需要查询数据库、是否需要检索知识库。 只输出 JSON格式 {needs_db: true/false, needs_kb: true/false, question_type: sales_query/document_query/hybrid_query, entities: [上月, A类产品, 销量]} 问题{{query}}如果模型返回 needs_db 为 true就走“生成 SQL”节点。这个节点的系统提示词里我会把 Oracle 的关键表结构摘要放进去比如表 sales_summary 字段 month VARCHAR2(7)格式 YYYY-MM product_category VARCHAR2(50) product_name VARCHAR2(100) sales_qty NUMBER sales_amount NUMBER(12,2) 请根据用户问题生成只读 SELECT 语句。注意当前日期是 2025 年某月上个月需要基于 DATE 函数或 TO_CHAR(SYSDATE, YYYY-MM) 计算。只输出 SQL不要解释。这里强调日期动态计算非常关键。如果提示词里写死“例如上月是 2024 年 12 月”模型一到下个月就生成错误日期。用 SYSDATE 提示模型是一条稳定有效的路。模型生成的 SQL 会经过 MCP Server 的校验然后执行最后返回表格文本。第三步汇总节点拿到 query_oracle 返回的文本把它和知识库检索到的产品介绍片段一起放进最终提示词要求模型用自然语言总结前十名并分析趋势。实测结果基本符合预期模型会直接把数据表转成“第一名某某产品多少件”的描述再结合文档里的产品定位给出趋势解读。整条链路跑通后我给客户演示时他们最惊讶的就是“它居然真的在用我们的数据库口径回答”。5. 常见问题与排查技巧实录5.1 Dify 部署与升级高频问题Dify 部署时很多人卡在 docker 镜像拉取。我常用的处理办法是设置 Docker 的 registry mirror 或者切换镜像源然后再 docker compose pull。如果拉下来之后容器一直重启先看日志 docker compose logs api多半是环境变量问题比如 SECRET_KEY 没设置或者 PostgreSQL 连接失败。Dify 的管理后台入口不是根路径而是 /apps登录后创建应用。遇到登录页加载不出来先检查 Web 容器是否正常监听 3000 端口。升级 Dify 社区版时我强烈建议先备份 docker volume。具体做法是 docker compose down 之后把挂载目录整个复制一份再进行目录更新和 docker compose up -d。1.17.1 这次更新里我对工作流节点变化印象很深旧版本创建的部分自定义节点在新版本里可能需要重新配置参数映射所以线上环境的升级要在测试环境先跑一遍。多租户方面不同工作空间的数据隔离是天然支持的管理员在“设置-成员”里邀请用户加入不同空间即可。5.2 Oracle 连接与数据格式问题Oracle 连接出问题先看监听。lsnrctl status 如果显示 “The listener supports no services”说明数据库实例没有注册到监听原因可能是数据库没启动、或者 REMOTE_LISTENER 配置不对、或者是本地服务注册延迟。此时可以用 sqlplus 登录数据库执行 ALTER SYSTEM REGISTER; 强制注册。分页查询是另一个高频需求。Agent 生成的 SQL 里如果带分页我提醒模型用 OFFSET FETCH 语法因为它是标准 SQL:2008 风格Oracle 12c 以后都支持。老项目里常见的 ROWNUM 写法在复杂查询中容易出错。还有一个很典型的“坑”从 Oracle 导出数据到 Excel 时身份证号变成科学计数法。这个问题的根源是 Excel 把长数字当数值处理了。解决方案有两种在 SQL 查询时就把身份证字段转成字符串比如 TO_CHAR(id_card)导出 CSV 时再加一个不可见的前缀或者在 Excel 里把列设置为文本再粘贴。在 Agent 回答场景中同样要注意如果返回的身份证号是 NUMBER 类型模型拿到后会显示成科学计数法所以查字段时就要在 SQL 层面转换成字符型。存储过程的调用我也提一句Agent 直接调 CALL procedure_name(...) 这种语法在 Python 的 oracledb 里通常用 cursor.callproc 更稳。MCP Server 里封装 call_procedure 工具时不要真的拼一个 SQL 字符串去执行而是解析出过程名和参数然后用 callproc 调用既安全又能正确拿到 OUT 参数。5.3 MCP 与 Agent 联调错误排查联调阶段最容易出现的问题有三个。第一工具调用后 Agent 长时间不返回十有八九是 SQL 执行太慢导致超时。我给 MCP Server 设置了 10 秒超时超过就返回错误信息提示 Agent 换一种查询方式或缩小数据范围。第二工具返回了结果但 Agent 只回复“无法生成回答”这种一般是返回内容太长把上下文窗口塞满了。解决方法是限制返回行数和字段数或者让 MCP Server 先做一层摘要再返回。第三权限问题客户端拿到的 MCP 令牌过期或者没有访问该工具的权限Dify 工具节点里配置 MCP endpoint 时记得测试连接多数平台会直接提示错误码。关于“Agent 画图”这类需求也经常有人问。用 MCP Server 接一个图表生成工具其实不难FastMCP 里再加一个函数返回 Base64 编码的 PNG 图片前端就能展示。但要注意Dify 的默认输出对图片的支持有限最好把图表生成的结果存成文件链接再在回答里引用。这种方式适合做数据可视化报表比如用户问“画一下近六个月销量趋势图”Agent 先从 Oracle 查数据再调用画图工具生成图表。5.4 提示词与结果的持续优化整套系统跑起来不难难的是让回答一直准。我维护了一份“字段字典”提示词把 Oracle 表里的业务字段、枚举值、单位都写成说明在生成 SQL 节点里作为上下文注入。比如“sales_qty 单位是件不含退款product_category 的枚举值有 A/B/C 三类”。模型有了这份字典后生成 SQL 的准确率大幅提升。Rerank 也是值得投入的点。Dify 知识库的检索设置里可以开启 Rerank选择一个 rerank 模型让系统在召回后重新计算相关性。实测下来加 Rerank 后回答的相关性打分能提升不少特别是文档主题杂、容易互相干扰的场景。最后还要建立回归测试集把客户常问的 30 个问题存成一个数据集每次调整提示词或模型后跑一遍防止改了一个问题的效果把另外几个问题改坏了。6. 实操心得与后续扩展项目收尾后我复盘了一下最值得保留的经验还是那三条边界清晰、安全前置、提示词驱动。Dify 管编排Oracle 管数据MCP 管连接三个组件各司其职出问题的时候能很快定位到具体环节。安全不是最后才补的而是在工具设计的第一版就考虑进去只读账号、白名单、脱敏这三板斧缺一不可。提示词不是写一次就完事它需要随着测试不断迭代特别是生成 SQL 的节点字段字典要持续维护。这套架构后续还能扩展的方向也很多。比如在 MCP Server 里加一个执行写操作的“可控写工具”配合审批流程就能让 Agent 具备简单的业务办理能力再比如把多个 Oracle 实例都封装成不同的 MCP Server通过 Dify 的 Agent 节点统一调度就变成了一个多数据源的企业数据助手。我后来在另一个项目里把 MCP Server 换成 LangGraph 重写调度逻辑更灵活但核心思路完全一致迁移成本很低。如果让我给还没动手的朋友一个建议不要一上来就追求复杂先按这篇文章搭一条最简单的链路用 Dify 的默认配置跑通一个“文档问答 单表查询”的场景再逐步加入多表、存储过程、意图分类。跑通一条链路给你带来的信心比看十篇架构分析都管用。
返回列表