专题十三:知识库开启 Rerank 重排序(Xinference bge-reranker-v2-m3)
本文记录在
192.168.110.207上,为播城行政问答智能体接入 Rerank 重排序:用已有 Xinference 部署bge-reranker-v2-m3,在知识库检索设置中开启 Rerank、Top-K=5,并同步改新 Agent 运行时配置。验收用题是此前答得含糊的「法定假日加班的制度是什么」。开启后命中测试把 300% 加班费条款 排到第 1,Agent 能直接给出计算公式。
全文分三部分:原理说明、可照做的操作步骤(含配置和代码)、本次实战踩坑与解法。
运行环境:业务脚本均在 207 固定目录
/opt/workbuddy执行,Python 为/usr/bin/python3。 Windows 开发机仅保留_probe_*调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。
目录
步骤 3 分两条:无 GPU 看 3.1;有 GPU 看 3.2。
第一部分 原理与说明
一、本文解决什么问题
目标只有一件事:
向量检索先召回一批可能相关的制度片段,再用 Rerank 模型按「和当前问句到底有多相关」重新排序,把真正能回答问题的条款送到大模型面前。
验收必须同时看到:
- Xinference 里
bge-reranker-v2-m3状态为 running,/v1/rerank能打分。 - Dify 知识库检索设置:Rerank 开启,模型为该 reranker,Top-K = 5。
- 新 Agent 对话用的也是同一套 Rerank(不能只改知识库页、Agent 仍按旧检索跑)。
- 用以前答得含糊的复杂问题对比前后:命中测试第 1 条应变成更贴题的条款;Agent 回答要能引用关键数字(本次是法定节假日加班费 300%)。
- Langfuse 里能看到这轮问答 Trace。召回名次变化以知识库命中测试为准(原因见第三部分)。
二、Rerank 在检索链路里做什么
知识库问答通常是两段:
用户问句
│
▼
① Embedding 召回(bge-m3 + Weaviate)
按向量相似度取出一批 chunk
分数高只代表「向量靠近」,不一定是能直接答题的条款
│
▼
② Rerank 精排(bge-reranker-v2-m3)
把「问句 + 每个 chunk 原文」成对送进交叉编码器
输出 relevance_score,按分数重排
只把 Top-K 条交给大模型
│
▼
③ LLM 根据精排后的片段生成答案没有 Rerank 时,Dify 把 Embedding 的前几条直接塞进 Prompt。问「法定假日加班费怎么算」时,向量检索更容易先命中标题像「法定节假日管理规定」的放假天数段落,真正写 300% 加班费公式 的 11.1.6 可能排在后面,模型就答得空、答得偏。
有 Rerank 时,模型看到的第 1 条更可能是「加班工资计算方法」。本次命中测试就是这样翻过来的。
三、为什么选 Xinference + bge-reranker-v2-m3
可选两条路:单独起一套 TEI(Text Embeddings Inference),或复用已经在跑的 Xinference。
本次选 Xinference,原因:
- 207 上已经有 Xinference 在跑
bge-m3(专题二),Dify 的 Xinference 插件本身支持rerank类型。 - 再起 TEI 要新端口、新镜像、新供应商配置,和现有 Embedding 拆成两套,运维面更大。
- Xinference 内置列表里就有
bge-reranker-v2-m3和jina-reranker-v2。前者和现有bge-m3同一家族、中英都支持,适合制度类中文文档。
为什么本次跑在 207 CPU,不放到 GPU 机 116.62.10.183:
- GPU 机已经给 vLLM(Qwen3.6-27B)留了 0.75 显存,还要给业务
mxh_env/mxh_live。 - 207 没有 GPU(
bge-m3的accelerators就是空数组),但内存大约还有 21Gi 可用;reranker 大约 1GB 级权重,CPU 推理对 Top-K=5 够用。 - 无 GPU 机器不要写
--n-gpu 0,原因见第三部分问题 4。
有 GPU、且 nvidia-smi 确认有余量时,应走步骤 3.2 的完整 GPU 操作(容器加 --gpus all,launch 用 --n-gpu auto 或 --n-gpu 1)。生产检索并发高时,Rerank 上 GPU 比长期趴在 Dify 同机 CPU 上更合适。
四、必须改两处配置,不能只改知识库页
Dify 1.16 新 Agent(mode=agent)的检索参数 不是只读知识库上的 retrieval_model。
| 位置 | 谁在用 | 本次改前 | 本次改后 |
|---|---|---|---|
知识库 datasets.retrieval_model | 知识库页、命中测试 | reranking_enable=false,top_k=3 | 开启,Top-K=5,模型 bge-reranker-v2-m3 |
Agent config_snapshot.knowledge.sets[].retrieval | 对话真正召回 | reranking_enable=false,top_k=4,reranking_mode=weighted_score | 开启,Top-K=5,reranking_mode=reranking_model |
只改知识库:命中测试变好,Agent 问答仍按旧的 Top-K=4、无 Rerank 跑。 只改 Agent:对话好了,知识库页上显示仍是关着的,后人会以为没配。
两处的 JSON 字段名还不一样,不能复制粘贴错:
知识库:
"reranking_model": {
"reranking_provider_name": "langgenius/xinference/xinference",
"reranking_model_name": "bge-reranker-v2-m3"
}新 Agent(AgentKnowledgeRerankingModelConfig,extra=forbid):
"reranking_model": {
"provider": "langgenius/xinference/xinference",
"model": "bge-reranker-v2-m3"
}运行时 Dify 会把 Agent 的 provider/model 转成知识库那套 reranking_provider_name/reranking_model_name 再调用插件。
五、和已有专题的关系
| 专题 | 关系 |
|---|---|
| 专题二 | Xinference + bge-m3。本文在同一容器再 launch 一个 model_type=rerank 实例。容器起来 ≠ 模型在跑,对 rerank 同样成立。 |
| 专题三 | 知识库必须 available、embedding 可达,Rerank 才有意义。 |
| 专题十一 / 十二 | 改 Agent 库表后 不要点发布,否则 max_tokens 可能被写回 null 或再踩 2048。本文改 knowledge.sets 时沿用同一条禁忌。 |
| Langfuse 连接 Dify | 用来看这轮问答 Trace。Generation 的 Input 目前往往只有用户原话,召回名次请看命中测试。 |
六、本次环境
| 项 | 值 |
|---|---|
| 服务器 | 192.168.110.207 |
| Dify | 1.16.1,http://192.168.110.207 |
| 应用 | 播城行政问答智能体 |
| 应用 ID | 40e95fd3-e7f5-44f2-a03b-dc3e60413540 |
| Agent ID | 01a012e7-bed3-7574-b0d1-a5cc79a0d3de |
| 活动快照 ID | 01a0135c-320e-79dc-97a0-94da9b904c13 |
| 编辑草稿 ID | 01a012e7-bf60-7673-a894-64551d984a23 |
| 知识库 | 播城内部行政管理知识库 |
| 知识库 ID | ee2495bf-c370-49d1-8268-10c51f650fa6 |
| Embedding | bge-m3(Xinference) |
| Rerank | bge-reranker-v2-m3(Xinference,CPU,engine=sentence_transformers) |
| Xinference | http://192.168.110.207:9997,容器名 xinference |
| Xinference 管理员 | admin / admin1234 |
| Xinference API Key | xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI(只能推理,不能 launch) |
| Dify 控制台 | 38205036@qq.com / Difyai123456 |
| 插件 | langgenius/xinference:0.0.15 |
| Top-K | 5 |
| 验收 Trace | http://192.168.110.207:3000/project/default/traces/45b0e430-e592-45c2-a582-b94b7ff63a70 |
换环境时 UUID、密钥、IP 都要重查,不要直接抄。
第二部分 详细操作步骤
Python 约定(全库统一)
- 每个操作步骤对应
/opt/workbuddy/scripts_cursor/下一个完整可运行程序(含main()、登录、常量);不要从正文里抄片段拼程序。- 有配置改动时提供 修改前 / 修改后 两个文件(文件名带
_修改前/_修改后),正文附 改动对比 表。- 换环境只改
/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/_config.py。在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/脚本名.py- 各步骤正文已嵌入对应完整程序;插件源码级补丁也做成完整 apply 脚本放在
/opt/workbuddy/scripts_cursor/。
下面按本次真实顺序写。人在 207 上可以直接 docker exec;从 Windows 工作区过去,普通 ssh 容易卡密码,用现有 dify_api.py(Paramiko)即可。
步骤速查(1~11:目标 · 通过 · 卡住)
| 步 | 目标 | 通过标准 | 卡住 → |
|---|---|---|---|
| 1~2 | 基线 | 命中测试有结果、未开 Rerank | 专题二 embedding |
| 3~4 | launch rerank | /v1/rerank 有分数 | 本文第三部分 Q1~Q4 |
| 5~6 | Dify 凭据 + 库设置 | 库 retrieval 开 Rerank | 字段名勿混 |
| 7 | Agent 快照+草稿 | 两处 JSON 都改 | 只改库未改 Agent |
| 8~9 | 命中+对话 | Top-1 变化、能引用 | 新开对话 |
| 10~11 | Trace + 检查单 | Langfuse 有 trace | 第三部分 Q10 |
Python 约定(全库统一)
- 正文不再只贴片段。每个步骤对应
/opt/workbuddy/scripts_cursor/下一个完整可运行的.py文件(含main()、常量、登录)。- 有配置改动时给 修改前 / 修改后 两个完整程序,并附 改动对比 表。
- 换环境只改
/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/_config.py。运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/文件名.py(在仓库根目录)。
步骤 1 · 确认现状
确认 Xinference 在跑、只有 embedding、知识库还没开 Rerank。
207 上可先跑 shell(容器/内存):
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.sh
#!/usr/bin/env bash
set -euo pipefail
docker ps --filter name=xinference --format '{{.Names}} {{.Status}} {{.Ports}}'
docker stats xinference --no-stream --format '{{.MemUsage}} CPU={{.CPUPerc}}'
free -h本次实录:容器 Up,映射 9997;大约占用 2.2Gi / 31Gi;宿主机还有约 21Gi available。
Dify / 库表侧用下面完整程序一次查完(含 Xinference 模型列表、知识库 retrieval、Agent retrieval、rerank 模型列表):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_01_确认现状.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_01_确认现状.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 1:确认现状(Rerank 开启前的基线)。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
print("=== 1/4 Xinference 容器与内存(在 207 上 ssh 执行 docker 命令)===")
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
for cmd in [
"docker ps --filter name=%s --format '{{.Names}} {{.Status}} {{.Ports}}'" % C.XF_CONTAINER,
"docker stats %s --no-stream --format '{{.MemUsage}} CPU={{.CPUPerc}}'" % C.XF_CONTAINER,
"free -h",
]:
out, err = d.sh(cmd)
print(out or err)
print("\n=== 2/4 Xinference 已加载模型(应只有 bge-m3 embedding)===")
out, err = d.sh(
"curl -s -H 'Authorization: Bearer %s' %s/v1/models" % (C.XF_KEY, C.XF_URL)
)
print(out or err)
try:
models = json.loads(out)
for m in models.get("data", models if isinstance(models, list) else []):
if isinstance(m, dict):
print(" -", m.get("id"), m.get("model_type"), m.get("accelerators"))
except Exception:
pass
print("\n=== 3/4 登录 Dify,读知识库 retrieval_model ===")
ok, _, csrf = d.login()
print("login ok:", ok, "csrf:", bool(csrf))
r = d.get("/datasets/%s" % C.DS_ID)
pp("dataset retrieval_model_dict", r)
retr = (r.get("json") or {}).get("retrieval_model_dict") or {}
if retr.get("reranking_enable"):
print("[WARN] 知识库已开 Rerank,不是「修改前」基线")
print("\n=== 4/4 Agent 快照 knowledge.sets[0].retrieval + rerank 模型列表 ===")
sql = (
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot::jsonb -> 'knowledge' -> 'sets' -> 0 -> 'retrieval' "
"FROM agent_config_snapshots WHERE id='%s';\"" % C.SNAP_ID
)
out, err = d.sh(sql)
print("agent retrieval:", out.strip() or err)
r2 = d.get("/workspaces/current/models/model-types/rerank")
pp("model-types/rerank(期望 data 为空)", r2)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())改前知识库 retrieval_model_dict 期望:
{
"search_method": "semantic_search",
"reranking_enable": false,
"reranking_mode": null,
"reranking_model": {
"reranking_provider_name": null,
"reranking_model_name": null
},
"top_k": 3,
"score_threshold_enabled": false,
"score_threshold": null
}改前 Agent knowledge.sets[0].retrieval 关键字段:
{
"mode": "multiple",
"top_k": 4,
"reranking_mode": "weighted_score",
"reranking_enable": false,
"reranking_model": null
}Dify 侧当时 GET /workspaces/current/models/model-types/rerank 返回 "data": [],说明还没有 Rerank 模型凭据。
步骤 2 · 先做一轮「开启前」命中测试(对照基线)
不要一上来就改。先把旧排序留下。
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_02_命中测试_修改前.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_02_命中测试_修改前.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 2:开启 Rerank 前的命中测试(对照基线)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
QUERY = "法定假日加班的制度是什么"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.post(
"/datasets/%s/hit-testing" % C.DS_ID,
body={
"query": QUERY,
"retrieval_model": {
"search_method": "semantic_search",
"reranking_enable": False,
"top_k": 5,
"score_threshold_enabled": False,
},
},
)
pp("hit-testing before rerank", r)
records = r.get("json")
if not isinstance(records, list):
records = (records or {}).get("records") or []
print("\n--- 前 5 条 ---")
for i, rec in enumerate(records[:5], 1):
seg = (rec.get("segment") or {}).get("content") or ""
print(i, rec.get("score"), seg[:80].replace("\n", " "))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())本次开启前前三条(分数为 Embedding 相似度):
| 名次 | 约分数 | 片段大意 |
|---|---|---|
| 1 | 0.695 | 10、法定节假日管理规定(放假天数、国务院办法) |
| 2 | 0.656 | 节假日加班认定 + 加班定义 |
| 3 | 约 0.65 | 11.1.6 加班工资,含 300% 公式 |
问加班费时,公式条款不在第 1,这就是「回答模糊」的检索侧原因。
步骤 3 · 用管理员登录 Xinference,再 launch Rerank
推理用的 xf-... Key 不能 launch。必须先在容器里用管理员登录,token 写进容器用户目录,再 launch。这条对 CPU / GPU 都成立。
按机器有没有 GPU 选下面 3.1 或 3.2,不要混用参数。
| 机器 | 容器 | launch 的 GPU 参数 | 结果 |
|---|---|---|---|
| 无 GPU(本次 207) | 不要加 --gpus | 不要传 --n-gpu,尤其不要 --n-gpu 0 | accelerators: [],CPU |
| 有 GPU | 必须 --gpus all(或指定卡号) | --n-gpu auto 或 --n-gpu 1 | accelerators 非空,CUDA |
3.1 本次实录:207 无 GPU,走 CPU
不要加 --n-gpu 0。207 没有 GPU,传 0 会被拒绝。不传该参数,行为与已经跑着的 bge-m3 一样,走 CPU。
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec xinference bash -lc '
set -e
xinference login -e http://127.0.0.1:9997 --username admin --password admin1234
xinference registrations -e http://127.0.0.1:9997 -t rerank
xinference launch -e http://127.0.0.1:9997 \
-n bge-reranker-v2-m3 \
-t rerank \
-u bge-reranker-v2-m3
'registrations 里应能看到内置:
rerank bge-reranker-v2-m3 ['en', 'zh', 'multilingual'] True
rerank jina-reranker-v2 ['en', 'zh', 'multilingual'] True本次 launch 会从 HuggingFace 镜像拉权重。实录大约 392 秒,进度条从长时间 0% 跳到 80% 再 100%。成功标志:
Model uid: bge-reranker-v2-m3日志应有:
ModelActor(bge-reranker-v2-m3-rep0) loaded
Launch finished: model_name=bge-reranker-v2-m3再次列模型,应同时有 embedding 和 rerank:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -H 'Authorization: Bearer xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI' \
http://127.0.0.1:9997/v1/models本次成功条目要点:
{
"id": "bge-reranker-v2-m3",
"model_type": "rerank",
"model_name": "bge-reranker-v2-m3",
"model_engine": "sentence_transformers",
"accelerators": [],
"language": ["en", "zh", "multilingual"],
"replica": 1
}accelerators 为空表示 CPU。engine 是 sentence_transformers,容器里即使没装 FlagEmbedding 也能起。
Xinference 重启后实例会丢,要按本步重新 login + launch,和 bge-m3 一样。无 GPU 继续用 3.1;有 GPU 用 3.2 的 launch 命令(带 --n-gpu)。
3.2 有 GPU 时的完整操作(生产推荐)
CPU 能把 Rerank 跑通,但交叉编码器比 Embedding 更吃算力。10 条长文档在 CPU 上十几秒是硬件问题,不是 Dify 配错。有 GPU、且确认不会挤掉 Qwen / 业务进程时,按下面做完。
先判断走哪条路径:
| 路径 | 适用 | 做什么 |
|---|---|---|
| A | 当前这台 Xinference 机器已经有 NVIDIA GPU,容器也已经加了 --gpus | 只 login + launch,指定 --n-gpu |
| B | 本次这种:Xinference 在 207(无 GPU),要把检索迁到 GPU 机 116.62.10.183 | 先查显存 → 在 116 起 GPU 容器 → login + launch Embedding 和 Rerank → 207 验网 → 改 Dify 两处凭据 URL |
两条路径的 login / uid / 不要用推理 Key 去 launch,规则相同。
路径 A · 本机已有 GPU
① 确认 GPU 和容器真能用到卡
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_04.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_04.sh
#!/usr/bin/env bash
set -euo pipefail
nvidia-smi
docker inspect xinference --format '{{json .HostConfig.DeviceRequests}} {{.HostConfig.Runtime}}'nvidia-smi 里要有空闲显存。reranker 权重大约 1GB 级,连运行时按 预留 2~4GB 估。若这张卡已经给 vLLM 留了 gpu-memory-utilization 0.75、再加上业务 mxh_env / mxh_live,余量不够就不要硬上,继续 3.1。
DeviceRequests 不能是 null。若容器创建时没加 --gpus all,容器内看不到 CUDA,launch 即使用 --n-gpu 1 也会失败。需要重建容器时用路径 B 的 docker run(本机把 host 换成这台 GPU 机即可)。
容器内再确认:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_05.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_05.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec xinference bash -lc 'nvidia-smi -L'应列出 GPU 0: ...。这里报错就先修 NVIDIA Container Toolkit / --gpus,不要继续 launch。
② 管理员登录后 launch(必须带 --n-gpu,且必须 > 0)
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_06.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_06.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec xinference bash -lc '
set -e
xinference login -e http://127.0.0.1:9997 --username admin --password admin1234
xinference registrations -e http://127.0.0.1:9997 -t rerank
xinference launch -e http://127.0.0.1:9997 \
-n bge-reranker-v2-m3 \
-t rerank \
-u bge-reranker-v2-m3 \
--n-gpu auto
'只给一张卡、要写死时,把 --n-gpu auto 换成 --n-gpu 1。
等价 REST(仍须管理员身份,推理 xf- Key 会 403)。login 之后也可用 Web UI:Models → Launch Model → 搜 bge-reranker-v2-m3 → 类型 rerank → Model UID 设成 bge-reranker-v2-m3 → GPU 数量选 auto 或 1,不要填 0。
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_07.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_07.sh
#!/usr/bin/env bash
set -euo pipefail
# 仅在已具备「能 launch 的管理员 token」时使用;Dify 用的 xf- Key 不能发这条
curl -s -X POST 'http://127.0.0.1:9997/v1/models' \
-H 'Authorization: Bearer <管理员或可 launch 的 token>' \
-H 'Content-Type: application/json' \
-d '{
"model_uid": "bge-reranker-v2-m3",
"model_name": "bge-reranker-v2-m3",
"model_type": "rerank",
"n_gpu": "auto"
}'指定卡也可以写成 "device": "cuda:0"(与手册里 bge-m3 的 GPU 写法一致)。n_gpu 为 "auto" 或整数 1 均可;不要 "n_gpu": 0。
③ 确认真的在 GPU 上
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_08.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_08.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -H 'Authorization: Bearer xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI' \
http://127.0.0.1:9997/v1/modelsGPU 成功时,rerank 条目应类似:
{
"id": "bge-reranker-v2-m3",
"model_type": "rerank",
"model_name": "bge-reranker-v2-m3",
"model_engine": "sentence_transformers",
"accelerators": ["0"],
"replica": 1
}accelerators 仍是 [] 就还在 CPU,回到 ① 查容器是否挂上 GPU。同时看:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_09.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_09.sh
#!/usr/bin/env bash
set -euo pipefail
docker logs xinference 2>&1 | grep -E 'bge-reranker-v2-m3|cuda|CUDA|Launch finished'
nvidia-sminvidia-smi 里应出现对应 Python/Xinference 进程,显存有增加。然后再做步骤 4 的 /v1/rerank 打分。
路径 B · 把 Xinference 迁到 GPU 机 116.62.10.183
和安装手册步骤 9.1(bge-m3 上 GPU)同一思路:检索服务换机器,Dify 只改供应商 URL。Rerank 必须和 Embedding 在同一套 Xinference 上,否则 Dify 两个凭据各指一台,运维会裂开。
① 先确认 GPU 余量(关键前置,避免挤压业务)
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_10.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_10.sh
#!/usr/bin/env bash
set -euo pipefail
# 在 GPU 主机 116.62.10.183 上
nvidia-smi确认:Qwen(约 0.75 利用率)+ 业务(约 11GB)之外,还要给检索留显存。
| 迁什么 | 建议空闲显存 |
|---|---|
| 只迁 Rerank(Embedding 仍在 207 CPU) | 不推荐。Dify 两套 URL,重启后容易漏 launch |
| Embedding + Rerank 一起迁(推荐) | 合计预留 约 4~8GB(bge-m3 约 2~4GB + reranker 约 2~4GB) |
余量不够就不要迁,继续 207 CPU。共享 GPU 保护见专题九·三。
② 在 GPU 主机起 Xinference(必须 --gpus all)
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_11.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_11.sh
#!/usr/bin/env bash
set -euo pipefail
# GPU 版(在 116 上)
docker run -d --name xinference-gpu --restart always --gpus all -p 9997:9997 \
-e HF_ENDPOINT=https://hf-mirror.com \
xprobe/xinference:latest
docker exec xinference-gpu bash -lc 'nvidia-smi -L'
docker exec xinference-gpu pip install sentence-transformers
docker logs -f xinference-gpu
# 看到 Uvicorn running on http://0.0.0.0:9997 即就绪若 116 上已有同名容器,先 docker ps 看清楚,不要误删正在给别的系统服务的实例。端口 9997 被占用就换宿主机映射,Dify URL 跟着改。
③ 建管理员、生成推理 Key,再 login + launch 两个模型
新起的容器没有 207 上那套账号。按专题二先建 admin,再生成推理用 xf-... Key(这条 Key 仍然只能推理,不能 launch)。
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_12.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_12.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec xinference-gpu bash -lc '
set -e
xinference login -e http://127.0.0.1:9997 --username admin --password admin1234
xinference launch -e http://127.0.0.1:9997 \
-n bge-m3 -t embedding -u bge-m3 --n-gpu auto
xinference launch -e http://127.0.0.1:9997 \
-n bge-reranker-v2-m3 -t rerank -u bge-reranker-v2-m3 --n-gpu auto
'uid 必须仍是 bge-m3 和 bge-reranker-v2-m3,否则已有知识库/Agent 对不上。
若是把 207 的数据盘/账号迁过来,原 xf- Key 可以继续用;新起一套就必须换 Key。
④ 在 Dify 机 207 上确认能访问新端点
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_13.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_13.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -m 10 http://116.62.10.183:9997/v1/cluster/auth
# 若不通:116 防火墙放行 9997,两机要可路由带新 Key 列模型,应同时看到 embedding、rerank,且 accelerators 非空:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_14.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_14.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -H 'Authorization: Bearer <116上的xf-Key>' \
http://116.62.10.183:9997/v1/models再打一次 /v1/rerank(把步骤 4 的 URL 换成 http://116.62.10.183:9997/v1/rerank)。
⑤ 改 Dify 两处 Xinference 凭据(唯一必改项)
控制台 → 设置 → 模型供应商 → Xorbits Inference:
| 凭据 | 改什么 | 不要改 |
|---|---|---|
Embedding bge-m3 | 服务器 URL → http://116.62.10.183:9997(仍不带 /v1);若新容器则 API Key 换成 116 上新 Key | model_uid 仍为 bge-m3 |
Rerank bge-reranker-v2-m3 | 同上,URL(和必要时 API Key) | model_uid 仍为 bge-reranker-v2-m3 |
知识库向量 不必重建(仍是 1024 维)。知识库检索设置、Agent 快照里的 provider / model 名也不必改,它们认的是 Dify 供应商里的模型名,不是 IP。
等价 API(Rerank 凭据;Embedding 同理改 server_url):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_03B_改Dify凭据_GPU路径.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_03B_改Dify凭据_GPU路径.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 3.2 路径 B:把 Dify 的 Xinference URL 改到 GPU 机(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def patch_credentials(d: Dify, model: str, model_type: str, server_url: str, api_key: str) -> None:
body = {
"model": model,
"model_type": model_type,
"credentials": {
"server_url": server_url,
"model_uid": model,
"api_key": api_key,
"invoke_timeout": "60",
"max_retries": "3",
},
"name": model,
}
path = "/workspaces/current/model-providers/%s/models/credentials" % C.XINFERENCE_PROVIDER
r = d.post(path, body)
pp("POST %s %s" % (model_type, model), r)
if r.get("code") not in ("200", "201"):
r = d.req("PUT", path, body=body)
pp("PUT %s %s" % (model_type, model), r)
def main() -> int:
gpu_key = C.XF_KEY # 新起 GPU 容器时换成 116 上生成的 xf- Key
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
print("=== 修改前 URL(207 CPU)===")
print("embedding/rerank server_url:", C.XF_URL)
print("\n=== 修改后 URL(116 GPU)===")
print("embedding/rerank server_url:", C.XF_GPU_URL)
patch_credentials(d, C.EMBED_MODEL, "text-embedding", C.XF_GPU_URL, gpu_key)
patch_credentials(d, C.RERANK_MODEL, "rerank", C.XF_GPU_URL, gpu_key)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())⑥ 切换前后对照
| 位置 | 本次 207 CPU | GPU 生产(路径 B) |
|---|---|---|
Xinference docker run | 在 207,无 --gpus | 在 116,--gpus all,容器名示例 xinference-gpu |
launch --n-gpu | 省略(禁止 0) | auto 或 1 |
模型列表 accelerators | [] | 非空,如 ["0"] |
| Dify 供应商 URL | http://192.168.110.207:9997 | http://116.62.10.183:9997 |
model_uid | bge-m3 / bge-reranker-v2-m3 | 不变 |
| 知识库索引 | 已建 | 无需重建 |
| GPU 显存 | 0 | Embedding+Rerank 合计约 +4~8GB |
vLLM / vllm_qwen_run.sh | 不动 | 不动 |
迁完后:停掉 207 上旧的 embedding/rerank 实例(或停旧容器),避免 Dify 改 URL 不彻底时两套模型同时活着、难以判断打到哪边。116 上 Xinference 重启后,按 ③ 重新 login + 两条 launch。
之后步骤 4~11 在 GPU 路径上同样要做;只是 /v1/rerank 和 Dify server_url 指向 GPU 机。
步骤 4 · 直接打 /v1/rerank 确认模型真能打分
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_15.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_15.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -m 60 \
-H 'Authorization: Bearer xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI' \
-H 'Content-Type: application/json' \
-d '{
"model": "bge-reranker-v2-m3",
"query": "法定假日加班怎么算工资",
"documents": [
"法定节假日加班的,加班费 = 固定工资÷应出勤天数×300% ×加班天数。",
"公司班车发车时间以通知为准。",
"员工请事假须提前申请。"
]
}' \
http://127.0.0.1:9997/v1/rerank本次返回(节选):
{
"results": [
{"index": 0, "relevance_score": 0.9980502128601074, "document": null},
{"index": 1, "relevance_score": 0.00312977796420455, "document": null},
{"index": 2, "relevance_score": 0.00271617597900331, "document": null}
]
}第 0 条(300% 公式)接近 1,另外两条接近 0。模型可用。document 为 null 不影响 Dify:插件主要用 index 和 relevance_score。
Dify 插件调用等价于:
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.py
handle = RESTfulRerankModelHandle(model_uid, server_url, auth_headers)
response = handle.rerank(
documents=docs,
query=query,
top_n=top_n,
return_documents=True,
)保存凭据时,插件会用这段样例做校验(必须 launch 成功后才能添加):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.py
self.invoke(
model=model,
credentials=credentials,
query="Whose kasumi",
docs=[
"Kasumi is a girl's name of Japanese origin meaning \"mist\".",
"Her music is a kawaii bass, a mix of future bass, pop, and kawaii music ",
"and she leads a team named PopiParty.",
],
score_threshold=0.8,
)步骤 5 · 把 Rerank 模型加进 Dify(自定义模型凭据)
控制台路径:设置 → 模型供应商 → Xorbits Inference → 添加模型 → 类型选 Rerank。
这一页在 工作区「设置 / 模型供应商」,不是新 Agent 工作室里。工作室里找不到 Rerank 开关是正常的。本次添加凭据走的是下面的 API(HTTP 201),UI 只作对照。
字段:
| 项 | 值 |
|---|---|
| 模型名称 | bge-reranker-v2-m3 |
| Server url | http://192.168.110.207:9997(不要带 /v1) |
| Model UID | bge-reranker-v2-m3 |
| API Key | xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI |
| invoke timeout | 60 |
| max retries | 3 |
等价 API(本次 HTTP 201):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_05_添加Rerank凭据.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_05_添加Rerank凭据.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 5:把 Rerank 模型加进 Dify 供应商凭据。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
body = {
"model": C.RERANK_MODEL,
"model_type": "rerank",
"credentials": {
"server_url": C.XF_URL,
"model_uid": C.RERANK_MODEL,
"api_key": C.XF_KEY,
"invoke_timeout": "60",
"max_retries": "3",
},
"name": C.RERANK_MODEL,
}
r = d.post(
"/workspaces/current/model-providers/%s/models/credentials" % C.XINFERENCE_PROVIDER,
body,
)
pp("add rerank cred", r)
r2 = d.get("/workspaces/current/models/model-types/rerank")
pp("rerank models", r2)
return 0 if r.get("code") in ("200", "201") else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())成功后 model-types/rerank 里应有:
{
"provider": "langgenius/xinference/xinference",
"models": [
{
"model": "bge-reranker-v2-m3",
"model_type": "rerank",
"fetch_from": "customizable-model",
"status": "active"
}
]
}若 POST 不是 200/201,可再试同路径 PUT。本次 POST 已成功,不必 PUT。
步骤 6 · 改知识库检索设置:开启 Rerank,Top-K=5
控制台:知识库 → 播城内部行政管理知识库 → 检索设置 / 召回设置:
- 检索方法:语义检索(semantic_search)
- 开启 Rerank
- 模型:
bge-reranker-v2-m3(Xinference) - Top-K:5
- 不启用 score 阈值(除非你明确要滤掉低分)
等价 API:
改动对比(知识库 retrieval_model)
| 字段 | 修改前 | 修改后 |
|---|---|---|
reranking_enable | false | true |
reranking_mode | null | "reranking_model" |
reranking_model | 两个 null | provider + bge-reranker-v2-m3 |
top_k | 3 | 5 |
修改前
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改前.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改前.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 6 · 修改前:只读知识库 retrieval_model(不要 PATCH)。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.get("/datasets/%s" % C.DS_ID)
pp("dataset before patch", r)
retr = (r.get("json") or {}).get("retrieval_model_dict") or {}
print("\n期望修改前关键字段:")
print(json.dumps({
"reranking_enable": retr.get("reranking_enable"),
"top_k": retr.get("top_k"),
"reranking_mode": retr.get("reranking_mode"),
"reranking_model": retr.get("reranking_model"),
}, ensure_ascii=False, indent=2))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())修改后
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改后.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改后.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 6 · 修改后:PATCH 知识库,开启 Rerank,Top-K=5。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def build_retrieval_after() -> dict:
return {
"search_method": "semantic_search",
"reranking_enable": True,
"reranking_mode": "reranking_model",
"reranking_model": {
"reranking_provider_name": C.XINFERENCE_PROVIDER,
"reranking_model_name": C.RERANK_MODEL,
},
"top_k": 5,
"score_threshold_enabled": False,
"score_threshold": None,
}
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
ds_retrieval = build_retrieval_after()
r = d.req("PATCH", "/datasets/%s" % C.DS_ID, body={"retrieval_model": ds_retrieval})
print("HTTP", r.get("code"))
j = r.get("json") or {}
print("retrieval_model_dict:", j.get("retrieval_model_dict"))
pp("patch dataset", r)
return 0 if r.get("code") == "200" else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())本次 PATCH HTTP 200,回读:
{
"search_method": "semantic_search",
"reranking_enable": true,
"reranking_mode": "reranking_model",
"reranking_model": {
"reranking_provider_name": "langgenius/xinference/xinference",
"reranking_model_name": "bge-reranker-v2-m3"
},
"top_k": 5,
"score_threshold_enabled": false,
"score_threshold": null
}对应后端:PATCH /console/api/datasets/<dataset_id>,body 字段 retrieval_model(DatasetUpdatePayload)。
步骤 7 · 同步改新 Agent 的快照和草稿(对话真正生效的地方)
路径:config_snapshot -> knowledge -> sets -> [0] -> retrieval。
两张表都要改:
| 表 | ID | 作用 |
|---|---|---|
agent_config_snapshots | 01a0135c-320e-79dc-97a0-94da9b904c13 | 运行时 |
agent_config_drafts | 01a012e7-bf60-7673-a894-64551d984a23 | 配置页草稿 |
目标 retrieval:
{
"mode": "multiple",
"model": null,
"top_k": 5,
"weights": {
"weight_type": "customized",
"vector_setting": {
"vector_weight": 1,
"embedding_model_name": "bge-m3",
"embedding_provider_name": "langgenius/xinference/xinference"
},
"keyword_setting": {
"keyword_weight": 0
}
},
"reranking_mode": "reranking_model",
"reranking_model": {
"provider": "langgenius/xinference/xinference",
"model": "bge-reranker-v2-m3"
},
"score_threshold": null,
"reranking_enable": true
}注意:AgentKnowledgeRetrievalConfig 是 extra=forbid,不要塞知识库那种 reranking_provider_name 字段,保存/运行可能直接拒。
推荐做法:先备份整段 JSON,用 Python 改对象再写回(比多层 jsonb_set 不容易漏字段)。
改动对比(Agent knowledge.sets[0].retrieval)
| 字段 | 修改前 | 修改后 |
|---|---|---|
top_k | 4 | 5 |
reranking_enable | false | true |
reranking_mode | "weighted_score" | "reranking_model" |
reranking_model | null | {provider, model}(不是知识库那套字段名) |
修改前
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改前.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改前.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 7 · 修改前:备份 Agent 快照/草稿 retrieval(只读)。"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def load_cfg(d: Dify, table: str, cid: str) -> dict:
out, err = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot FROM %s WHERE id='%s'\"" % (table, cid)
)
raw = out.strip()
if not raw:
raise RuntimeError(err or "empty config_snapshot")
return json.loads(raw)
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
snap = load_cfg(d, "agent_config_snapshots", C.SNAP_ID)
draft = load_cfg(d, "agent_config_drafts", C.DRAFT_ID)
bak_dir = Path(__file__).resolve().parent / "backup"
bak_dir.mkdir(exist_ok=True)
for name, cfg in [("snap_before", snap), ("draft_before", draft)]:
p = bak_dir / ("专题十三_07_%s.json" % name)
p.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8")
retr = cfg.get("knowledge", {}).get("sets", [{}])[0].get("retrieval", {})
print(name, "retrieval:", json.dumps(retr, ensure_ascii=False))
print(" -> 已备份", p)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())修改后
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改后.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改后.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 7 · 修改后:同步改 Agent 快照与草稿的 knowledge.sets[0].retrieval。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def load_cfg(d: Dify, table: str, cid: str) -> dict:
out, err = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot FROM %s WHERE id='%s'\"" % (table, cid)
)
raw = out.strip()
if not raw:
raise RuntimeError(err or "empty config_snapshot")
return json.loads(raw)
def save_cfg(d: Dify, table: str, cid: str, cfg: dict, tag: str) -> None:
sql = "UPDATE %s SET config_snapshot = $cfg$%s$cfg$ WHERE id='%s';" % (
table,
json.dumps(cfg, ensure_ascii=False),
cid,
)
remote = "/tmp/upd_%s.sql" % tag
with d.sftp.open(remote, "w") as f:
f.write(sql)
out, err = d.sh("docker exec -i docker-db_postgres-1 psql -U postgres -d dify < %s" % remote)
print(table, (out or err).strip())
def enable_agent_rerank(cfg: dict) -> dict:
retr = cfg["knowledge"]["sets"][0].setdefault("retrieval", {})
retr["top_k"] = 5
retr["reranking_enable"] = True
retr["reranking_mode"] = "reranking_model"
retr["reranking_model"] = {
"provider": C.XINFERENCE_PROVIDER,
"model": C.RERANK_MODEL,
}
return cfg
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
snap = load_cfg(d, "agent_config_snapshots", C.SNAP_ID)
draft = load_cfg(d, "agent_config_drafts", C.DRAFT_ID)
save_cfg(d, "agent_config_snapshots", C.SNAP_ID, enable_agent_rerank(snap), "snap_rerank")
save_cfg(d, "agent_config_drafts", C.DRAFT_ID, enable_agent_rerank(draft), "draft_rerank")
out, _ = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot::json -> 'knowledge' -> 'sets' -> 0 -> 'retrieval' "
"FROM agent_config_snapshots WHERE id='%s';\"" % C.SNAP_ID
)
print("验证 retrieval:", out.strip())
print("\n[WARN] 改完不要点「构建/发布」")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())成功应看到两次 UPDATE 1。
验证:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_16.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_16.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec docker-db_postgres-1 psql -U postgres -d dify -c "
SELECT config_snapshot::json -> 'knowledge' -> 'sets' -> 0 -> 'retrieval'
FROM agent_config_snapshots
WHERE id = '01a0135c-320e-79dc-97a0-94da9b904c13';
"改完不要点「构建 / 发布」。 新 Agent 发布可能把 max_tokens 写回 null(专题十一、十二)。Rerank 改库后直接强刷页面、新开对话测试即可。
步骤 8 · 开启后命中测试(看召回名次变了没有)
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_08_命中测试_修改后.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_08_命中测试_修改后.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 8:开启 Rerank 后的命中测试(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
QUERY = "法定假日加班的制度是什么,加班费怎么算?"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.post("/datasets/%s/hit-testing" % C.DS_ID, body={"query": QUERY})
pp("hit-testing after rerank", r)
records = r.get("json")
if not isinstance(records, list):
records = (records or {}).get("records") or []
print("\n--- 前 5 条(分数为 Rerank relevance_score)---")
for i, rec in enumerate(records[:5], 1):
seg = (rec.get("segment") or {}).get("content") or ""
print(i, rec.get("score"), seg[:120].replace("\n", " "))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())本次开启后(分数已是 Rerank 的 relevance_score,和开启前的向量分不是同一量纲):
| 名次 | 分数 | 片段大意 |
|---|---|---|
| 1 | 0.947 | 11.1.6 加班工资计算方法(150% / 200% / 300%) |
| 2 | 0.844 | 10、法定节假日管理规定 |
| 3 | 0.611 | 节假日加班认定、续假/旷工 |
| 4 | 0.507 | 11.1.4 加班管理规定(审批、钉钉) |
| 5 | 0.221 | 打卡认定、加班纪律 |
对比步骤 2:300% 计费条款从靠后升到第 1,放假天数规定降到第 2。这就是 Rerank 的效果。
步骤 9 · 新开对话做 Agent 验收
不要用专题十二里已经 error 的旧会话。新开对话,问:
法定假日加班的制度是什么,加班费怎么算?控制台 API(本次 HTTP 200,messages.status=normal):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_09_Agent验收对话.py
在 207 上运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_09_Agent验收对话.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 9:新开 Agent 对话验收(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
QUERY = "法定假日加班的制度是什么,加班费怎么算?"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
payload = {
"inputs": {},
"query": QUERY,
"response_mode": "streaming",
"files": [],
"conversation_id": "",
}
r = d.post(
"/agent/%s/chat-messages" % C.AGENT_ID,
body=payload,
extra="-m 420 --max-time 420",
t=450,
)
print("HTTP", r.get("code"))
text = r.get("text") or ""
print(text[-1500:] if len(text) > 1500 else text)
if r.get("stderr"):
print("[stderr]", r["stderr"][:400])
return 0 if r.get("code") == "200" else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())本次答案要点(已实际生成):
- 须公司安排才认定为法定节假日加班;自行上班不算。
- 钉钉提交加班流程;要打卡。
- 加班费公式:
法定节假日加班费 = 固定工资 ÷ 应出勤天数 × 300% × 加班天数。 - 也可申请调休,选择调休则不补加班费差额。
流式结束事件里 metadata.retriever_resources 本次是空数组 [],不代表没检索。新 Agent 这条字段经常不填,以命中测试和答案内容为准。
Token 实录:prompt_tokens=7325,completion_tokens=1065,耗时约 23s。Prompt 已包含检索进的制度原文。
步骤 10 · 在 Langfuse 里核对这轮 Trace
Worker 日志应有:
Task tasks.ops_trace_task.process_trace_tasks ... Processing trace tasks success查 Trace:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_17.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_17.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -u 'pk-lf-84a76d89-b6b4-4943-b4ea-2dee48d4cda7:sk-lf-24aa6d73-ed36-4447-9eb6-3ebd446fb298' \
'http://127.0.0.1:3000/api/public/traces?limit=8'本次对应该问句的 Trace ID:45b0e430-e592-45c2-a582-b94b7ff63a70
打开:
http://192.168.110.207:3000/project/default/traces/45b0e430-e592-45c2-a582-b94b7ff63a70
能看到用户原话、模型完整回答(含 300% 公式)、Token。Generation 的 Input 目前只有用户那一句,没有逐条 chunk 列表。要看「召回片段名次变了」,回到步骤 2 和步骤 8 的命中测试对比。
步骤 11 · 收尾检查单
- [ ]
GET /v1/models同时有bge-m3和bge-reranker-v2-m3 - [ ] 走 CPU 时 rerank 的
accelerators为空;走 GPU 时非空,且nvidia-smi能看到对应进程 - [ ] Dify
model-types/rerank里模型active - [ ] 知识库
retrieval_model_dict.reranking_enable=true,top_k=5 - [ ] 快照和草稿的
knowledge.sets[0].retrieval均为reranking_enable=true、top_k=5、reranking_model.provider/model填对 - [ ] 命中测试第 1 条是计费/直接答题条款,而不是只命中章节标题
- [ ] 新开对话能答出关键数字
- [ ] Langfuse 有对应 Trace
- [ ] 没有点发布
本专题 Python 程序一览
| 文件 | 用途 | |---|---|| 专题十三_01_确认现状.py | 专题十三 · 步骤 1:确认现状(Rerank 开启前的基线)。 | | 专题十三_02_命中测试_修改前.py | 专题十三 · 步骤 2:开启 Rerank 前的命中测试(对照基线)。 | | 专题十三_03B_改Dify凭据_GPU路径.py | 专题十三 · 步骤 3.2 路径 B:把 Dify 的 Xinference URL 改到 GPU 机(修改后)。 | | 专题十三_05_添加Rerank凭据.py | 专题十三 · 步骤 5:把 Rerank 模型加进 Dify 供应商凭据。 | | 专题十三_06_改知识库检索_修改前.py | 专题十三 · 步骤 6 · 修改前:只读知识库 retrieval_model(不要 PATCH)。 | | 专题十三_06_改知识库检索_修改后.py | 专题十三 · 步骤 6 · 修改后:PATCH 知识库,开启 Rerank,Top-K=5。 | | 专题十三_07_改Agent快照_修改前.py | 专题十三 · 步骤 7 · 修改前:备份 Agent 快照/草稿 retrieval(只读)。 | | 专题十三_07_改Agent快照_修改后.py | 专题十三 · 步骤 7 · 修改后:同步改 Agent 快照与草稿的 knowledge.sets[0].retrieval。 | | 专题十三_08_命中测试_修改后.py | 专题十三 · 步骤 8:开启 Rerank 后的命中测试(修改后)。 | | 专题十三_09_Agent验收对话.py | 专题十三 · 步骤 9:新开 Agent 对话验收(修改后)。 |
专题十三_01_确认现状.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_01_确认现状.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_01.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 1:确认现状(Rerank 开启前的基线)。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
print("=== 1/4 Xinference 容器与内存(在 207 上 ssh 执行 docker 命令)===")
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
for cmd in [
"docker ps --filter name=%s --format '{{.Names}} {{.Status}} {{.Ports}}'" % C.XF_CONTAINER,
"docker stats %s --no-stream --format '{{.MemUsage}} CPU={{.CPUPerc}}'" % C.XF_CONTAINER,
"free -h",
]:
out, err = d.sh(cmd)
print(out or err)
print("\n=== 2/4 Xinference 已加载模型(应只有 bge-m3 embedding)===")
out, err = d.sh(
"curl -s -H 'Authorization: Bearer %s' %s/v1/models" % (C.XF_KEY, C.XF_URL)
)
print(out or err)
try:
models = json.loads(out)
for m in models.get("data", models if isinstance(models, list) else []):
if isinstance(m, dict):
print(" -", m.get("id"), m.get("model_type"), m.get("accelerators"))
except Exception:
pass
print("\n=== 3/4 登录 Dify,读知识库 retrieval_model ===")
ok, _, csrf = d.login()
print("login ok:", ok, "csrf:", bool(csrf))
r = d.get("/datasets/%s" % C.DS_ID)
pp("dataset retrieval_model_dict", r)
retr = (r.get("json") or {}).get("retrieval_model_dict") or {}
if retr.get("reranking_enable"):
print("[WARN] 知识库已开 Rerank,不是「修改前」基线")
print("\n=== 4/4 Agent 快照 knowledge.sets[0].retrieval + rerank 模型列表 ===")
sql = (
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot::jsonb -> 'knowledge' -> 'sets' -> 0 -> 'retrieval' "
"FROM agent_config_snapshots WHERE id='%s';\"" % C.SNAP_ID
)
out, err = d.sh(sql)
print("agent retrieval:", out.strip() or err)
r2 = d.get("/workspaces/current/models/model-types/rerank")
pp("model-types/rerank(期望 data 为空)", r2)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_02_命中测试_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_02_命中测试_修改前.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_02.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 2:开启 Rerank 前的命中测试(对照基线)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
QUERY = "法定假日加班的制度是什么"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.post(
"/datasets/%s/hit-testing" % C.DS_ID,
body={
"query": QUERY,
"retrieval_model": {
"search_method": "semantic_search",
"reranking_enable": False,
"top_k": 5,
"score_threshold_enabled": False,
},
},
)
pp("hit-testing before rerank", r)
records = r.get("json")
if not isinstance(records, list):
records = (records or {}).get("records") or []
print("\n--- 前 5 条 ---")
for i, rec in enumerate(records[:5], 1):
seg = (rec.get("segment") or {}).get("content") or ""
print(i, rec.get("score"), seg[:80].replace("\n", " "))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_03B_改Dify凭据_GPU路径.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_03B_改Dify凭据_GPU路径.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 3.2 路径 B:把 Dify 的 Xinference URL 改到 GPU 机(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def patch_credentials(d: Dify, model: str, model_type: str, server_url: str, api_key: str) -> None:
body = {
"model": model,
"model_type": model_type,
"credentials": {
"server_url": server_url,
"model_uid": model,
"api_key": api_key,
"invoke_timeout": "60",
"max_retries": "3",
},
"name": model,
}
path = "/workspaces/current/model-providers/%s/models/credentials" % C.XINFERENCE_PROVIDER
r = d.post(path, body)
pp("POST %s %s" % (model_type, model), r)
if r.get("code") not in ("200", "201"):
r = d.req("PUT", path, body=body)
pp("PUT %s %s" % (model_type, model), r)
def main() -> int:
gpu_key = C.XF_KEY # 新起 GPU 容器时换成 116 上生成的 xf- Key
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
print("=== 修改前 URL(207 CPU)===")
print("embedding/rerank server_url:", C.XF_URL)
print("\n=== 修改后 URL(116 GPU)===")
print("embedding/rerank server_url:", C.XF_GPU_URL)
patch_credentials(d, C.EMBED_MODEL, "text-embedding", C.XF_GPU_URL, gpu_key)
patch_credentials(d, C.RERANK_MODEL, "rerank", C.XF_GPU_URL, gpu_key)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_05_添加Rerank凭据.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_05_添加Rerank凭据.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_04.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_04.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 5:把 Rerank 模型加进 Dify 供应商凭据。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
body = {
"model": C.RERANK_MODEL,
"model_type": "rerank",
"credentials": {
"server_url": C.XF_URL,
"model_uid": C.RERANK_MODEL,
"api_key": C.XF_KEY,
"invoke_timeout": "60",
"max_retries": "3",
},
"name": C.RERANK_MODEL,
}
r = d.post(
"/workspaces/current/model-providers/%s/models/credentials" % C.XINFERENCE_PROVIDER,
body,
)
pp("add rerank cred", r)
r2 = d.get("/workspaces/current/models/model-types/rerank")
pp("rerank models", r2)
return 0 if r.get("code") in ("200", "201") else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_06_改知识库检索_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改前.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_05.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_05.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 6 · 修改前:只读知识库 retrieval_model(不要 PATCH)。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.get("/datasets/%s" % C.DS_ID)
pp("dataset before patch", r)
retr = (r.get("json") or {}).get("retrieval_model_dict") or {}
print("\n期望修改前关键字段:")
print(json.dumps({
"reranking_enable": retr.get("reranking_enable"),
"top_k": retr.get("top_k"),
"reranking_mode": retr.get("reranking_mode"),
"reranking_model": retr.get("reranking_model"),
}, ensure_ascii=False, indent=2))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_06_改知识库检索_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_06_改知识库检索_修改后.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_06.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_06.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 6 · 修改后:PATCH 知识库,开启 Rerank,Top-K=5。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def build_retrieval_after() -> dict:
return {
"search_method": "semantic_search",
"reranking_enable": True,
"reranking_mode": "reranking_model",
"reranking_model": {
"reranking_provider_name": C.XINFERENCE_PROVIDER,
"reranking_model_name": C.RERANK_MODEL,
},
"top_k": 5,
"score_threshold_enabled": False,
"score_threshold": None,
}
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
ds_retrieval = build_retrieval_after()
r = d.req("PATCH", "/datasets/%s" % C.DS_ID, body={"retrieval_model": ds_retrieval})
print("HTTP", r.get("code"))
j = r.get("json") or {}
print("retrieval_model_dict:", j.get("retrieval_model_dict"))
pp("patch dataset", r)
return 0 if r.get("code") == "200" else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_07_改Agent快照_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改前.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_07.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_07.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 7 · 修改前:备份 Agent 快照/草稿 retrieval(只读)。"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def load_cfg(d: Dify, table: str, cid: str) -> dict:
out, err = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot FROM %s WHERE id='%s'\"" % (table, cid)
)
raw = out.strip()
if not raw:
raise RuntimeError(err or "empty config_snapshot")
return json.loads(raw)
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
snap = load_cfg(d, "agent_config_snapshots", C.SNAP_ID)
draft = load_cfg(d, "agent_config_drafts", C.DRAFT_ID)
bak_dir = Path(__file__).resolve().parent / "backup"
bak_dir.mkdir(exist_ok=True)
for name, cfg in [("snap_before", snap), ("draft_before", draft)]:
p = bak_dir / ("专题十三_07_%s.json" % name)
p.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8")
retr = cfg.get("knowledge", {}).get("sets", [{}])[0].get("retrieval", {})
print(name, "retrieval:", json.dumps(retr, ensure_ascii=False))
print(" -> 已备份", p)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_07_改Agent快照_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_07_改Agent快照_修改后.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_08.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_08.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 7 · 修改后:同步改 Agent 快照与草稿的 knowledge.sets[0].retrieval。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def load_cfg(d: Dify, table: str, cid: str) -> dict:
out, err = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot FROM %s WHERE id='%s'\"" % (table, cid)
)
raw = out.strip()
if not raw:
raise RuntimeError(err or "empty config_snapshot")
return json.loads(raw)
def save_cfg(d: Dify, table: str, cid: str, cfg: dict, tag: str) -> None:
sql = "UPDATE %s SET config_snapshot = $cfg$%s$cfg$ WHERE id='%s';" % (
table,
json.dumps(cfg, ensure_ascii=False),
cid,
)
remote = "/tmp/upd_%s.sql" % tag
with d.sftp.open(remote, "w") as f:
f.write(sql)
out, err = d.sh("docker exec -i docker-db_postgres-1 psql -U postgres -d dify < %s" % remote)
print(table, (out or err).strip())
def enable_agent_rerank(cfg: dict) -> dict:
retr = cfg["knowledge"]["sets"][0].setdefault("retrieval", {})
retr["top_k"] = 5
retr["reranking_enable"] = True
retr["reranking_mode"] = "reranking_model"
retr["reranking_model"] = {
"provider": C.XINFERENCE_PROVIDER,
"model": C.RERANK_MODEL,
}
return cfg
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
snap = load_cfg(d, "agent_config_snapshots", C.SNAP_ID)
draft = load_cfg(d, "agent_config_drafts", C.DRAFT_ID)
save_cfg(d, "agent_config_snapshots", C.SNAP_ID, enable_agent_rerank(snap), "snap_rerank")
save_cfg(d, "agent_config_drafts", C.DRAFT_ID, enable_agent_rerank(draft), "draft_rerank")
out, _ = d.sh(
"docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
"\"SELECT config_snapshot::json -> 'knowledge' -> 'sets' -> 0 -> 'retrieval' "
"FROM agent_config_snapshots WHERE id='%s';\"" % C.SNAP_ID
)
print("验证 retrieval:", out.strip())
print("\n[WARN] 改完不要点「构建/发布」")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_08_命中测试_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_08_命中测试_修改后.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_09.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_09.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 8:开启 Rerank 后的命中测试(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
QUERY = "法定假日加班的制度是什么,加班费怎么算?"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.post("/datasets/%s/hit-testing" % C.DS_ID, body={"query": QUERY})
pp("hit-testing after rerank", r)
records = r.get("json")
if not isinstance(records, list):
records = (records or {}).get("records") or []
print("\n--- 前 5 条(分数为 Rerank relevance_score)---")
for i, rec in enumerate(records[:5], 1):
seg = (rec.get("segment") or {}).get("content") or ""
print(i, rec.get("score"), seg[:120].replace("\n", " "))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十三_09_Agent验收对话.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_09_Agent验收对话.py
完整补丁:patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_10.py
运行/引用:见 patches_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_10.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十三 · 步骤 9:新开 Agent 对话验收(修改后)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
QUERY = "法定假日加班的制度是什么,加班费怎么算?"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
payload = {
"inputs": {},
"query": QUERY,
"response_mode": "streaming",
"files": [],
"conversation_id": "",
}
r = d.post(
"/agent/%s/chat-messages" % C.AGENT_ID,
body=payload,
extra="-m 420 --max-time 420",
t=450,
)
print("HTTP", r.get("code"))
text = r.get("text") or ""
print(text[-1500:] if len(text) > 1500 else text)
if r.get("stderr"):
print("[stderr]", r["stderr"][:400])
return 0 if r.get("code") == "200" else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())第三部分 本次实战问题与解决方案
这一节只写 本次 207 上真实碰到的问题,按发生顺序。不是清单式猜测。
问题 1 · 不带 Key 列模型:Could not validate credentials
现象:curl http://127.0.0.1:9997/v1/models 返回 401/校验失败。
原因:Xinference 开了鉴权(GET /v1/cluster/auth → {"auth":true})。
解决:所有推理请求加:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_18.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_18.sh
#!/usr/bin/env bash
set -euo pipefail
-H 'Authorization: Bearer xf-K8H3LiS27H4188XdOyWJj74MWMOlIR9tZbiybDbxbSas7eAI'问题 2 · 用推理 Key 做 launch:403
现象:
RuntimeError: Failed to launch model, detail: API keys can only access model query and inference endpoints日志:POST /v1/models?wait_ready=False HTTP/1.1" 403
原因:Dify 里填的 xf-... 是推理 Key,权限仅查询和推理,不能创建模型实例。
解决:在容器内用管理员登录后再 launch:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_19.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_19.sh
#!/usr/bin/env bash
set -euo pipefail
xinference login -e http://127.0.0.1:9997 --username admin --password admin1234
xinference launch ...不要把管理员密码配进 Dify。Dify 继续用推理 Key 调用 /v1/rerank 即可。
问题 3 · 未登录就 launch:Cannot find access token
现象:容器里直接 xinference launch,不带 -ak:
RuntimeError: Cannot find access token, please login first!原因:CLI 会先读本地登录 token;没 login 就没有 token。
解决:同一 docker exec bash -lc 里先 xinference login 再 launch,保证 token 写在同一次会话的 home 里。
问题 4 · --n-gpu 0 被拒绝
现象:
ValueError: The parameter `n_gpu` must be greater than 0 and not greater than the number of GPUs: 0 on the machine.CLI 进度条会假成功打出 Model uid: bge-reranker-v2-m3,但 GET /v1/models 里没有 rerank。
原因:207 没有 GPU。--n-gpu 0 被解析成非法:必须 > 0,同时机器 GPU 数是 0,怎么填都不成立。
解决(按有没有 GPU 分开):
无 GPU(本次 207):不要传 --n-gpu。与已经跑着的 bge-m3 一样走 auto,结果 accelerators: [],即 CPU。
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_20.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_20.sh
#!/usr/bin/env bash
set -euo pipefail
# 错误:无 GPU 机器上传 0 会被拒绝
xinference launch ... --n-gpu 0
# 正确:207 CPU,省略该参数
xinference launch -e http://127.0.0.1:9997 -n bge-reranker-v2-m3 -t rerank -u bge-reranker-v2-m3有 GPU:容器必须已加 --gpus all,launch 要传且必须 > 0:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_21.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_21.sh
#!/usr/bin/env bash
set -euo pipefail
xinference launch -e http://127.0.0.1:9997 \
-n bge-reranker-v2-m3 -t rerank -u bge-reranker-v2-m3 \
--n-gpu auto
# 或 --n-gpu 1完整步骤见 3.2。不要在 GPU 机器上为了「和 207 一样」去省略 --gpus,也不要把 207 上那条「不要传 --n-gpu」抄到有卡的机器上当唯一正确写法。
问题 5 · 查询内置 rerank 列表的 URL 抄错
现象:
GET /v1/model_registrations?model_type=rerank→{"detail":"Not Found"}GET /v1/models/registrations?...→Model not found in the model list, uid: registrations
原因:Xinference 3.1 这条 REST 路径不是这样;CLI 会把 registrations 当成 model uid。
解决:用 CLI:
完整脚本:scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_22.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_22.sh
#!/usr/bin/env bash
set -euo pipefail
xinference login ...
xinference registrations -e http://127.0.0.1:9997 -t rerank问题 6 · 容器里没有 FlagEmbedding,会不会起不来
现象:启动前检查 FlagEmbedding 为 False,只有 sentence_transformers 5.7.0。
原因:有的教程按 FlagEmbedding 装依赖。Xinference 3.1 对 bge-reranker-v2-m3 实际用的 engine 是 sentence_transformers。
解决:先按步骤 3 直接 launch。本次拉权重并加载成功,不必先 pip install FlagEmbedding。若将来换 jina-reranker 或别的 engine 再按报错补依赖。
问题 7 · 只改知识库,Agent 对话仍不走 Rerank
现象:知识库页已显示开启 Rerank、Top-K=5,命中测试也变了,但对话检索仍像旧的。
原因:新 Agent 的召回读 agent_config_snapshots.config_snapshot.knowledge.sets[].retrieval,改前是 top_k=4、reranking_enable=false、reranking_mode=weighted_score。
解决:快照和草稿一起改,字段用 {provider, model},reranking_mode 改成 reranking_model。见步骤 7。
问题 8 · 两套 JSON 字段不能混用
现象:若把知识库的 reranking_provider_name 写进 Agent 配置,可能被 extra=forbid 拒绝。
原因:
- 知识库:
reranking_provider_name/reranking_model_name - Agent:
provider/model
运行时由 knowledge_retrieval_inner_service.py 做映射:
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十三_知识库Rerank重排序/专题十三_内联_03.py
"reranking_provider_name": request.retrieval.reranking_model.provider,
"reranking_model_name": request.retrieval.reranking_model.model,解决:改哪一层就用哪一层的字段名。不要两个结构对半混。
问题 9 · 改完点「发布」可能把 max_tokens 搞丢
现象:为了「让 Rerank 生效」去点发布,之后 Max Tokens 又回到默认或对话再超限。
原因:专题十一已确认,新 Agent UI 发布不一定把 model.model_settings.max_tokens 正确落库。专题十二刚把值改回 8192。
解决:Rerank 改库后只强刷、新开对话。不要发布。
问题 10 · Langfuse 里看不到逐条召回 chunk
现象:Trace 只有一条 Generation,Input 是用户原话;retriever_resources 为空。
原因:当前 Dify → Langfuse 上报对这条链路没有把每条检索片段做成独立 Observation;Generation Input 也被收成用户句。这和 Rerank 是否生效不是一回事。
解决:
- 看召回名次:知识库命中测试(步骤 2 vs 步骤 8)
- 看是否用上条款:Agent 答案是否出现 300% 等原文数字
- 看 Trace 是否记到本轮:Langfuse 链接与
messages.status=normal
不要因为 Langfuse 没列出 chunk 就再 launch 一次或重装插件。
问题 11 · CLI 显示 100% 但模型列表没有 rerank
现象:问题 4 时 CLI 已打印 Model uid: bge-reranker-v2-m3,列表仍只有 bge-m3。
原因:supervisor 接受了请求(wait_ready=False 先 200),worker 随后 launch 失败。CLI 的 100% 不是「已经可服务」。
解决:以 GET /v1/models 是否出现 model_type=rerank,以及日志 ModelActor(...) loaded 为准。失败时看 docker logs xinference 里 Leave launch_builtin_model, error:。
问题 12 · Xinference 重启后 Rerank 消失
现象:重启 xinference 容器后,Dify 命中测试/对话报 Rerank 不可用或校验失败。
原因:launch 的实例不随容器持久化(和 bge-m3 相同)。Dify 凭据还在,对面 uid 没了。
解决:按步骤 3 重新 login + launch,保持 uid 仍为 bge-reranker-v2-m3。不必重加 Dify 凭据,也不必重建知识库。Embedding 的 bge-m3 若一并没了,也要先 launch 回来,否则知识库会从 Agent 列表消失(专题三)。
附录 · 开启前后对照(本次实录)
| 项 | 开启前 | 开启后 |
|---|---|---|
| Xinference 模型 | 仅 bge-m3 | bge-m3 + bge-reranker-v2-m3 |
| 知识库 Top-K | 3 | 5 |
| 知识库 Rerank | 关 | 开,bge-reranker-v2-m3 |
| Agent Top-K | 4 | 5 |
| Agent Rerank | 关,weighted_score | 开,reranking_model |
| 命中第 1 条 | 法定节假日放假规定 | 加班工资 300% 公式 |
| 对话 | 问加班制度时条款容易偏、空 | 给出认定条件 + 300% 公式 + 调休规则 |
| 验收 Trace | — | 45b0e430-e592-45c2-a582-b94b7ff63a70 |
附录 · 结论
- Rerank 是第二段精排,不替代 bge-m3,也不替代大模型。
- 本次 207 复用 Xinference、CPU 跑
bge-reranker-v2-m3即可,不必上 TEI。有 GPU 且显存有余量时,按步骤 3.2 上 GPU(容器--gpus all,launch--n-gpu auto或1)。 - 必须同时改知识库
retrieval_model和新 Agent 的knowledge.sets[].retrieval,字段名两套。 - launch 用管理员登录,推理用
xf-Key。无 GPU 不要--n-gpu 0(直接省略);有 GPU 必须传auto或1。 - 改完不要发布。召回名次看命中测试,对话看新开会话,Trace 看 Langfuse。