news 2026/9/16 9:32:32

BISHENG 知识空间 AI 问答检索权限过滤(F029):双层 view_file 过滤架构与落地实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BISHENG 知识空间 AI 问答检索权限过滤(F029):双层 view_file 过滤架构与落地实现

BISHENG 知识空间 AI 问答检索权限过滤(F029):双层 view_file 过滤架构与落地实现

【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng

导读

本文围绕 BISHENG v2.6.0 特性F029-knowledge-qa-permission-filter(知识空间 AI 问答 - 检索权限过滤)展开,讲解其如何解决"列表 UI 看不到的文件,却能被 AI 问答检索到"这一越权读取风险。文中以 spec.md 与 tasks.md 为骨架,结合仓库真实源码,完整呈现双层过滤架构(AD-01/02/03/08)、KnowledgeFileVisibilityService的实现细节、可配置参数、四大问答入口(整空间 / 文件夹 / 文件预览 / 首页与工作台多 KB)与角标溯源接口的改造,以及 Test-First 测试策略与 E2E 回归清单。读完本文,你将掌握 BISHENG 如何在不新增 OpenFGA 关系、不新增数据库表的前提下,用"索引层粗滤 + 结果层精滤"保证 AI 问答可见性与列表 UI 可见性严格一致。

1. 背景:AI 问答为什么成为越权读取的入口

知识空间内,用户可能对某个空间有view_space权限,但只对其中部分文件有view_file权限。改造前,知识空间 AI 问答的检索链路(KnowledgeSpaceChatService、工作台WorkStationService.queryChunksFromDB)只做空间级鉴权,检索召回出的 chunk 没有按文件级权限过滤,导致:

  • 普通用户在空间页发起问答,回答引用的文件名、来源、预览链接可能暴露无权文件;
  • 历史会话的角标溯源(citation resolve)接口在用户失去view_file后仍返回结构化字段,构成新的越权读取路径;
  • 首页 / 工作台多 KB 检索以kb_id_whitelist+check_auth=False跳过 per-KB 鉴权,可被构造请求绕过下拉框。

该特性被定义为P0 优先级,必须在 v2.6.0 关闭这条越权路径。

1.1 范围边界(本期纳入 / 排除)

本期纳入:知识空间 AI 助手的 4 个问答入口(整空间 / 文件夹 / 文件预览 / 首页 & 工作台多 KB 检索)+ 新版角标溯源接口(POST /api/v1/citations/resolve批量、GET /api/v1/citations/{citation_id}单条)。

本期明确排除

  • 工作流KNOWLEDGE_RETRIEVER节点(workflow/nodes/knowledge_retriever/)——涉及流程节点执行上下文中的"运行用户身份"问题,留待后续 feature;
  • 对外 RPC/api/v2/filelib/retrieve——当前以"默认 operator"身份运行而非真实终端用户,需先定义"代用户检索"协议;
  • OpenFGA 模型变更——不引入新的view_fileReBAC 关系,复用现有can_readview_filepermission_id;
  • chunk 元数据 ACL 索引化("超大规模"方案)——单空间单用户可见文件 > 10 万时所需的索引层 ACL 字段方案不在本期,本期通过"双层过滤 + 检索次数封顶"在 10 万规模内提供可接受性能。

2. 核心设计:双层过滤架构(AD-01 / AD-02 / AD-03 / AD-08)

2.1 语义对齐基线(AD-01):为什么不能只用单一权限

架构决策 AD-01 在三个选项中选择了双层过滤(选 C)

方案问题
A:仅用 ReBACcan_read会导致"列表看不到但问答能查到"(越权)
B:仅用 fine-grainedview_file10 万规模冷启动需要对每个文件单独 OpenFGA tuple 读取,单空间一次性解析耗时不可接受
C:双层(选)can_read在 Milvus/ES 端把候选缩到至少有 ReBAC 读权限的文件(一次list_objects+ Redis 缓存),再在召回结果的 unique file_id(一般规模 ≤ 30 个)上跑 fine-grainedview_file解析,保证最终送进 LLM 的 chunks必然满足列表 UI 可见性

