Skip to content

feat(knowledge): add read-only RAGFlow retrieval - #4955

Merged
WillemJiang merged 14 commits into
bytedance:mainfrom
zhangwei-way:codex/ragflow-retrieval-only
Aug 25, 2026
Merged

feat(knowledge): add read-only RAGFlow retrieval#4955
WillemJiang merged 14 commits into
bytedance:mainfrom
zhangwei-way:codex/ragflow-retrieval-only

Conversation

@zhangwei-way

@zhangwei-way zhangwei-way commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an opt-in async httpx client for read-only RAGFlow retrieval
  • expose one Agent tool, knowledge_search(query); there is no Agent-visible tenant catalog tool
  • keep base_url, api_key, retrieval limits, and an optional stable dataset-ID allowlist on the normal knowledge_search tool entry
  • always send an explicit, non-empty dataset_ids list to POST /api/v1/retrieval
  • resolve configured IDs at invocation time and return ordinal configuration guidance when a binding is deleted or inaccessible
  • when datasets is omitted, list every tenant-visible dataset, skip empty datasets, and group searchable datasets by exact embedding-model identifier
  • retrieve at most four embedding groups concurrently, interleave provider-ranked results under one global page_size, and omit score labels on multi-group output because scores are not comparable across embedding spaces
  • skip an empty dataset with missing embedding metadata with a server warning; keep fail-loud behavior for a searchable dataset missing that metadata and for retrieval-group failures
  • keep catalog data and opaque dataset IDs out of model-visible output; redact the API key on every path and dataset UUIDs on error paths only
  • keep RAGFlow as the sole source of truth with no database, migration, Gateway management API, SSE watcher, or frontend changes

Configuration

- name: knowledge_search
  group: knowledge
  use: deerflow.community.ragflow.tools:knowledge_search_tool
  base_url: http://localhost:9380
  api_key: $RAGFLOW_API_KEY
  # Optional. Omit to search every dataset visible to this API key.
  datasets:
    - 0123456789abcdef0123456789abcdef
    - fedcba9876543210fedcba9876543210

datasets is an optional operator-controlled stable-ID allowlist. IDs remain valid when a dataset is renamed. If it is omitted, DeerFlow lists all pages at invocation time and explicitly forwards every searchable ID, grouped by embedding model. Configuration loading remains network-free. Dataset IDs and catalog listing are never exposed to the Agent.

API contract and live validation

The implementation follows RAGFlow's official List datasets and Retrieval contracts. RAGFlow v0.26.4 and v0.27.0 expose the normalized response fields consumed here and support the id filter used by the configured allowlist path.

Validated on 2026-08-25 against a real local infiniflow/ragflow:v0.27.0 stack, not a mock.

Missing configured dataset

A deliberately nonexistent configured ID produced the real RAGFlow response shape (code=102, provider text containing lacks permission). The tool converted it during dataset resolution to:

Error: The 1st entry of knowledge_search.datasets was not found or is inaccessible; check config.yaml.

The provider permission text and opaque ID were absent from model-visible output; the full configured ID and code=102 remained in the server warning log.

Empty embedding dataset and heterogeneous groups

The live tenant contained an empty dataset whose API representation was embedding_model: "" and chunk_count: 0. A temporary v2 dataset was then created, one document was uploaded and parsed to one chunk, and it was searched together with the existing v3 dataset through the default all-dataset path.

blank_dataset_state: embedding_empty, chunk_count_0
v2_document_state: parsed, chunks_1
grouped_recall_entries: 8
contains_v2_result: true
contains_v3_result: true
empty_embedding_dataset_skipped: true
multi_group_scores_hidden: true

Both temporary datasets were deleted after validation. A separate real negative control that omitted dataset_ids returned RAGFlow code=102, confirming that the field must always be explicit.

Tests

  • uv run pytest tests/test_ragflow_client.py tests/test_ragflow_tools.py -q — 43 passed
  • targeted Ruff check and format check — passed
  • make test — 12,348 passed, 78 skipped; two pre-existing environment-only failures remained outside this change: public GitHub DNS resolution and the developer checkout's local SQLite checkpointer/config isolation

