1. dify 接 pg 数据库为什么绕不开 DBHubdify 本身是个工作流编排平台它擅长把 LLM、知识库、代码节点串起来但它默认不会直接连你的 PostgreSQL。你当然可以在代码节点里塞一段 psycopg2但那样每次改表结构都要改代码Agent 也没法自己决定查哪张表。DBHub 解决的就是这个问题它是一个 MCP 服务把数据库包装成一组标准工具list_tables、describe_table、execute_sql 等dify 的 Agent 通过 Function Calling 调用这些工具就能自己拼 SQL 去查 pg。适合谁本地或云端跑着 dify手里有一个 PostgreSQL不管是本机、内网还是云数据库想让工作流里的 Agent 直接读业务数据比如查学生成绩、查订单、查日志。这篇的目标很明确——给你一份能直接复制的配置链路DBHub 连接串、dify 侧 MCP 端点、TaoToken 统一 Key 的 settings.json/config.toml 骨架最后跑一次查询验证连通性。我试过把 DBHub 和 dify 分开部署在两台机器上踩过的坑主要集中在连接串转义和容器网络这两块下面会逐个说清楚。2. 前置准备TaoToken 统一 Key 与 DBHub 镜像在动 dify 之前先把两样东西准备好一个能用的模型 Key和一个跑起来的 DBHub。模型这块dify 的 Agent 节点需要 Function Calling 能力所以你得有一个支持工具调用的模型。TaoToken 的好处是统一 Key一个 Key 走多个模型省得在 dify 里配一堆供应商。先去控制台拿 Key模型对话入口https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewriteAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteAPI 基址统一用https://taotoken.net/api这个地址不加 UTM直接填进 dify 的模型供应商配置里。DBHub 用官方镜像bytebase/dbhubDocker 一条命令就能起。注意 DBHub 默认监听 8080我们映射到宿主机 8081避免和别的服务撞端口。docker run --init \ --name dbhub \ --publish 8081:8080 \ bytebase/dbhub \ --transport http \ --port 8080 \ --dsn postgres://用户名:密码数据库地址:5432/库名?sslmodedisable这里有个高频坑密码里如果带必须转义成%40。比如密码是zqyt123连接串里要写成zqyt%40123否则 DBHub 会把后面的部分当成主机名直接连不上。# 密码含 的正确写法 --dsn postgres://roadshow_owner:zqyt%40123101.200.238.239:5432/roadshow?sslmodedisable启动后访问http://localhost:8081能看到 DBHub 自带的 Web UI workbench说明服务活着。这个 workbench 后面还有用——dify 里 Agent 执行的 SQL 会在这里留痕方便你排查它到底查了什么。3. 可复制配置DBHub 连接串与 dify MCP 端点DBHub 起来之后dify 侧要做两件事装 MCP 插件、加 MCP 服务。先装插件。在 dify 的插件市场搜 MCP安装官方那个 MCP 插件支持 HTTP 和 SSE 两种传输。装完在「工具」里能看到 MCP 分类。然后添加 MCP 服务选 HTTP 传输服务端点 URL 填http://host.docker.internal:8081/mcp这里的关键是host.docker.internal。如果你的 dify 是 Docker 部署的容器内部访问宿主机要用这个域名如果 dify 和 DBHub 在同一个 Docker 网络里直接填 DBHub 的容器名加端口比如http://dbhub:8080/mcp。填错了会一直连不上绿点不亮。保存后如果看到绿点说明 dify 已经成功握手 DBHub工具列表也拉到了。接下来是模型配置。在 dify 的「模型供应商」里选 OpenAI 兼容类型API Base 填https://taotoken.net/apiKey 填你从控制台拿的那个。如果你用配置文件方式管理TaoToken 的 settings.json 骨架大概长这样{ model_provider: openai_compatible, api_base: https://taotoken.net/api, api_key: sk-你的TaoTokenKey, default_model: claude-sonnet-4-5, function_calling: true }如果你更习惯 config.toml 风格比如在自建网关或 CLI 工具里复用同一套 Key可以这样写[provider] name taotoken api_base https://taotoken.net/api api_key sk-你的TaoTokenKey [model] default claude-sonnet-4-5 supports_function_calling true注意dify 的 Agent 节点必须选支持 Function Calling 的模型否则工具调用不会触发Agent 只会干聊不查库。4. 验证请求建表、插数据、跑一次查询配置齐了得用真实数据验证链路通不通。下面这套 pg 建表语句可以直接在 DBHub 的 workbench 里执行也可以在 psql 里跑。注意这是 PostgreSQL 语法和 MySQL 有区别自增用SERIAL枚举用INT加 CHECK 约束YEAR类型换成SMALLINT。-- 教师表 CREATE TABLE teachers ( teacher_id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, gender INT, contact_phone VARCHAR(20), email VARCHAR(100), hire_date DATE NOT NULL, CONSTRAINT check_teacher_gender CHECK (gender IS NULL OR gender IN (1,2,3)) ); COMMENT ON TABLE teachers IS 教师基本信息表; -- 班级表 CREATE TABLE classes ( class_id SERIAL PRIMARY KEY, class_name VARCHAR(50) NOT NULL, head_teacher_id INT NOT NULL, create_year SMALLINT NOT NULL, CONSTRAINT fk_head_teacher FOREIGN KEY (head_teacher_id) REFERENCES teachers(teacher_id) ); -- 学生表 CREATE TABLE students ( student_id SERIAL PRIMARY KEY, student_number CHAR(12) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender INT, birth_date DATE, class_id INT NOT NULL, enrollment_date DATE NOT NULL, contact_phone VARCHAR(20), CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES classes(class_id), CONSTRAINT check_student_gender CHECK (gender IS NULL OR gender IN (1,2,3)) ); -- 课程表 CREATE TABLE courses ( course_id SERIAL PRIMARY KEY, course_code VARCHAR(20) NOT NULL UNIQUE, course_name VARCHAR(100) NOT NULL, credit NUMERIC(3,1) NOT NULL, teacher_id INT NOT NULL, course_hours SMALLINT, CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teachers(teacher_id) ); -- 成绩表 CREATE TABLE exam_results ( result_id SERIAL PRIMARY KEY, student_id INT NOT NULL, course_id INT NOT NULL, exam_date DATE NOT NULL, score NUMERIC(5,2), exam_type INT NOT NULL, recorder_id INT NOT NULL, record_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT unique_exam_record UNIQUE (student_id, course_id, exam_date, exam_type), CONSTRAINT fk_result_student FOREIGN KEY (student_id) REFERENCES students(student_id), CONSTRAINT fk_result_course FOREIGN KEY (course_id) REFERENCES courses(course_id), CONSTRAINT fk_result_recorder FOREIGN KEY (recorder_id) REFERENCES teachers(teacher_id), CONSTRAINT exam_results_score_check CHECK (score 0 AND score 100), CONSTRAINT exam_results_type_check CHECK (exam_type IN (1,2,3,4)) );插几条测试数据让 Agent 有东西可查INSERT INTO teachers (name, gender, hire_date) VALUES (张老师, 1, 2020-09-01), (李老师, 2, 2019-03-15); INSERT INTO classes (class_name, head_teacher_id, create_year) VALUES (2023级1班, 1, 2023); INSERT INTO students (student_number, name, gender, class_id, enrollment_date) VALUES (202301010001, 王小明, 1, 1, 2023-09-01), (202301010002, 赵小红, 2, 1, 2023-09-01); INSERT INTO courses (course_code, course_name, credit, teacher_id) VALUES (MATH101, 高等数学, 4.0, 1); INSERT INTO exam_results (student_id, course_id, exam_date, score, exam_type, recorder_id) VALUES (1, 1, 2024-01-10, 88.5, 2, 1), (2, 1, 2024-01-10, 92.0, 2, 1);数据就位后回到 dify 建一个对话流。Agent 节点的指令可以这样写把表结构喂给它你是一个学校的工作人员负责调用工具查询老师、学生、班级、课程和成绩信息。 数据库表结构如下 - teachers(teacher_id, name, gender, contact_phone, email, hire_date) - classes(class_id, class_name, head_teacher_id, create_year) - students(student_id, student_number, name, gender, birth_date, class_id, enrollment_date, contact_phone) - courses(course_id, course_code, course_name, credit, teacher_id, course_hours) - exam_results(result_id, student_id, course_id, exam_date, score, exam_type, recorder_id, record_time) gender 和 exam_type 用整数表示gender: 1男 2女 3其他exam_type: 1期中 2期末 3补考 4测验。 根据用户提问查询数据库并返回结果。Agent 策略选 Function Calling模型选你配好的 TaoToken 模型。预览时问一句「2023级1班高等数学期末成绩最高的是谁」Agent 会自己调 DBHub 的 execute_sql 工具拼出类似这样的 SQLSELECT s.name, e.score FROM exam_results e JOIN students s ON e.student_id s.student_id JOIN courses c ON e.course_id c.course_id WHERE c.course_name 高等数学 AND e.exam_type 2 ORDER BY e.score DESC LIMIT 1;返回「赵小红 92.0」链路就通了。同时去 DBHub 的 workbenchhttp://localhost:8081能看到这条 SQL 的执行记录确认 Agent 真的查了库而不是编答案。5. 本篇常见错排查绿点不亮MCP 连不上。九成是端点 URL 写错。dify 在容器里localhost指的是 dify 自己不是宿主机。用host.docker.internal或者同网络的容器名。另外确认 DBHub 的--transport http参数带了默认可能是 stdio。连接串报错提示 host 解析失败。检查密码里的特殊字符。转义成%40:转义成%3A/转义成%2F。最稳妥的办法是给数据库单独建一个只读账号密码用纯字母数字省得转义。Agent 不调工具只回文字。模型不支持 Function Calling或者 dify 里没开工具调用。换一个明确支持工具调用的模型Agent 策略确认选的是 Function Calling 而不是 ReAct部分版本 ReAct 对 MCP 工具支持不稳。SQL 执行报权限错误。DBHub 用的数据库账号没有目标表的 SELECT 权限。给它授只读权限就行GRANT SELECT ON ALL TABLES IN SCHEMA public TO 你的dbhub账号;查出来的字段是数字不是中文。因为 gender 和 exam_type 存的是 INT。要么在 Agent 指令里写清楚映射关系要么在数据库里建视图把 INT 转成中文让 Agent 查视图。dify 版本差异导致 MCP 插件入口不同。老版本 dify 可能没有内置 MCP 插件市场需要手动装插件包。建议升到较新版本或者用 HTTP 请求节点直接调 DBHub 的 REST 接口作为兜底。6. 长期跑 Agent 查库Key 和模型怎么管如果你只是偶尔查一次上面这套配置够用了。但如果你要把这个对话流做成长期运行的 Agent比如每天定时跑报表、或者接进客服系统实时查订单那模型调用量会上来Key 管理和成本控制就得提前想。TaoToken 的统一 Key 在这里的价值是你不用在 dify 里为每个模型单独配供应商一个 Key 切换模型Agent 节点换模型只改一个字段。对于需要长期编码或跑 Agent 的场景Coding Plan 比按量付费更划算适合高频调用的工作流Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite另外提醒一句DBHub 暴露的是数据库的完整查询能力生产环境务必用只读账号并且限制它能访问的 schema。别让 Agent 拿到 DROP 或 DELETE 的权限这是接入生产库的底线。