这一设计的目标不变量(INV-7,登记在 release-contract.md 表 2):

知识空间内容的"AI 问答可检索可见性"必须是"列表 UI 可见性"的子集;即对任意(user, space, file),若用户在列表 UI 中不可见该fileview_file ∉ effective_permissions),则任何 AI 问答入口都不得让该file的 chunk / 文件名 / 来源出现在模型上下文、回答引用、角标溯源/api/v1/citations/resolve响应的结构化字段中。

2.2 索引层过滤策略(AD-02):自适应 IN / NOT-IN / 不过滤

以可见集合大小K与空间内主版本文件总数N为决策依据:

  • K ≤ 5000IN(可见集合)
  • N − K ≤ 5000NOT IN(排除集合)(排除集合 = 非主版本 ∪ 不可见);
  • 二者均 > 5000 → 不下推索引过滤,仅靠"扩大候选 + 结果层精滤"。

设计理由:ESterms默认上限 65536;Milvus 长表达式解析在 ≤ 5000 内表现稳定;自适应让 95% 业务走快路径,极端规模有兜底。阈值 5000 写为可配置常量KnowledgeQAFilterConf.index_filter_threshold

2.3 结果层扩展策略(AD-03):最多 2 次检索

  • 首轮按top_k × initial_multiplier(默认 3)召回;
  • 若过滤后 <top_k且首轮命中文件数 > 0,进行至多 1 次扩张到top_k × expansion_multiplier
  • 仍不足则返回已有结果(可少于top_k),不存在"凑齐 top_k 的无限扩张"

该策略保证最坏情况下也只是 2 次 Milvus + 2 次精滤批量调用,单次问答额外延迟 ≤ 400ms,可观测可控。

2.4 结果层精滤并发与缓存(AD-08)

复用列表 UI 已验证的_build_child_permission_context(含tuple_cache)+ 同一 semaphore 8 并发上限(与_CHILD_PERMISSION_CHECK_CONCURRENCY默认一致);空间维度的权限上下文在结果集少(≤ 30 个 file_id)时近乎一次性构建。

3. 核心服务源码解析:KnowledgeFileVisibilityService

新增服务文件位于 knowledge_file_visibility_service.py,集中"可见文件集合"的解析,避免逻辑散落在 chat service 各处。它由 FastAPI 依赖工厂(knowledge/api/dependencies.py 中的get_knowledge_file_visibility_service)构造,构造函数注入request: Request, login_user: UserPayload

3.1IndexFilter:索引层过滤产物

@dataclass class IndexFilter: """Index-layer filter to be injected into Milvus / ES search_kwargs.""" strategy: str # in | notin | none | empty milvus_expr: str | None = None es_filter: list | None = None accessible_size: int = 0 excluded_ids: list[int] = field(default_factory=list) @property def is_empty(self) -> bool: return self.strategy == "empty"

四种策略语义(源码 docstring 明确定义):

  • in——document_id in [visible ids],适用于小可见集;
  • notin——document_id not in [excluded ids],适用于几乎全可见;
  • none—— 不过滤,要么是 admin 调用者,要么两侧规模都过大(由结果层精滤兜底);
  • empty—— 用户在该空间可见文件数为 0,调用方必须直接跳过检索。

3.2is_space_visible(space_id) -> bool(AC-11)

non-throwing 版本,复用KnowledgeSpaceService._require_permission_id('knowledge_space', space_id, 'view_space')的判定逻辑,捕获SpacePermissionDeniedError(错误码 18040,复用不新增)返回False。admin 用户由底层PermissionService短路返回True。该方法是首页 / 工作台多 KB 检索"无view_space的 KB 静默跳过"(AC-11)的判定来源。

3.3build_index_prefilter(space_id, candidate_file_ids) -> IndexFilter