@CLAassistant

CLAassistant commented Aug 23, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added area:backend Gateway / runtime / core backend under backend/ area:docs Documentation and Markdown only risk:medium Medium risk: regular code changes size/XL PR changes 700+ lines and removed area:backend Gateway / runtime / core backend under backend/ labels Aug 23, 2026
@zhangwei-way
zhangwei-way force-pushed the codex/ragflow-retrieval-only branch from d06a331 to 5341371 Compare August 23, 2026 04:08
@foreleven
foreleven requested a review from rayhpeng August 23, 2026 04:10

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at head 5341371. The overall shape is solid and follows the established community-tool conventions closely (get_tool_config + model_extra with a warned-once set mirrors brave; env refs like $RAGFLOW_API_KEY are resolved by the config loader's resolve_env_variables before validation, so the SecretStr flow works; hatchling ships the package; bounded output at chunk and response level; api-key and UUID redaction on every error path with tests). Four findings below - two suggestions on model-visible output, two convention/robustness nits. One design observation, not blocking: knowledge_search lists all datasets (paginated) before every retrieval to resolve names - fine for the no-state design, just be aware each search pays at least one extra HTTP roundtrip, and list_knowledge_bases depends on the knowledge_search entry being present even when only the listing tool is configured (the error message explains this, and the docs call it out).

Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
Comment thread backend/packages/harness/deerflow/community/ragflow/client.py Outdated

@rayhpeng rayhpeng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhangwei139623 感谢贡献。这个 PR 对应 RFC #4900 的 Phase 2(retrieval-only 切片),方向是社区呼声很高的功能(#3268 / #3302 / #2609)。测试覆盖和文档同步做得比较完整,API key 的双层 redaction(连 base_url 内嵌凭据的场景都覆盖了)考虑得很细;provider 连接配置放在 tool entry 上也正确地沿用了 web_search 的 provider 模式,这部分保持现状即可。不过存在一个正确性问题和一个设计问题,需要一轮较大的调整,逐条说明如下。

1. 全库兜底检索在 RAGFlow 的 /api/v1/retrieval 上不成立(blocking)

PR 将「省略 knowledge_bases 时不传 dataset_ids,由 RAGFlow 全库检索」作为受支持的路径,并固化在 docstring、README 和 test_retrieve_omits_dataset_ids_when_unspecified 中。但 POST /api/v1/retrieval 的第一个校验就是 dataset_ids 必填,且自 SDK HTTP API 引入以来的所有版本均如此(已核查 v0.13.0、v0.15.0、v0.17.2、v0.19.1、v0.21.1、v0.23.0、v0.25.0、v0.26.4):

if not req.get("dataset_ids"):
    return get_error_data_result("`dataset_ids` is required.")

需要说明一个容易混淆的点:RAGFlow 官方 API 文档对 dataset_ids 的描述是与 document_ids 二选一("If you do not set this argument, ensure that you set document_ids"),但源码实现比文档更严格——该检查位于 handler 第一行,此时尚未读取 document_ids,因此只传 document_ids 同样会被拒绝;document_ids 的实际语义是在给定 datasets 范围内的进一步过滤(chunk_api.py#L335-L345)。无论按文档口径还是实现口径,「两者都不传 = 全库检索」均不成立。

如果你手上有省略 dataset_ids 可以工作的部署,请给出具体的 RAGFlow 版本号和所测端点。无论如何,实现不应依赖这里的版本或文档口径差异:始终显式传 dataset_ids(见第 2 条,它将这一点结构化)。

另外说明:这个错误断言源自 RFC 本身。RFC §5.1 写了「省略 knowledge_bases 即全库检索」并声称已对 v0.26.4 验证——这条验证是失实的,属于 RFC 的缺陷,已在 #4900 中修订。但这也正说明了 mock 测试的局限:mock 验证的是我们对 API 的假设,只有真实调用才能验证假设本身。请在下一版对真实 RAGFlow 实例完整跑通工具链,并在 PR 描述中注明版本、验证命令与输出摘要。

同源的一个小问题:formatting.py 对 chunk 字段做了 kb_id/dataset_id 双备选兼容,但已核查的所有版本都会在返回前做显式改名(kb_id → dataset_iddoc_id → document_iddocnm_kwd → document_keyword),真实响应中不会出现 kb_id:https://github.com/infiniflow/ragflow/blob/v0.26.4/api/apps/restful_apis/chunk_api.py#L424-L433 。除非你能指出确实返回 kb_id 的版本,否则请写死字段并在注释中注明验证版本。

2. 在 tool 配置上显式绑定 dataset,取消动态目录(blocking)

结合第 1 条,这个切片的正确形态是:operator 在 knowledge_search 的 tool entry 上(与现有连接配置并列)按名字绑定一个或多个 dataset,工具总是解析这些名字并显式传 dataset_ids。理由:

  • 租户级 API key 可见租户下所有 dataset,当前动态 list_datasets 等于把整个租户目录无差别暴露给每个 Agent;显式绑定是 allowlist,由 operator 决定 Agent 的可读范围;
  • operator 可在配置时一次性保证所绑 dataset 的 embedding model 兼容,消除跨库检索的运行时报错(chunk_api.py#L325-L327);
  • 每次搜索减少一次串行的全量分页 list_datasets 调用(当前实现即使模型未指定库名也会先全量拉取目录用于格式化),且第 1 条从此结构性成立——dataset_ids 永远存在;
  • 为后续 per-agent / per-user 的 dataset 隔离铺路。

相应地,list_knowledge_bases 工具不再必要,应移除;绑定的库名直接写入 knowledge_search 的工具描述。这同时消除了当前的耦合——list_knowledge_bases 的 entry 是空壳、运行时借读 knowledge_search 的配置;调整后整个集成收敛为一个自足的 tool entry,与 web_search provider 完全同构,将来换成其他引擎(Dify 等)仍只是改一行 use:

说明:这与 RFC §5.1 的原方案相反——原方案否决配置绑定、选择动态目录,依据的正是第 1 条中的错误假设。该裁决已作为 RFC 修订记录在 #4900,本 PR 按修订后的方向实现即可。

dataset 存在性:配置加载时不做网络校验(config 校验不应有网络 IO);在调用时按名解析(GET /datasets?name= 支持按名过滤),解析失败则返回明确错误,提示绑定的 dataset 可能已被删除或改名、需检查 config.yaml。

3. 模型可见文本应使用英文(blocking)

client.py 抛出的异常消息与 tools.py 返回的错误串目前为中文(如「请求超时」「未检索到相关内容」)。community/ 下现有工具的模型可见错误均为英文("Error: ..." 形式),请保持一致。

4. Non-blocking

  • _RAGFLOW_UUID_PATTERN 会把模型可见文本中所有 32 位 hex / 标准 UUID 格式的内容替换为 [DATASET_ID],且该替换同样作用于 knowledge_search 的正常检索输出——检索正文中合法的 MD5 校验和、trace id、UUID 字面量会被破坏,引用保真度受损,替换标记还会误导模型将其理解为知识库 ID。正常路径上 formatting.py 已将 dataset UUID 映射为库名、并无泄漏面,该正则应收缩到仅错误路径(RAGFlow 的报错确实可能回显 dataset UUID,那里值得保留);
  • 每次请求新建 httpx.AsyncClient,无连接复用;v1 可接受,后续 Gateway 侧接入时再统一考虑;
  • 按第 2 条返工时,config_version 的改动请合并到同一版中,避免连续两次 bump。

Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
@github-actions github-actions Bot added the area:backend Gateway / runtime / core backend under backend/ label Aug 24, 2026
@zhangwei-way

Copy link
Copy Markdown
Contributor Author

@rayhpeng 已按这轮 review 和 RFC #4900 的 v2 修订完成返工,提交为 137a0b8a

  1. dataset_ids blocking:删除全库兜底;client 现在拒绝空 ID,request body 永远包含非空 dataset_ids。真实 RAGFlow v0.27.0 负向探针确认省略该字段返回 code=102;正向完整 tool 链路已跑通。
  2. 显式 dataset 绑定 blockingknowledge_search entry 新增必填 exact-name datasets allowlist;移除 list_knowledge_bases;每次调用并发执行按名过滤的 GET /datasets?name=...,精确匹配后检索;配置加载阶段无网络 I/O;删除/改名有明确英文 config.yaml 指引;绑定名写入模型可见 tool description。
  3. 英文 blocking:client 自有异常、tool 错误和 formatting 标签已统一为英文。
  4. 字段与脱敏:formatting 仅接受标准化的 dataset_id / document_id / document_keyword;UUID 规则只用于错误路径,成功内容中的合法 UUID/MD5/trace ID 保留;API key 仍在所有路径脱敏。
  5. 其余 review 点:新增显式 __init__.py;全量分页已从调用路径结构性移除;拒绝 plain/percent-encoded URL userinfo;每请求新建 AsyncClient 保持不变;config_version 保持 35,没有二次 bump。

PR 描述已更新真实版本、验证命令和输出摘要。定向测试 32 passed,harness 边界/打包 3 passed,完整 Ruff lint/format 通过。烦请复核。

@zhangwei-way

Copy link
Copy Markdown
Contributor Author

@rayhpeng Follow-up: the two remaining review-evidence gaps are now closed. formatting.py explicitly records validation against RAGFlow v0.26.4 and v0.27.0, and the PR description now includes the reproducible positive real-instance knowledge_search tool-chain command plus its redacted output. Revalidation: 35 targeted tests passed; Ruff check/format passed.

@github-actions github-actions Bot removed the area:backend Gateway / runtime / core backend under backend/ label Aug 24, 2026
@zhangwei-way

Copy link
Copy Markdown
Contributor Author

@rayhpeng 按最新反馈已将 knowledge_search.datasets 改为稳定 dataset ID allowlist(commit 1018bba9)。每次调用通过 GET /datasets?id=... 校验 ID 并获取当前名称用于引用,然后将同一组 ID 原样传给 retrieval;dataset 重命名不会破坏绑定,配置加载仍无网络 I/O,ID 不进入模型描述或检索输出。真实 RAGFlow v0.27.0 全工具链已按 ID 重新跑通,PR 描述已同步复现命令与脱敏输出;35 个定向/边界测试和完整 Ruff lint/format 均通过。

@zhangwei-way

Copy link
Copy Markdown
Contributor Author

@rayhpeng 已按后续意见补充默认兜底行为(commit f0af2362):

  • knowledge_search.datasets 现在是可选的稳定 ID allowlist;配置时仍逐 ID 校验并原样传给 retrieval。
  • 未配置时,工具内部通过官方 GET /api/v1/datasets 接口分页读取当前 API key 可见的全部 dataset,收集所有 ID 后显式传入 POST /api/v1/retrieval;不会省略 dataset_ids
  • 全量分页使用 page/page_size,优先按 total_datasets 终止、兼容短页终止,并设 100 页硬上限。
  • 目录和 ID 不会成为 Agent 工具或进入模型可见输出;工具描述、配置注释、README 和后端文档均已同步。
  • 新增分页、页数上限、默认全量传 ID、空目录提示和配置可选测试,RAGFlow 定向测试 36 passed。
  • 已在真实本地 RAGFlow v0.27.0 验证:列出 2 个 dataset,retrieval 收到相同的 2 个 ID,检索成功,API key 与 dataset IDs 均未出现在模型可见结果中。

PR 描述也已更新为新的可选配置和真实验证结果。

@rayhpeng

rayhpeng commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

@zhangwei139623 感谢持续跟进。经维护者侧讨论,当前版本的两个方向性设计予以确认:datasets 使用稳定 dataset ID 绑定,以及未配置时默认检索全部可见 dataset。这两点不用再改。但默认全量有一个必须处理的风险点,另有一个从 v1 遗留至今的正确性问题仍未修复,详见下文。先给我这边的真实环境验证结果。

E2E 验证(基于 1232af61,真实环境非 mock)

环境:RAGFlow v0.27.0 官方镜像完整栈;PR 分支代码,全程真实 HTTP,仅按正常 tool entry 形式注入配置;注册 OpenAI 兼容 embedding provider 后文档真实完成解析(chunk 化 + 向量化 + 索引);测试语料中故意埋入一个标准 UUID 和一个 32 位 MD5,用于验证脱敏不误伤正文。

# 场景 结果
T1 绑定 HR Policies 正向检索 [1] HR Policies / leave-policy.txt (score 0.44) + 正确内容
T2 检索正文中 UUID/MD5 保真 ✅ 均原样保留,error-only 脱敏有效
T3 绑定不存在的 dataset ❌ 见下文问题 2
T4 dataset UUID / API key 泄漏 ✅ 均未出现
T6 空 query 拦截
C1 裸 API:省略 dataset_ids code=102 "dataset_ids is required."
C2 裸 API:GET /datasets?name=<不存在> ⚠️ 返回 code=102 "User '...' lacks permission for dataset '...'",不是空列表,见问题 2

问题 1(blocking):默认全量检索必须说明并处理混合 embedding 失败

RAGFlow 拒绝跨 embedding 模型的检索——服务端对本次检索涉及的所有 dataset 取 embedding 模型集合,不唯一即直接报错(chunk_api.py#L325-L327):

embd_nms = list(set([split_model_name(kb.embd_id)[0] for kb in kbs]))

if len(embd_nms) != 1:

    return get_result(message="Datasets use different embedding models.", code=RetCode.DATA_ERROR)

RAGFlow 每个 dataset 建库时可独立选择 embedding 模型,租户运行一段时间后异构是常态。也就是说默认全量行为会随部署状态从"能用"退化为"每次检索必失败":单库正常,第二个异构库一建,所有未配置 datasets 的部署立即开始报 Datasets use different embedding models.。默认行为保留,但这个风险必须显式处理,要求:

  1. 文档 note:config.example.yaml 的配置注释、README、backend/docs/CONFIGURATION.md 三处明确说明——默认全量要求所有可见 dataset 使用相同 embedding 模型,异构租户必须配置 datasets 缩小范围;

  2. 运行时指引:默认全量路径下捕获该错误(按 RAGFlow 错误码,不要匹配消息文本),将模型可见错误包装为可操作指引,例如 Error: The accessible datasets use different embedding models; configure knowledge_search.datasets to a subset that shares one embedding model.——否则 operator 只能看到 RAGFlow 原文,不知道解法在配置里;

  3. 为该场景补一条测试(mock 按真实响应形状:code=102 + 该 message),并在真实实例上验证一次(建两个不同 embedding 的库即可复现)。

问题 2(blocking):missing-dataset 指引是死代码,已三个版本未修

实现假设"绑定的 dataset 不存在时,GET /datasets?id= 返回空列表",并据此设计了 "...check knowledge_search.datasets in config.yaml" 的指引。但真实 API 在 miss 时返回的是 code != 0 错误而非空列表——C2 是我在 v0.27.0 真实实例上的实测,id 分支与 name 分支是相邻的同款代码(v0.27.0 dataset_api_service.py#L433-L439):

if kb_id:

    kbs = KnowledgebaseService.get_kb_by_id(kb_id, tenant_id)

    if not kbs:

        return False, f"User '{tenant_id}' lacks permission for dataset '{kb_id}'"

因此 client 抛出的是 RAGFlowAPIError,走 _tool_error 通用分支,精心设计的配置指引从未触发。T3 实测的模型可见输出为:


Error: User '[DATASET_ID]' lacks permission for dataset 'Nonexistent Dataset E2E'

三个叠加的问题:指引死代码;RAGFlow 回显的 tenant id 被 UUID 正则误标为 [DATASET_ID](模型会误读为"某个 dataset 无权限");错误是权限口径,operator 得不到任何可操作信息。test_missing_bound_dataset_returns_operator_guidance 的 mock(返回空列表)固化的是真实 API 不会产生的行为——这一假设已以相同姿势在 v1(全库兜底)、v2(按名 miss)、v3(按 ID miss)三个版本漏网。

修复建议:

  • 解析阶段捕获 RAGFlowAPIError 并统一包装为 missing-dataset 指引(按阶段捕获,不要匹配错误消息文本——那是 RAGFlow 内部实现);

  • [DATASET_ID] 标记建议改为语义中性的 [REDACTED_ID]——该正则实际命中的是任意 32 位 hex id(本例中是 tenant id);

  • 测试 mock 改为真实响应形状(code=102 错误),并在真实实例上补一条"绑定已删除/不存在的 dataset ID"的验证,验收标准:工具返回指向 knowledge_search.datasets 的配置指引。今后凡涉及 RAGFlow 响应形状的断言,请一律以真实调用为准——mock 只能验证我们对 API 的假设,不能验证假设本身。

Non-blocking

  • 绑定 ID 后模型可见的工具描述不再包含可检索范围,模型只能盲调;建议描述中至少静态说明作用域规则(已配置=受限范围,未配置=全部可见库),当前文案已接近,保持即可;

  • 解析阶段 asyncio.gather 未设异常策略,首个异常传播时其余任务结果被丢弃且可能产生 "Task exception was never retrieved" 噪音;绑定条目通常很少,顺序解析即可;

  • 每次请求新建 AsyncClient(N 个绑定 + 1 次检索 = N+1 条连接),v1 可接受,Gateway 侧接入时再统一。

@rayhpeng

Copy link
Copy Markdown
Collaborator

@zhangwei139623 看了 90feeda6 和三个 docs commit。先说结论:按 embedding 模型分组检索的方案予以接受——它超出了上一条 review 问题 1 的最低要求(note + 运行时指引),让异构 embedding 的租户在默认全量下真正可用,分数不可比时按名次交错而非跨组排序的处理是正确的,信号量限流和 return_exceptions 的异常处理也比之前干净。但这一轮有一个明确的 blocking 被跳过了,还引入了一个新问题,文档删除也需要调整。按优先级说明如下。

1. 上一条 review 的问题 2(missing-dataset)仍未修复,这是第四个版本原样保留(blocking,请最优先处理)

_resolve_datasets 的绑定路径、_current_dataset_missing_dataset_error 与测试 mock 在本轮全部原样:实现仍假设"绑定的 dataset ID 不存在时 GET /datasets?id= 返回空列表",而真实 API 返回的是 code=102 "User '...' lacks permission for dataset '...'"(我在 v0.27.0 真实实例上的实测,源码 [v0.27.0 dataset_api_service.py#L433-L439](https://github.com/infiniflow/ragflow/blob/v0.27.0/api/apps/services/dataset_api_service.py#L433-L439))。配置指引依然是死代码,`test_missing_bound_dataset_returns_operator_guidance` 依然在固化真实 API 不会产生的行为。

修复要求与上一条 review 相同,这里重申验收标准:

  • 解析阶段捕获 RAGFlowAPIError 并包装为指向 knowledge_search.datasets 的配置指引(按阶段捕获,不要匹配错误消息文本);
  • 模型可见错误用绑定序号定位失效条目(如 the 2nd entry of knowledge_search.datasets),完整 ID 记服务端日志;
  • 测试 mock 改为真实响应形状(code=102 错误);
  • 在真实实例上验证:配置一个不存在的 dataset ID,工具必须返回配置指引而非 "lacks permission" 原文。

请在下一轮提交中优先完成这一项,再做其他改动。

2. 新问题:embedding_model 为空的 dataset 会导致整个默认全量检索失败,且与文档矛盾(blocking)

_resolved_datasetembedding_model 为空的 dataset 直接抛 RAGFlowProtocolError,且发生在 chunk_count == 0 跳过逻辑之前;test_group_failure_remains_strict_and_redacts_secret_and_dataset_id 把该行为固化成了断言。

但"embedding_model 为空的库"是 RAGFlow 真实允许的状态:租户未配置默认 embedding 模型时创建的 dataset 就是如此(create 响应返回 "embedding_model": "",该库无法解析、chunk_count 恒为 0——我在真实实例上实际产生过这样的库)。当前实现下,租户里只要存在一个这样的死库,所有未配置 datasets 的检索整体失败,错误还是 protocol 口径、不指向元凶。而 CONFIGURATION.md 新增的 "Empty datasets are ignored" 恰恰会让 operator 以为这类库无害——文档与实现直接矛盾。

修复:embedding_model 为空的 dataset 应跳过并记 warning 日志(与空库同等对待),仅对"有 chunk 却无 embedding 元数据"这种理论上不该出现的状态保留报错。"If any searchable group fails, the whole tool call fails" 的 fail-loud 策略针对检索阶段是合理的,可以保留;但解析阶段的死库不应该有毒性。请同步修正该测试,并在真实实例上验证:存在一个未设 embedding 的空库时,默认全量检索应正常返回其余库的结果。

3. README 与 AGENTS.md 改动不应整体删除(blocking)

0beeb7c7 / 2f4121c1 / 207a56c0 把 README 和两个 AGENTS.md 的改动全部移除。"feature 细节不进 agent guides" 这个原则本身成立,但仓库的 Documentation update policy 明确要求:user-facing 变更同步 README,开发/架构约束同步相关 AGENTS.md。正确做法是精简而非清空:

  • README:保留一小节 user-facing 说明(功能是什么、如何启用、tenant 共享注意事项),不写实现细节;
  • backend/AGENTS.md / harness AGENTS.md:保留架构红线段落——API key 不得进入日志与模型可见错误、dataset UUID 不得进入模型上下文、retrieval-only 边界(无写操作、无 Gateway API)、测试位置指针。这些是给后续开发者和 coding agent 的约束,恰恰属于 agent guides 应载内容;分组算法之类的 feature 细节留在 CONFIGURATION.md 即可。

4. Minor

  • 交错合并后,来自不同 embedding 空间的 (score …) 仍并列展示在同一列表中,模型必然会跨组比较这些不可比的分数——与代码注释声明的原则相悖。建议多组合并路径下省略分数后缀,或在结果尾部注明"结果来自多个检索组,分数不跨组可比";
  • PR 描述的真实验证部分请补充分组场景:真实实例上创建两个使用不同 embedding 模型的库,验证默认全量检索返回合并结果——顺带覆盖第 2 条的死库场景。

@zhangwei-way

Copy link
Copy Markdown
Contributor Author

@rayhpeng 已在 1ad7f59f 处理本轮问题 1、2 和 Minor 问题 4:

  1. configured dataset 解析阶段现在捕获真实 RAGFlowAPIError(code=102),模型可见错误按 knowledge_search.datasets 绑定序号定位;完整 ID/code 仅写服务端 warning。配置绑定改为顺序解析,避免并发解析异常噪音。
  2. embedding_model 为空且 chunk_count == 0 的真实空库现在 warning 后跳过;有 chunk 却缺少 embedding 元数据仍 fail loud。
  3. 多 embedding 分组结果不再展示不可跨组比较的 score;单组结果继续保留 score。

真实 RAGFlow v0.27.0 已验证:

  • 不存在 ID 的裸 API code=102 被转换为第 1 个绑定项的配置指引,未返回 lacks permission 原文或 ID;
  • 空 embedding/0 chunk 死库 + 临时 v2 可检索库 + 现有 v3 库共同走默认全量,v2/v3 均有召回、死库被跳过、多组 score 隐藏;
  • 两个临时库已删除,无残留。

测试:RAGFlow targeted 43 passed;完整 backend 12,348 passed, 78 skipped,仅保留两个与本改动无关的既有本地环境失败。PR 描述已补充命令/输出摘要。本提交未处理问题 3(README / AGENTS.md)。

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for three scope/error-handling issues. The targeted RAGFlow tests and Ruff checks pass, but the empty-allowlist behavior is fail-open and should be addressed before merge.

Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
Comment thread backend/packages/harness/deerflow/community/ragflow/client.py Outdated
Comment thread backend/packages/harness/deerflow/community/ragflow/tools.py Outdated
@rayhpeng

rayhpeng commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

@zhangwei139623 1ad7f59 已核查:问题 1(missing-dataset 解析阶段捕获 + 序号定位 + 测试改真实形状)、问题 2(空 embedding 死库跳过、有 chunk 缺元数据仍 fail loud)、Minor 的 score 处理均验收通过,PR 描述的真实验证记录也符合要求。

另外,处理一下上述 @willem-bd 的修改建议

@zhangwei-way
zhangwei-way force-pushed the codex/ragflow-retrieval-only branch from 1ad7f59 to 1f177a5 Compare August 25, 2026 08:05
@rayhpeng

Copy link
Copy Markdown
Collaborator

@zhangwei139623 1f177a50 复核完毕,我此前提出的问题至此全部验收通过:

  • missing-dataset(四轮遗留):解析阶段捕获、绑定序号定位、完整 ID 仅入服务端日志、测试 mock 改为真实 code=102 形状,PR 描述中真实实例的验证输出与验收标准一致;
  • 空 embedding 死库:chunk_count == 0 时 warning 后跳过、有 chunk 缺元数据仍 fail loud,边界划分正确;
  • 跨组 score:多组合并隐藏、单组保留,符合建议;
  • 顺序解析同时消除了此前提到的并发异常噪音。

文档一条(README/AGENTS.md)经维护者侧讨论撤回:agent guides 保持精简、feature 细节由 CONFIGURATION.md 承载的现状可以接受,不再要求恢复。

我这边的 review 范围没有遗留问题,准备 approve。剩余事项在 @willem-bd 的 changes-requested 里,我补充三点意见:

1. datasets: [] fail-open——同意,这是合入前必须修的。 validator 末行 return normalized or None 把显式空列表静默转成 None,deny-all 语义反转为全量检索。修复很小:空列表直接抛 ValueError(配置错误 fail-fast),补 datasets: [] 回归测试。

2. 解析阶段错误一刀切——同意。 当前 except RAGFlowAPIError 会把凭据失效、限流、服务端故障也转成"第 N 个绑定项找不到"的误导指引。请按 code == 102(DATA_ERROR,即 not-found/permission)收窄翻译范围,其余 code re-raise 走 _tool_error 的正常脱敏路径,并补一条 code != 102 的测试。

3. 分页 total 字段——现有实现是正确的,这条建议不成立,但可做防御性兼容。 @willem-bd 引用的 get_result(..., total=result.get("total"))函数入参名;v0.27.0 get_result 的实现序列化时写入的字段是 total_datasets,与 v0.26.4 一致([api_utils.py#L311-L317](https://github.com/infiniflow/ragflow/blob/v0.27.0/api/utils/api_utils.py#L311-L317)):

if total is not None:
    response["total_datasets"] = total

我在真实 v0.27.0 实例上做分页验证时也确认过该字段名。误导源是 get_result 自己的 docstring 示例(写的是 "total": 47,与实现不符)——这已是本 PR 周期内第三处 RAGFlow 文档与实现不一致的案例。因此"生产环境永远走短页 fallback"与"恰好 10,000 库误报 page-cap"的推论不成立。建议的收尾方式:读取时 total_datasets 优先、total 兜底,防未来版本按 docstring 改动,并在注释注明两个字段名的出处版本。

第 1、2 条修复落地后,本 PR 从我的角度即可合入。感谢几轮快速迭代——从首版到现在,工具链在真实 RAGFlow 上的行为已经完整验证过一遍。

@WillemJiang WillemJiang added this to the 2.1.0 milestone Aug 25, 2026
@WillemJiang
WillemJiang merged commit 431892e into bytedance:main Aug 25, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:docs Documentation and Markdown only risk:medium Medium risk: regular code changes size/XL PR changes 700+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: RAGFlow Integration — Private-Corpus Retrieval and Knowledge Base Management

5 participants