AD-02 策略决策的核心实现,关键逻辑如下(源码级):

  1. PermissionService.list_accessible_ids(user_id, 'can_read', 'knowledge_file', login_user)拿到用户租户级可读文件集(admin 返回None);
  2. 拉取该空间主版本 file_id 集合(排除非主版本,复用version_repo.find_non_primary_file_ids_by_knowledge_ids),计算交集K = accessible_ids ∩ candidate (∩ space_files if candidate=None)
  3. 按阈值选策略并返回IndexFilter

源码中一个值得注意的细节是业务范围的正确性优先candidate_file_ids(文件夹 / tag 业务范围)没有结果层兜底(post_filter_visible_files只强制view_file),因此只要传入了 candidate,就必须下推到索引层,不能走 admin 或"两侧过大"的none短路——大 IN 列表优于泄漏到范围之外。

3.4post_filter_visible_files(space_id, file_ids) -> Set[int]

结果层精滤,保证"最终送 LLM 前必经":

  1. admin → 返回输入集合(短路);
  2. 空输入 → 在构建权限上下文前短路;
  3. 一次性_build_child_permission_context(space_id)拿共享 binding / tuple_cache / membership 上下文;
  4. semaphore(fine_grained_concurrency默认 8)并发跑_get_child_item_effective_permission_ids,保留view_file ∈ effective的 file_id。

源码 docstring 特别强调:委托调用应用了 per-item lineage walk(file → 祖先文件夹 → space)、nearest_binding_wins=True语义(文件级 revoke 优先于空间成员默认)、membership 默认权限与 public-space viewer 默认——若缺少这些参数,早期版本会返回所有 binding 的并集,让被 revoke 的文件泄漏过滤,这正是用户报告的 bug。测试 test_knowledge_file_visibility_service.py 中的test_post_filter_visible_files_regression_revoke_overrides_membership即该回归场景的验证。

4. 配置参数:KnowledgeQAFilterConf

配置块定义于 core/config/settings.py,挂在Settings.knowledge_qa_filtersettings.py第 740 行)顶层为可选块,YAML 缺省时按默认值。

class KnowledgeQAFilterConf(BaseModel): """Knowledge space AI Q&A retrieval permission filter (F029).""" index_filter_threshold: int = Field( default=5000, ge=1, description="AD-02 threshold. ...当可见或排除文件数 ≤ 该值走 IN / NOT-IN,否则仅后过滤", ) retrieval_initial_multiplier: int = Field( default=3, ge=1, description="AD-03 first attempt. 首轮召回 top_k * 该倍数", ) retrieval_expansion_multiplier: int = Field( default=6, ge=1, description="AD-03 capped expansion. 不足 top_k 时单次重试召回 top_k * 该倍数,不再扩张", ) fine_grained_concurrency: int = Field( default=8, ge=1, le=64, description="AD-08 concurrency. 结果层 view_file 解析的 semaphore 并发上限", ) @model_validator(mode="after") def validate(self): if self.retrieval_expansion_multiplier < self.retrieval_initial_multiplier: raise ValueError("retrieval_expansion_multiplier must be >= retrieval_initial_multiplier") return self

参数速查表:

参数默认值约束作用关联决策
index_filter_threshold5000>= 1索引层 IN / NOT-IN 切换阈值AD-02,AC-23/24/25
retrieval_initial_multiplier3>= 1首轮召回倍数AD-03
retrieval_expansion_multiplier6>= initial单次扩张召回倍数,扩张后封顶AD-03,AC-26
fine_grained_concurrency81..64结果层精滤并发AD-08

实现偏差提示:spec.md 中retrieval_expansion_multiplier写的是默认 10,而实际提交到源码的默认值是6。源码注释说明改为 6 是为了控制重试搜索成本(base_k=100 时 k=600),且 Milvus wrapper 会提高 ef 覆盖该 k 值。这也是 tasks.md「实际偏差记录」一节建议登记的内容。

5. 四个问答入口的改造

5.1 整空间 / 文件夹问答:KnowledgeSpaceChatService

knowledge_space_chat_service.py 的改造要点:

  • _build_folder_search_kwargs拆为_compute_candidate_file_ids(knowledge_id, folder_id, tags)(保留原 candidate 计算逻辑)+ 调build_index_prefilter
  • 新增_retrieve_and_filter(space, query, candidate, max_content) -> List[Document](对应 spec §7.4 五步流程);
  • chat_folder替换原milvus_kwargs/es_kwargs构造为_retrieve_and_filter调用;
  • space_ragretriever_tool.ainvoke调用迁入_retrieve_and_filter,移除原 retriever_tool 直接拼接逻辑,保留 prompt + LLM 调用;
  • chat_single_filespace_rag之前增加防御性post_filter_visible_files({file_id})一次(已通过门禁的请求应必中;不中则记 WARN 并返回空,避免越权);
  • _render_rag_responsespace_rag中抽出,供chat_folder复用不变的 prompt + 流式路径。

_retrieve_and_filter的关键实现(源码第 480-566 行):

  • build_index_prefilter,若index_filter.is_empty则直接返回空(并写一条strategy=empty的结构化日志);
  • (initial_multiplier, expansion_multiplier)二元组循环,每轮以base_k × multiplier构造 Milvus/ES search_kwargs(base_k=100ef=110),注入milvus_expr/es_filter
  • 调用KnowledgeRetrieverTool.ainvoke(query)后抽 unique file_id,走post_filter_visible_filesview_file子集,过滤 chunks;
  • 一旦某轮 survivors 非空立即 break(AD-03 封顶 = 2 次尝试);
  • 每轮结束写一条结构化日志(AC-27 字段)。

5.2 首页 / 工作台多 KB 检索:WorkStationService.queryChunksFromDB

workstation_service.py 按 spec §7.2b 逐项落地:

  • Stage 1(AC-11):进入循环前对space_bucket的每个 kb_id 调is_space_visible(kb_id);不通过则continue+ INFO 日志skipped_kb_id=%s reason=no_view_space(源码第 1017-1031 行)。该 KB 静默跳过、不抛错、不阻塞其他 KB、不出现在kb_succeed列表;
  • Stage 2(索引层粗滤):构造MultiRetrieversearch_kwargs时注入build_index_prefilter(kb_id, None)的产物,与chat_folder路径一致。不做显式预检测"用户在该 KB 是否有可见文件"——若accessible_ids与 KB 文件集交集为空,Milvus / ES 查询自然返回 0 chunks(AC-12);
  • Stage 3(结果层精滤)per_kb_tool.ainvoke返回后对kb_docspost_filter_visible_files(kb_id, unique_file_ids);过滤后空则不进入finally_docs/ 不计入kb_succeed(源码第 1085 行);
  • 沿用 AD-03 检索循环上限(首轮 ×3 / 扩张 ×10),多 KB 间不互相补量;
  • org_bucket(legacyknowledge_library)路径完全不改(AC-14),check_auth=False不动(与本特性正交)。

5.3 角标溯源:CitationResolveService

citiy_resolve_service.py 的改造:

  • 删除_has_file_access静态方法(旧 RBACRoleAccessDao+AccessType.KNOWLEDGE空间级判定,是 arch-guard RULE-8 VIOLATION)及其对from bisheng.database.models.role_access import AccessType的导入;
  • 新增_permitted_file_ids(items, login_user)login_user is None(匿名)返回None表示不过滤(AC-20 保持现状);否则按(knowledgeId, documentId)分组,对每个 knowledgeId 调post_filter_visible_files,扁平合并为允许集合;
  • _apply_tier_filter:web 类型 citation 永远通过(AC-19);per_userRAG citation 在文件不满足view_file时整条剔除;sharedRAG citation(toggle-OFF 的知识空间来源)保留、其整文件 URL 在 enrich 阶段再门控;匿名时全保留;
  • resolve_citations(批量):先过滤再走原_enrich_item并发流程;
  • resolve_citation(单条):RAG 类型且无权 → 抛NotFoundError(AC-18,与"citation 不存在"语义一致);web 类型不受影响;
  • _enrich_rag_item内旧的has_access = login_user is None or ...整段删除——精滤已在上层完成,进入这里的 item 必然通过view_file或匿名,URL/bbox 无条件填充。

5.4 不修改的端点(明确排除)

  • knowledge/api/endpoints/qa.py/qa/chunk/qa/keyword是历史角标溯源接口,本期不动,待后续统一下线;
  • open_endpoints/api/endpoints/filelib.pycitation.py的 v2 RPC:默认 operator 身份,本期不动(AD-06);
  • workflow/nodes/knowledge_retriever/:工作流节点检索路径(AD-06)。

6. API 契约与错误码

所有端点继续走UserPayload = Depends(UserPayload.get_login_user)(QA 端点新增依赖);响应包装沿用UnifiedResponseModel[T]无外部契约变化,前端契约透明。

MethodPath变更
POST/api/v1/knowledge/space/{space_id}/chat/file/{file_id}内部新增结果层精滤(防御性确认)
POST/api/v1/knowledge/space/{space_id}/chat/folder内部接入双层过滤(folder_id=0 即整空间)
POST工作台search_kb工具(无独立 HTTP 端点)queryChunksFromDB内部按view_file过滤;无view_space的 KB 静默跳过
POST/api/v1/citations/resolveService 内_has_file_access改造为文件级view_file精滤;无权 citation 整条剔除
GET/api/v1/citations/{citation_id}单条无权返回NotFoundError
POST/api/v1/qa/chunk/GET/api/v1/qa/keyword历史接口不修改,待下线

错误码表(不新增错误码):

HTTP StatusCode场景关联 AC
200(body)18040SpacePermissionDeniedError(复用)view_space/view_folder/view_fileAC-01, AC-05, AC-08
200(body)view_*但可见集合为空;resolve 时 items 全被过滤AC-03, AC-12, AC-17
200(body)现有NotFoundError单条 citation 无view_file,与"citation 不存在"语义一致AC-18

请求 / 响应示例(spec §6,/citations/resolve部分 citation 无权,AC-16 整条剔除):

请求:

POST /api/v1/citations/resolve { "citationIds": ["cit_A_visible", "cit_B_invisible", "cit_C_visible"] }

响应(cit_B_invisible对应的文件用户无view_file,整条不返回):

{ "status_code": 200, "status_message": "SUCCESS", "data": { "items": [ { "citationId": "cit_A_visible", "type": "rag", "sourcePayload": { "knowledgeId": 5, "knowledgeName": "Q1 报告库", "documentId": 1234, "documentName": "report-Q1.pdf", "snippet": "...", "previewUrl": "...", "downloadUrl": "...", "items": [{ "itemId": "...", "bbox": "..." }] } }, { "citationId": "cit_C_visible", "type": "rag", "sourcePayload": { "...": "..." } } ] } }

chat_folder流式响应source_documents[*].file_id必为当前用户view_file子集(AD-01 结果层精滤保证),可能为空数组(AC-03 触发,但模型仍按 prompt 给出回答)。

7. 验收标准(AC)全景

7.1 整空间 / 文件夹 / 文件预览问答

ID场景预期
AC-01view_space调整空间问答返回 18040;不进入检索;不写 RecallChunk;不消耗 LLM 配额
AC-02view_space、部分文件有view_file检索仅命中可见的主版本文件;source_documents[*].file_id是当前view_file子集
AC-03view_space但空间内无任何view_file文件空文档集;模型按 prompt 回答(如"未找到相关内容");HTTP 200 不报错
AC-04问答后管理员收回部分view_file,立即追问第二轮用最新权限(缓存 TTL ≤ 10s 或invalidate_user已触发),被收回文件不再进入上下文
AC-05view_folder调文件夹问答返回 18040;不进入检索
AC-06view_folder检索范围 = 文件夹子树下可见主版本文件;无view_folder的子文件夹分支被剔除(与列表 UI 对齐)
AC-07view_folder+tags过滤检索范围 = 子树可见集 ∩ tag 文件集;交集为空按 AC-03 行为
AC-08view_file调单文件问答(URL 直构造)返回 18040;不进入检索
AC-09view_file检索限定document_id == file_id;沿用version_filter;行为与原有逻辑等价

7.2 首页 / 工作台多 KB

ID场景预期
AC-10拉取 KB 下拉框(mine/managed/joined/department 4 端点)本特性不改 4 端点行为(现有服务层已按can_read+ membership 过滤),E2E 回归验证
AC-11某 KB 无view_space(构造请求绕过下拉框)静默跳过;不抛错不阻塞;不出现在kb_succeed;INFO 日志skipped_kb_id=X reason=no_view_space
AC-12KB 有view_space但无任何view_file文件Stage 2/3 自然产出 0 docs;不进入finally_docs/kb_succeed;日志输出 AC-27 字段
AC-13KB 内部分可见每 KB 走双层过滤;跨 KB 合并按max_total_docs=100截断不变
AC-14选择org_bucket(legacyknowledge_library不修改org_bucket路径行为;变更历史登记差异

7.3 角标溯源

ID场景预期
AC-15所有 RAG 文件均满足view_fileitems结构与现状一致;downloadUrl/previewUrl/bbox照常填充
AC-16部分 RAG citation 已无view_file整条剔除无权 citation(documentName / knowledgeId / snippet / chunk 文本均不返回);web 类型不受影响
AC-17全部无权items返回空数组[](HTTP 200)
AC-18单条 RAG citation 无权返回NotFoundError;不返回任何文件元数据
AC-19web类型 citation本期不修改_enrich_web_item行为
AC-20匿名调用方(share link,login_user is None保持现状,不引入新鉴权

7.4 实时性 / 性能 / 可观测

ID场景预期
AC-21管理员 authorize / revoke同步触发PermissionCache.invalidate_user(user_id),下一轮问答即用新权限
AC-22缓存未及时失效TTL 上限 10s,最迟 10s 内自动收敛
AC-23≤ 5000 可见文件、热缓存权限过滤新增耗时 ≤ 80ms(不含 LLM / Milvus / ES)
AC-24≤ 5000 可见文件、冷缓存(OpenFGA list_objects)新增耗时 ≤ 500ms
AC-25≤ 100000 可见文件、热缓存新增耗时 ≤ 200ms(走 NOT-IN 或后过滤路径)
AC-26任意场景检索循环次数 ≤ 2,不存在无限扩张
AC-27任意场景结构化日志含strategy=in\|notin\|postfilteraccessible_ids_sizeprefilter_candidate_sizeretrieval_attemptspost_filter_dropped_count

8. Test-First 测试策略与测试覆盖

tasks.md 采用后端 Test-First(务实版):先写测试(红)再写实现(绿)。新测试统一放在test/<module>/之下,复用 conftest.py 现有 fixtures(mock_openfgamock_redisasync_db_sessiontenant_context等);OpenFGA 通过mock_openfga桩;Milvus / ES 不在单元测试中真起,用 monkeypatch 替换 retriever 工具最外层ainvoke返回固定 docs。

8.1 三个新增测试文件

测试文件任务数量覆盖 AC
test_knowledge_file_visibility_service.pyT00217 testsAC-11 / AC-23 / AC-24 / AC-25 部分
test_knowledge_space_chat_service_visibility.pyT0047 testsAC-01 ~ AC-09 / AC-26 / AC-27
test_query_chunks_visibility.py(新建test/workstation/目录)T0068 tests(4 主场景)AC-11 ~ AC-14
test_citation_resolve_visibility.py(新建test/citation/目录)T0089 tests(7 主场景)AC-15 ~ AC-20

8.2 关键测试场景示例

  • test_build_index_prefilter_*系列:K=0 → strategy=empty;K=10 → strategy=in 且milvus_expr="document_id in [..]"es_filterterms;N−K ≤ 5000 → strategy=notin;两侧均 > 5000 → strategy=none;admin → strategy=none;传入 candidate 时 admin 仍受业务范围约束(test_build_index_prefilter_admin_still_scopes_to_candidate);大 candidate 永不下沉为 none(test_build_index_prefilter_large_candidate_never_falls_to_none)。
  • test_post_filter_visible_files_*系列:仅保留view_file ∈ effective的 file_id;admin 返回全部;空输入短路;50 个并发 file_id 验证 semaphore 限流不死锁;回归用例验证"revoke 覆盖 membership 默认"。
  • test_retrieve_and_filter_*系列empty策略直接跳过 retriever 调用;首轮即满足 top_k;精滤砍掉部分;封顶 2 次尝试test_retrieve_and_filter_capped_at_two_attempts);扩张第二轮成功;用caplog验证permission_filter结构化字段齐全(test_retrieve_and_filter_logs_structured_fields)。
  • test_chat_single_file_logs_view_file_passed_debug:验证单文件问答通过门禁后的 DEBUG 日志。
  • Citation 测试:全可见原样返回(含 URL/bbox);部分不可见整条剔除(web 不受影响);全不可见 items 空数组;单条无权抛NotFoundError;web 类型不走view_file;匿名跳过精滤;admin 短路。

9. 前端改动:仅 i18n 文案,无组件 / 路由 / store

后端变更对前端契约透明,唯一需要联调的是"无可见内容"提示文案:

  • Platform:public/locales/{en-US,zh-Hans,ja}/knowledge.json 补knowledge.qa.noVisibleContent
  • Client:src/locales/{en,zh-Hans,ja}/translation.json 补同名语义 key。

三语文案:

语言文案
中文未在你有权访问的内容中找到相关信息
英文No relevant content found in resources accessible to you
日文アクセス権限のあるリソースに該当する内容が見つかりませんでした

不涉及:新增组件、新增路由、新增 store。整空间 / 文件夹 / 单文件问答响应 schema 不变;角标溯源面板items数组变短时按数组渲染无需特殊处理;单条无权返回NotFoundError时前端复用现有"该来源已不可访问"提示。

10. E2E 回归清单与性能验证

tasks.md T012 定义的全链路回归场景(e2e-report.md 模板待人工执行):

  1. KB 下拉框回归(AC-10):以view_space不全的测试账号登录,确认GET /api/v1/knowledge/space/{mine,managed,joined,department}仅返回有view_space的空间;
  2. 整空间问答(AC-02 / AC-06):部分文件可见账号触发问答,截图source_documents仅含可见 file_id 的 chunk;
  3. 首页 / 工作台问答(AC-11/12/13):选 3 个 KB(1 个无 view_space、1 个无可见 view_file、1 个有可见 file),仅最后一个 KB 出现在回答 citation 列表;
  4. 角标溯源(AC-15/16/17):历史会话引用 N 个文件,管理员收回一半文件的view_file,重开会话展开 citation 面板,确认无权 citation 整条不出现;
  5. 单条 citation(AC-18):直接GET /api/v1/citations/{无权 citation_id}返回NotFoundError
  6. 匿名调用(AC-20):share link 公开访问触发 citation resolve,行为不变;
  7. 实时失效(AC-21/22,关键场景):账号 A 对 F1 有view_file→ 问答确认 F1 出现 → 管理员调PermissionService.revoke→ 10s 内追问验证 F1 消失;grep 后端日志确认accessible_ids_size前后两轮变化,证明list_accessible_ids已重算;
  8. 结构化日志(AC-27):grep 日志permission_filter行,确认 5 个字段齐全;
  9. 性能 sanity(AC-23/24):单空间 5000+ 可见文件账号整空间问答,warmup ≤ 80ms、冷启动 ≤ 500ms;10 万规模(AC-25)环境不可达则在报告标注"未验证 + 留待 staging";
  10. arch-guard 校验:运行bash scripts/arch-guard.sh,确认CitationResolveService的 RULE-8 VIOLATION 已消除。

11. 实现偏差记录与后续演进

tasks.md 末尾保留「实际偏差记录」章节,实现完成后登记与 spec.md 的偏差。本文可确认的一处偏差是retrieval_expansion_multiplier默认值由 spec 的 10 调整为源码的 6(为控制重试搜索成本,且 Milvus wrapper 会提高 ef 覆盖)。建议后续执行 E2E 时在报告中同步记录该调整对 AC-26 的影响。

后续演进方向(本期明确不支持,详见 spec §3 边界情况):

  • 工作流 KNOWLEDGE_RETRIEVER 节点的检索权限过滤(需要先定义"运行身份"语义);
  • 对外 RPC/api/v2/filelib/retrieve的代用户检索协议(on_behalf_of_user_id或新鉴权方式);
  • 单用户单空间 > 10 万可见文件的低延迟保证(chunk 元数据 ACL group 字段方案);
  • /api/v1/qa/chunk/api/v1/qa/keyword历史接口的统一下线;
  • 匿名调用方 citations 接口的鉴权专项(share-link 场景)。

相关文档

  • 特性规格:spec.md
  • 任务拆分:tasks.md
  • 版本契约与 INV-7:release-contract.md
  • 权限体系参考:10-permission-rbac.md(描述 v2.5 之前旧 RBAC 模型,仅作历史背景)

【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 9:31:05

Windows虚拟内存与页面文件配置指南:告别内存不足与OOM

最近帮朋友排查一个很典型的故障&#xff1a;浏览器开着十几个标签页&#xff0c;编译工具一跑&#xff0c;系统直接弹“内存不足无法打开此网页”&#xff0c;接着整个桌面卡到鼠标都挪不动。打开任务管理器一看&#xff0c;物理内存明明还有几个GB空闲&#xff0c;但“提交”…

作者头像 李华
网站建设 2026/9/16 9:29:32

Agent技能工程化:用Nx+TypeScript构建可治理的技能契约体系

1. “agent-skills”不是库名&#xff0c;而是工程级能力抽象层的设计起点你第一次在 GitHub 或内部项目里看到agent-skills这个仓库名时&#xff0c;大概率会下意识认为&#xff1a;这是个封装了“AI Agent 常用工具函数”的 npm 包——比如调用天气 API、查数据库、发邮件、读…

作者头像 李华
网站建设 2026/9/16 9:26:59

ESP32+MAX30102+OLED医疗级健康监测仪实战

1. 这不是“玩具项目”&#xff0c;而是一台能跑通临床级信号链的微型健康监测终端你搜“ESP32 健康监测仪”&#xff0c;十有八九看到的是“呼吸灯串口打印心率”的Demo&#xff0c;连滤波都没有&#xff0c;更别说校准。但这次标题里写的“MAX30102 OLED显示——做一个完整的…

作者头像 李华
网站建设 2026/9/16 9:23:19

HarmonyOS NEXT域名解析失败解决方案

1. 问题现象与背景分析最近在HarmonyOS NEXT应用开发过程中&#xff0c;不少开发者反馈遇到了域名解析失败的问题。具体表现为&#xff1a;当应用尝试通过域名访问网络资源时&#xff0c;系统抛出"域名解析错误"或"验证URL无法被访问"的异常。这个问题在纯…

作者头像 李华
网站建设 2026/9/16 9:23:09

GESP C++八级考试有哪些必考词汇

GESP C八级考试的必考词汇全部围绕核心考点展开&#xff0c;结合近10次官方真题的出现频率&#xff0c;以下是100%覆盖选择题英文考点的必考词汇清单&#xff0c;优先级从高到低排序&#xff1a; &#x1f525; 第一梯队&#xff08;每套真题必考&#xff0c;出现5次以上&…

作者头像 李华
网站建设 2026/9/16 9:20:39

GitHub科研AI项目排行榜:十大开源工具与趋势解析

学术界这几年被GitHub上的AI项目改变得非常彻底。你去看CV、NLP、甚至生物信息方向的论文&#xff0c;方法部分几乎都在给某个开源仓库贴引用&#xff1b;你身边做科研的朋友&#xff0c;十有八九先跑到GitHub上搜有没有现成实现&#xff0c;再决定自己还做不做实验。GitHub早就…

作者头像 李华