docs(plan): deepen portal platform evolution plan — KTD5/7/8/9 expanded, KTD11 added
This commit is contained in:
parent
864bb95a30
commit
af96cb49bd
|
|
@ -3,6 +3,7 @@ title: "AgentKit 门户平台整体演进路线"
|
||||||
type: feat
|
type: feat
|
||||||
date: 2026-06-24
|
date: 2026-06-24
|
||||||
origin: docs/brainstorms/2026-06-24-portal-platform-evolution-requirements.md
|
origin: docs/brainstorms/2026-06-24-portal-platform-evolution-requirements.md
|
||||||
|
deepened: 2026-06-24
|
||||||
---
|
---
|
||||||
|
|
||||||
# AgentKit 门户平台整体演进路线
|
# AgentKit 门户平台整体演进路线
|
||||||
|
|
@ -63,24 +64,26 @@ R17. 自研语义缓存替换为 LiteLLM 内置 Redis Semantic Cache。阈值调
|
||||||
|
|
||||||
**KTD1: LlamaIndex 作为 RAG 管道框架。** 外部研究确认 LlamaIndex 2026 原生覆盖所有所需能力(双索引/智能分段/rerank/问题生成),pgvector 一等支持。相比从零构建避免重复造轮子;相比集成 MaxKB 独立服务避免引入 Django + 独立 PG 的运维复杂度。风险:LlamaIndex 频繁 breaking changes → 通过版本锁定 + 集成测试缓解。
|
**KTD1: LlamaIndex 作为 RAG 管道框架。** 外部研究确认 LlamaIndex 2026 原生覆盖所有所需能力(双索引/智能分段/rerank/问题生成),pgvector 一等支持。相比从零构建避免重复造轮子;相比集成 MaxKB 独立服务避免引入 Django + 独立 PG 的运维复杂度。风险:LlamaIndex 频繁 breaking changes → 通过版本锁定 + 集成测试缓解。
|
||||||
|
|
||||||
**KTD2: TaskIQ 替代 Celery 作为异步任务队列。** 外部研究确认 Celery 对 asyncio 原生栈过度设计(引入 broker + worker + beat = 3 个新运维组件)。TaskIQ 提供一等 FastAPI 集成、Redis broker 支持、asyncio 原生。R12 提前至 P1 解决文档向量化阻塞事件循环的优先级依赖冲突。ARQ 已废弃不采用。
|
**KTD2: TaskIQ 替代 Celery 作为异步任务队列。** 外部研究确认 Celery 对 asyncio 原生栈过度设计(引入 broker + worker + beat = 3 个新运维组件)。TaskIQ 提供一等 FastAPI 集成、Redis broker 支持、asyncio 原生。R10 提前至 P1 解决文档向量化阻塞事件循环的优先级依赖冲突。ARQ 已废弃不采用。
|
||||||
|
|
||||||
**KTD3: KB 元数据持久化到 PostgreSQL。** 遵循 `memory/episodic.py` 的 EpisodicMemory 模式(SQLAlchemy async session + PG)。KB 源、文档记录、ACL 存关系表,embedding 存 pgvector。替换现有内存 `KnowledgeSourceStore`。
|
**KTD3: KB 元数据持久化到 PostgreSQL。** 遵循 `memory/episodic.py` 的 EpisodicMemory 模式(SQLAlchemy async session + PG)。KB 源、文档记录、ACL 存关系表,embedding 存 pgvector。替换现有内存 `KnowledgeSourceStore`。
|
||||||
|
|
||||||
**KTD4: MCP Server 合并至主 FastAPI app。** 现有 `mcp/server.py` 独立 app 零认证是 RCE 面(终端工具存在)。合并为 `/api/v1/mcp/` 子路由,复用 `require_permission` + `APIKeyHeader` 认证。独立 `MCPServer` 类重构为路由工厂。
|
**KTD4: MCP Server 合并至主 FastAPI app。** 现有 `mcp/server.py` 独立 app 零认证是 RCE 面(终端工具存在)。合并为 `/api/v1/mcp/` 子路由,复用 `require_permission` + `APIKeyHeader` 认证。独立 `MCPServer` 类重构为路由工厂。
|
||||||
|
|
||||||
**KTD5: Per-KB ACL 通过新 `kb_acl` 表实现。** 遵循现有 `filter_kb_sources_by_department` 模式,新增 `kb_acl` 表(kb_id, user_id, role: owner/viewer)。Agent 检索时通过 `filter_kb_by_user_acl()` 过滤,与部门级过滤并行。
|
**KTD5: Per-KB ACL 通过新 `kb_acl` 表实现。** 遵循现有 `filter_kb_sources_by_department` 模式,新增 `kb_acl` 表(kb_id, user_id, role: owner/viewer)。`kb_acl` 表定义于 `rag_platform/store.py`(PostgreSQL,与 KB 元数据同库)以保证事务边界一致 — 不放在 `server/auth/models.py`。`kb_acl.kb_id` 外键 → KB 表 `id` ON DELETE CASCADE。Agent 检索时通过 `filter_kb_by_user_acl()` 过滤,与部门级过滤并行。ACL 变更必须触发检索缓存失效。
|
||||||
|
|
||||||
**KTD6: 应用层 jieba 分词实现中文全文检索。** PostgreSQL 内置 `tsvector` 不支持中文分词。在 Python 层用 jieba 分词后写入 `tsvector`,避免安装 PG 扩展(pg_jieba/zhparser)的运维复杂度,也避免引入 Elasticsearch 外部搜索引擎。术语表通过 jieba 自定义词典实现。
|
**KTD6: 应用层 jieba 分词实现中文全文检索。** PostgreSQL 内置 `tsvector` 不支持中文分词。在 Python 层用 jieba 分词后写入 `tsvector`,避免安装 PG 扩展(pg_jieba/zhparser)的运维复杂度,也避免引入 Elasticsearch 外部搜索引擎。术语表通过 jieba 自定义词典实现。
|
||||||
|
|
||||||
**KTD7: LiteLLM 替换直接 provider,RemoteLLMProvider 保留。** LiteLLM 原生支持 Volcengine/Doubao;Wenxin/Yuanbao 通过 OpenAI 兼容端点。`RemoteLLMProvider` 是客户端→服务端代理(架构上不同于直接 API provider),LiteLLM 无法替代。语义缓存/fallback/用量追踪保留自研上层逻辑,底层 provider 适配走 LiteLLM。
|
**KTD7: LiteLLM 替换直接 provider,RemoteLLMProvider 保留。** LiteLLM 原生支持 Volcengine/Doubao;Wenxin/Yuanbao 通过 OpenAI 兼容端点。`RemoteLLMProvider` 是客户端→服务端代理(架构上不同于直接 API provider),LiteLLM 无法替代。fallback/用量追踪保留自研上层逻辑,底层 provider 适配走 LiteLLM。语义缓存在 P3(U17)由自研替换为 LiteLLM 内置 Redis Semantic Cache — KTD7 保留自研指的是 P1/P2 阶段,P3 完成后自研缓存废弃。
|
||||||
|
|
||||||
**KTD8: 加密 DB 列存储平台凭证。** 单部署企业场景下,加密 DB 列 + 环境变量 master key 足够。避免引入 HashiCorp Vault 外部依赖。凭证轮换通过 API 触发 re-encrypt。访问审计记录到现有审计日志。
|
**KTD8: 加密 DB 列存储平台凭证。** 单部署企业场景下,加密 DB 列 + master key 足够。避免引入 HashiCorp Vault 外部依赖。加密算法强制 AES-256-GCM(非泛指 "AES-256"),每行使用随机 96-bit nonce 与密文一同存储。采用 envelope encryption(master key 包裹 per-row data key)或 HKDF with per-row salt 派生 per-row 密钥。生产环境 master key 必须存储于云 KMS(AWS KMS / 阿里云 KMS),应用启动 guard 在生产模式下若仅检测到环境变量 key 则拒绝启动;环境变量仅作为开发环境 fallback。凭证轮换通过 API 触发 re-encrypt。访问审计记录到现有审计日志。Master key 轮换采用双密钥窗口策略:新 key 与旧 key 同时有效(DB 列存储 key_id 标识),后台批量 re-encrypt 旧密钥数据到新密钥,完成后旧 key 失效。Master key 泄露恢复流程从 Deferred 提升至 Risks(见 Risks & Dependencies)。
|
||||||
|
|
||||||
**KTD9: 新建 `src/agentkit/rag_platform/` 顶层模块。** 与 `memory/` 职责分离:`rag_platform/` 服务企业知识库场景,`memory/` 服务 Agent 运行时记忆。`LocalRAGService` 对 KB 场景废弃,Agent 记忆(EpisodicMemory)保留使用。
|
**KTD9: 新建 `src/agentkit/rag_platform/` 顶层模块。** 与 `memory/` 职责分离:`rag_platform/` 服务企业知识库场景,`memory/` 服务 Agent 运行时记忆。`LocalRAGService` 对 KB 场景废弃,Agent 记忆(EpisodicMemory)保留使用。明确决策:`rag_platform/` 定义自己的检索模型;现有 `memory/knowledge_base.py` 中的 `KnowledgeBase` protocol 对 KB 用途废弃。`MemoryRetriever` 将在新的单元中重新接线,直接调用 `rag_platform/retrieval.py`。由于当前无外部代码 import `LocalRAGService`,"代码重复"顾虑不再成立 — 真正的问题是 protocol 复用,答案是:新 protocol、清晰边界、`MemoryRetriever` 内置 adapter。
|
||||||
|
|
||||||
**KTD10: 成功标准从"MaxKB 功能对等"重构为"用户结果导向"。** MaxKB 是 RAG 知识库产品,AgentKit 是 Agent 平台。对标不同产品类别的功能对等可能构建不服务于实际用户的能力。成功标准改为:企业用户可上传文档、配置检索、测试召回、通过多端使用 Agent 检索知识库。
|
**KTD10: 成功标准从"MaxKB 功能对等"重构为"用户结果导向"。** MaxKB 是 RAG 知识库产品,AgentKit 是 Agent 平台。对标不同产品类别的功能对等可能构建不服务于实际用户的能力。成功标准改为:企业用户可上传文档、配置检索、测试召回、通过多端使用 Agent 检索知识库。
|
||||||
|
|
||||||
|
**KTD11: RetrievalPrincipal 用于非交互路径的 ACL 强制执行。** 定义 `RetrievalPrincipal` 概念,封装用户身份用于 ACL 过滤。WebSocket chat:principal = JWT 用户。MCP 工具调用:principal = per-client API Key 绑定的身份(要求 per-client keys,非全局 key)。TaskIQ worker:principal 序列化进任务 payload 并在 worker 内 re-hydrate。多端 webhook:IM 用户 → AgentKit 用户映射步骤。没有此机制,非交互路径的 ACL 会被静默绕过。
|
||||||
|
|
||||||
## High-Level Technical Design
|
## High-Level Technical Design
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
|
@ -91,7 +94,7 @@ flowchart TB
|
||||||
U3 --> U4[U4 双索引检索]
|
U3 --> U4[U4 双索引检索]
|
||||||
U4 --> U5[U5 Rerank/问题生成/术语表]
|
U4 --> U5[U5 Rerank/问题生成/术语表]
|
||||||
U5 --> U6[U6 命中处理 + KB 设置]
|
U5 --> U6[U6 命中处理 + KB 设置]
|
||||||
U2 --> U7[U7 上传安全 + 净化]
|
U3 --> U7[U7 上传安全 + 净化]
|
||||||
U3 --> U8[U8 TaskIQ 异步任务]
|
U3 --> U8[U8 TaskIQ 异步任务]
|
||||||
U6 --> U9[U9 前端 KB 管理]
|
U6 --> U9[U9 前端 KB 管理]
|
||||||
U8 --> U9
|
U8 --> U9
|
||||||
|
|
@ -105,9 +108,8 @@ flowchart TB
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph P3["P3: 生态替换降本"]
|
subgraph P3["P3: 生态替换降本"]
|
||||||
U15[U15 LiteLLM Provider]
|
U15[U15 LiteLLM Provider] --> U17[U17 LiteLLM 语义缓存]
|
||||||
U16[U16 langchain-mcp-adapters]
|
U16[U16 langchain-mcp-adapters]
|
||||||
U17[U17 LiteLLM 语义缓存]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
P1 --> P2 --> P3
|
P1 --> P2 --> P3
|
||||||
|
|
@ -163,6 +165,7 @@ flowchart LR
|
||||||
- `src/agentkit/rag_platform/pipeline.py` — LlamaIndex IngestionPipeline 封装
|
- `src/agentkit/rag_platform/pipeline.py` — LlamaIndex IngestionPipeline 封装
|
||||||
- `src/agentkit/rag_platform/indexing.py` — pgvector 索引管理(LlamaIndex PGVectorStore)
|
- `src/agentkit/rag_platform/indexing.py` — pgvector 索引管理(LlamaIndex PGVectorStore)
|
||||||
- **Patterns:** 遵循现有模块结构(cf. `memory/__init__.py`, `mcp/__init__.py`);LlamaIndex `PGVectorStore` 连接现有 PostgreSQL。
|
- **Patterns:** 遵循现有模块结构(cf. `memory/__init__.py`, `mcp/__init__.py`);LlamaIndex `PGVectorStore` 连接现有 PostgreSQL。
|
||||||
|
- **pgvector Schema Isolation:** LlamaIndex `PGVectorStore` 必须使用显式表名(如 `rag_platform_kb_chunks`),不可使用默认 `data_<name>`。确认 `create_if_not_exists` 不会触碰 `episodic_memory` 或任何 `memory/` 所属表。KB chunk 表(含 `search_vector`)与 LlamaIndex 向量表应统一为单表(同时含两列)或通过 FK 关联并维持同步不变式:每个 chunk 行恰好对应一个 embedding 行。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- LlamaIndex PGVectorStore 连接现有 pgvector 扩展
|
- LlamaIndex PGVectorStore 连接现有 pgvector 扩展
|
||||||
- 基础 ingest(文档 → chunk → embedding → pgvector INSERT)端到端工作
|
- 基础 ingest(文档 → chunk → embedding → pgvector INSERT)端到端工作
|
||||||
|
|
@ -175,11 +178,13 @@ flowchart LR
|
||||||
|
|
||||||
- **Goal:** 替换内存 `KnowledgeSourceStore` 为 PostgreSQL 持久化,实现 per-KB ACL。
|
- **Goal:** 替换内存 `KnowledgeSourceStore` 为 PostgreSQL 持久化,实现 per-KB ACL。
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/rag_platform/store.py` — KB/Document 持久化(SQLAlchemy async)
|
- `src/agentkit/rag_platform/store.py` — KB/Document 持久化(SQLAlchemy async)+ `kb_acl` 表定义
|
||||||
|
- `src/agentkit/rag_platform/models.py` — ORM 模型(SQLAlchemy 2 `DeclarativeBase` + `Mapped`)
|
||||||
- `src/agentkit/rag_platform/acl.py` — per-KB ACL 逻辑
|
- `src/agentkit/rag_platform/acl.py` — per-KB ACL 逻辑
|
||||||
- `src/agentkit/server/auth/models.py` — 新增 `kb_acl` 表模型
|
|
||||||
- `src/agentkit/server/routes/kb_management.py` — 替换 `KnowledgeSourceStore` 调用
|
- `src/agentkit/server/routes/kb_management.py` — 替换 `KnowledgeSourceStore` 调用
|
||||||
- **Patterns:** 遵循 `memory/episodic.py` 的 EpisodicMemory PG 模式(async session);遵循 `filter_kb_sources_by_department` 模式实现 `filter_kb_by_user_acl()`。
|
- **Patterns:** 遵循 `memory/episodic.py` 的 EpisodicMemory PG 模式(async session);遵循 `filter_kb_sources_by_department` 模式实现 `filter_kb_by_user_acl()`。使用 SQLAlchemy 2 ORM(`DeclarativeBase` + `Mapped`)而非原始 SQL — 便于 Alembic 迁移演进 schema。Alembic 迁移在本单元范围内。
|
||||||
|
- **Migration note:** 现有内存 `KnowledgeSourceStore` 数据是 ephemeral 的;首次 PG 后端启动时,若内存 store 非空,记录 warning 日志以便运维重新注册 KB。这对未在 config 中捕获的 KB 状态是破坏性变更。
|
||||||
|
- **Invariant:** ACL 条目与 KB 元数据共享事务边界(同一 PG DB)。`kb_acl.kb_id` FK → KB 表 `id` ON DELETE CASCADE。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- KB 元数据写入 PG,重启后仍存在
|
- KB 元数据写入 PG,重启后仍存在
|
||||||
- owner 用户可查询自己的 KB
|
- owner 用户可查询自己的 KB
|
||||||
|
|
@ -196,7 +201,8 @@ flowchart LR
|
||||||
- `src/agentkit/rag_platform/document_processor.py` — 文档处理管道
|
- `src/agentkit/rag_platform/document_processor.py` — 文档处理管道
|
||||||
- `src/agentkit/rag_platform/preview.py` — 分段预览 API
|
- `src/agentkit/rag_platform/preview.py` — 分段预览 API
|
||||||
- `src/agentkit/server/routes/kb_management.py` — 重写 `upload_document()` 端点
|
- `src/agentkit/server/routes/kb_management.py` — 重写 `upload_document()` 端点
|
||||||
- **Patterns:** LlamaIndex `IngestionPipeline`(SentenceSplitter + MetadataExtractor);现有 `memory/document_loader.py` 的 `DocumentLoader` 用于解析;文档状态模型(pending→parsing→segmenting→vectorizing→indexed|failed)。
|
- **Patterns:** LlamaIndex `IngestionPipeline`(jieba-aware 自定义 SentenceSplitter + MetadataExtractor);现有 `memory/document_loader.py` 的 `DocumentLoader` 用于解析;文档状态模型(pending→parsing→segmenting→vectorizing→indexed|failed)。LlamaIndex 默认 `SentenceSplitter` 按英文标点/空格切分,中文场景需自定义 splitter(基于 jieba 分词边界切分)以保证分段质量。分段预览端点必须复用 `require_permission` + per-KB ACL 校验(owner/viewer 可预览,其他拒绝)。
|
||||||
|
- **Failure & Retry Semantics:** 向量化是 all-or-nothing(单个 PG 事务包裹所有 chunk+embedding INSERT)。失败时部分工作回滚。`segmenting` 阶段产生的孤儿 chunks 标记为 `orphaned` 并从检索中排除。重试从 `pending` 重新处理,按 document hash + chunk hash 去重。不变式:检索永不返回来自非 `indexed` 文档的 chunk。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 上传 PDF → 解析 → 分段 → 返回预览(只读)
|
- 上传 PDF → 解析 → 分段 → 返回预览(只读)
|
||||||
- 确认预览 → 向量化 → 索引 → 可检索
|
- 确认预览 → 向量化 → 索引 → 可检索
|
||||||
|
|
@ -213,7 +219,9 @@ flowchart LR
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/rag_platform/retrieval.py` — 检索逻辑(三模式)
|
- `src/agentkit/rag_platform/retrieval.py` — 检索逻辑(三模式)
|
||||||
- `src/agentkit/rag_platform/fulltext.py` — jieba 分词 + tsvector 写入/查询
|
- `src/agentkit/rag_platform/fulltext.py` — jieba 分词 + tsvector 写入/查询
|
||||||
- **Patterns:** LlamaIndex hybrid retriever(VectorStoreRetriever + NLSQLRetriever 或自定义);jieba 在 Python 层分词后写入 `search_vector` 列。
|
- `src/agentkit/rag_platform/models.py` — 新增 `RetrievalRequest` 模型(含可选 `retrieval_mode` 与 `hit_processing_mode` 覆盖字段,默认值为 KB 配置值)
|
||||||
|
- **Patterns:** LlamaIndex hybrid retriever(VectorStoreRetriever + NLSQLRetriever 或自定义);jieba 在 Python 层分词后写入 `search_vector` 列。集成机制:Python 层调用 `jieba.cut(text)` 得到 token 列表,用空格连接后写入 `tsvector` 列(`to_tsvector('simple', space_joined_tokens)`),查询时同样用 jieba 分词后构造 tsquery。避免依赖 PG 扩展(pg_jieba/zhparser)。
|
||||||
|
- **Agent access:** Agent 通过 `MemoryRetriever` 参数访问(隐式,按 session 配置)— 不是单独的 Tool。Chat handler 通过 Agent context 传播 per-query 覆盖。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- embedding 模式:语义检索返回相关结果
|
- embedding 模式:语义检索返回相关结果
|
||||||
- keywords 模式:中文全文检索返回包含关键词的结果
|
- keywords 模式:中文全文检索返回包含关键词的结果
|
||||||
|
|
@ -230,7 +238,7 @@ flowchart LR
|
||||||
- `src/agentkit/rag_platform/rerank.py` — rerank 模型集成(Cohere/BGE-Reranker 可配置)
|
- `src/agentkit/rag_platform/rerank.py` — rerank 模型集成(Cohere/BGE-Reranker 可配置)
|
||||||
- `src/agentkit/rag_platform/question_gen.py` — LLM-based 问题生成
|
- `src/agentkit/rag_platform/question_gen.py` — LLM-based 问题生成
|
||||||
- `src/agentkit/rag_platform/termbase.py` — 术语表管理 + jieba 自定义词典
|
- `src/agentkit/rag_platform/termbase.py` — 术语表管理 + jieba 自定义词典
|
||||||
- **Patterns:** LlamaIndex rerankers(`CohereRerank` 或 `SentenceTransformerRerank`);参考 `memory/contextual_retrieval.py` 的 ContextualChunker 模式生成问题;jieba `load_userdict()` 加载术语表。
|
- **Patterns:** LlamaIndex rerankers(`CohereRerank` 或 `SentenceTransformerRerank`);参考 `memory/contextual_retrieval.py` 的 ContextualChunker 模式生成问题;jieba `load_userdict()` 加载术语表。Rerank API 调用将文档 chunks 发送到第三方(Cohere 等),需在 KB 设置中标注数据出境风险并提供本地部署选项(BGE-Reranker via Xinference)作为敏感数据 KB 的默认。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- rerank 后结果相关性顺序改善
|
- rerank 后结果相关性顺序改善
|
||||||
- 问题生成产生与段落内容相关的问题
|
- 问题生成产生与段落内容相关的问题
|
||||||
|
|
@ -247,7 +255,7 @@ flowchart LR
|
||||||
- `src/agentkit/rag_platform/hit_processing.py` — 命中处理逻辑
|
- `src/agentkit/rag_platform/hit_processing.py` — 命中处理逻辑
|
||||||
- `src/agentkit/rag_platform/settings.py` — KB 设置模型(检索模式默认/命中处理默认/授权用户)
|
- `src/agentkit/rag_platform/settings.py` — KB 设置模型(检索模式默认/命中处理默认/授权用户)
|
||||||
- `src/agentkit/server/routes/kb_management.py` — KB 设置端点
|
- `src/agentkit/server/routes/kb_management.py` — KB 设置端点
|
||||||
- **Patterns:** 模型优化模式调用现有 LLM Gateway;直接回答模式返回匹配段落;KB 设置存 PG。
|
- **Patterns:** 模型优化模式调用现有 LLM Gateway;直接回答模式返回匹配段落;KB 设置存 PG。KB 设置端点必须复用 `require_permission` + per-KB ACL 校验(仅 owner 可修改设置,viewer 只读)。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- model_opt 模式:LLM 基于检索结果生成回答
|
- model_opt 模式:LLM 基于检索结果生成回答
|
||||||
- direct 模式:直接返回匹配段落
|
- direct 模式:直接返回匹配段落
|
||||||
|
|
@ -260,10 +268,17 @@ flowchart LR
|
||||||
### U7. 文件上传安全 + 内容净化
|
### U7. 文件上传安全 + 内容净化
|
||||||
|
|
||||||
- **Goal:** 后端文件类型白名单 + 内容净化。
|
- **Goal:** 后端文件类型白名单 + 内容净化。
|
||||||
|
- **Depends on:** U3(U7 重写 `upload_document()` 端点,U3 也重写同一端点 — 两者必须协同实施,U7 的白名单/净化逻辑嵌入 U3 的上传管道)
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/rag_platform/sanitize.py` — 内容净化(markdown sanitize + PDF 安全)
|
- `src/agentkit/rag_platform/sanitize.py` — 内容净化(markdown sanitize + PDF 安全)
|
||||||
- `src/agentkit/server/routes/kb_management.py` — 上传端点增加白名单验证
|
- `src/agentkit/server/routes/kb_management.py` — 上传端点增加白名单验证
|
||||||
- **Patterns:** `DocumentLoader._detect_format()` 映射作为白名单源;`bleach` 或 `markdown` 库净化 HTML/markdown;PDF 解析限制页面数/大小。
|
- **Patterns:** `memory/document_loader.py` 的模块级函数 `_detect_format()`(注意:是模块级函数,非 `DocumentLoader` 方法)映射作为白名单源;`bleach` 或 `markdown` 库净化 HTML/markdown;PDF 解析限制页面数/大小。
|
||||||
|
- **Expanded attack surface:**
|
||||||
|
- (a) ZIP 类格式(`.docx/.xlsx/.pptx`)的解压比率限制 — 比率 > 100:1 或解压后 > N MB 时拒绝(防 zip bomb)。
|
||||||
|
- (b) XML parser 加固 — 对 XML-based 格式禁用 ENTITY 解析、外部 DTD、XInclude(防 XXE)。
|
||||||
|
- (c) URL egress 过滤 — deny by default(allowlist 模式)。Blocked ranges 必须包含:127.0.0.0/8(loopback)、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16(RFC1918)、169.254.0.0/16(link-local + cloud metadata)、::1(IPv6 loopback)、fc00::/7(IPv6 ULA)、fe80::/10(IPv6 link-local)。优先使用 egress allowlist(resolve target,仅允许 public IP)以覆盖未来内部范围。
|
||||||
|
- (d) 嵌入图片的像素数上限(防 image bomb)。
|
||||||
|
- (e) 若使用 LlamaParse 托管服务,添加与 U5 rerank note 相同的数据出境警告 + per-KB opt-in。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 白名单外文件类型被拒绝(.exe, .sh 等)
|
- 白名单外文件类型被拒绝(.exe, .sh 等)
|
||||||
- 超大小限制文件被拒绝
|
- 超大小限制文件被拒绝
|
||||||
|
|
@ -281,6 +296,14 @@ flowchart LR
|
||||||
- `src/agentkit/server/app.py` — TaskIQ startup/shutdown
|
- `src/agentkit/server/app.py` — TaskIQ startup/shutdown
|
||||||
- `src/agentkit/server/task_store.py` — 扩展状态跟踪(复用现有 TaskStore 模式)
|
- `src/agentkit/server/task_store.py` — 扩展状态跟踪(复用现有 TaskStore 模式)
|
||||||
- **Patterns:** TaskIQ FastAPI 集成(`TaskiqMiddleware`);现有 Redis 作为 broker;`TaskStore` 状态模型(PENDING/RUNNING/COMPLETED/FAILED)。
|
- **Patterns:** TaskIQ FastAPI 集成(`TaskiqMiddleware`);现有 Redis 作为 broker;`TaskStore` 状态模型(PENDING/RUNNING/COMPLETED/FAILED)。
|
||||||
|
- **Worker liveness & sweeper:** 在 document/task 记录上增加 `worker_heartbeat_at` 时间戳列,由 worker 周期性更新。新增 sweeper 任务(TaskIQ scheduled job),将卡在 `parsing`/`segmenting`/`vectorizing` 超过 TTL 的记录转回 `failed` 并写入 `error_message="worker_timeout"`。测试场景:"worker 在 vectorization 中途死亡 → sweeper 在 TTL 后转为 failed → 用户可重试。"
|
||||||
|
- **Redis isolation:** TaskIQ broker 使用独立 Redis DB index(如 `db=1`)或 key 前缀 `taskiq:`。连接池与 bus/cache 分离。
|
||||||
|
- **Security:**
|
||||||
|
- (a) per-user 任务并发上限 + per-task 内存/CPU 上限。
|
||||||
|
- (b) 任务参数 schema 校验(每种 task type 一个 Pydantic 模型)。
|
||||||
|
- (c) `error_message` 在暴露给用户前净化(剥离内部路径、redact 租户标识)。
|
||||||
|
- (d) 任务参数在 Redis 中带 TTL(完成后 + grace period 自动过期)。
|
||||||
|
- (e) worker 权限分离建议(worker 进程以低于主服务的权限运行)。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 大文档(50MB PDF)向量化在后台执行,不阻塞事件循环
|
- 大文档(50MB PDF)向量化在后台执行,不阻塞事件循环
|
||||||
- 任务状态可查询(pending→running→completed)
|
- 任务状态可查询(pending→running→completed)
|
||||||
|
|
@ -294,17 +317,21 @@ flowchart LR
|
||||||
|
|
||||||
- **Goal:** 扩展 KnowledgeBaseView/DocumentUpload/SearchTest 组件,增加分段预览、状态展示、KB 设置。
|
- **Goal:** 扩展 KnowledgeBaseView/DocumentUpload/SearchTest 组件,增加分段预览、状态展示、KB 设置。
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/server/frontend/src/components/knowledge/KnowledgeBaseView.vue` — 扩展
|
- `src/agentkit/server/frontend/src/views/KnowledgeBaseView.vue` — 扩展
|
||||||
- `src/agentkit/server/frontend/src/components/knowledge/DocumentUpload.vue` — 增加状态展示
|
- `src/agentkit/server/frontend/src/components/kb/DocumentUpload.vue` — 增加状态展示
|
||||||
- `src/agentkit/server/frontend/src/components/knowledge/SearchTest.vue` — 扩展检索测试
|
- `src/agentkit/server/frontend/src/components/kb/SearchTest.vue` — 扩展检索测试
|
||||||
- `src/agentkit/server/frontend/src/components/knowledge/SegmentPreview.vue` — 新建分段预览组件
|
- `src/agentkit/server/frontend/src/components/kb/SourceConfig.vue` — 扩展 ACL 配置(现有组件,可能需扩展以支持 per-KB ACL 设置)
|
||||||
- `src/agentkit/server/frontend/src/components/knowledge/KBSettings.vue` — 新建 KB 设置组件
|
- `src/agentkit/server/frontend/src/components/kb/SegmentPreview.vue` — 新建分段预览组件
|
||||||
|
- `src/agentkit/server/frontend/src/components/kb/KBSettings.vue` — 新建 KB 设置组件
|
||||||
- **Patterns:** 现有 Ant Design Vue 组件模式;WebSocket 推送文档处理状态(复用现有 ws 协议)。
|
- **Patterns:** 现有 Ant Design Vue 组件模式;WebSocket 推送文档处理状态(复用现有 ws 协议)。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 上传后显示处理进度(pending→parsing→...→indexed)
|
- 上传后显示处理进度(pending→parsing→...→indexed)
|
||||||
- 分段预览显示分段结果(只读)
|
- 分段预览显示分段结果(只读)
|
||||||
- 检索测试支持三模式切换
|
- 检索测试支持三模式切换
|
||||||
- KB 设置可配置检索模式默认/命中处理默认/授权用户
|
- KB 设置可配置检索模式默认/命中处理默认/授权用户
|
||||||
|
- 文档处理失败时显示 failed 状态 + error_message
|
||||||
|
- 失败文档可触发重试(UI 按钮 → 重新提交 TaskIQ 任务)
|
||||||
|
- 任务历史列表可查看(按 KB/时间筛选,显示状态/耗时/错误)
|
||||||
- **Verification:** `npm run typecheck` + 手动验证
|
- **Verification:** `npm run typecheck` + 手动验证
|
||||||
|
|
||||||
### P2: 平台触达扩展
|
### P2: 平台触达扩展
|
||||||
|
|
@ -313,13 +340,14 @@ flowchart LR
|
||||||
|
|
||||||
### U10. 多端消息适配器骨架 + secrets store
|
### U10. 多端消息适配器骨架 + secrets store
|
||||||
|
|
||||||
- **Goal:** 创建 MessageAdapter 协议 + secrets store 基础设施。
|
- **Goal:** 创建 MessageAdapter ABC + secrets store 基础设施。
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/channels/__init__.py` — 模块入口
|
- `src/agentkit/channels/__init__.py` — 模块入口
|
||||||
- `src/agentkit/channels/base.py` — MessageAdapter 协议(receive_message/send_message/verify_signature)
|
- `src/agentkit/channels/base.py` — `MessageAdapter` ABC(receive_message/send_message/verify_signature/close,与 `KBAdapter` 的 `authenticate()`/`close()` 生命周期方法对齐 — `verify_signature()` 对应 `authenticate()`)
|
||||||
- `src/agentkit/channels/secrets.py` — 加密 DB 列 secrets store
|
- `src/agentkit/channels/secrets.py` — 加密 DB 列 secrets store
|
||||||
- `src/agentkit/server/routes/channels.py` — 渠道管理端点
|
- `src/agentkit/server/routes/channels.py` — 渠道管理端点
|
||||||
- **Patterns:** 新 `MessageAdapter` 协议(cf. `memory/adapters/base.py` 的 KBAdapter);加密 DB 列(AES-256,master key 来自环境变量)。
|
- **Patterns:** `MessageAdapter` 应为 ABC(与 `KBAdapter` 模式一致),不是 Protocol。加密 DB 列按 KTD8 更新:AES-256-GCM + per-row 96-bit nonce,envelope encryption 或 HKDF with per-row salt。渠道管理端点必须复用 `require_permission`(管理员级 CRUD 权限)。Webhook 端点必须实现重放攻击保护:验证请求时间戳(拒绝超出窗口的请求,飞书 5 分钟、钉钉 1 小时)+ nonce 去重(Redis 缓存已见 nonce,TTL 与时间戳窗口一致)。渠道 onboarding 前端需新增 `ChannelConfig.vue` 组件(管理员导航→选择平台→输入凭证→系统生成 webhook URL→连通性测试),归入 U10 范围。
|
||||||
|
- **Webhook fail-closed:** webhook 端点在 Redis 不可用时必须返回 503(fail-closed),不可跳过 nonce dedup 直接处理消息。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- secrets 写入后加密存储(非明文)
|
- secrets 写入后加密存储(非明文)
|
||||||
- secrets 读取时解密
|
- secrets 读取时解密
|
||||||
|
|
@ -336,6 +364,7 @@ flowchart LR
|
||||||
- `src/agentkit/channels/feishu.py` — 飞书 IM 适配器
|
- `src/agentkit/channels/feishu.py` — 飞书 IM 适配器
|
||||||
- `src/agentkit/server/routes/channels.py` — 飞书 webhook 端点
|
- `src/agentkit/server/routes/channels.py` — 飞书 webhook 端点
|
||||||
- **Patterns:** 飞书 webhook 签名验证(encrypt_key + AES 解密);消息格式转换(飞书事件 → AgentKit 标准消息);现有 chat handler 集成(`RequestPreprocessor` → `ExecutionMode`)。
|
- **Patterns:** 飞书 webhook 签名验证(encrypt_key + AES 解密);消息格式转换(飞书事件 → AgentKit 标准消息);现有 chat handler 集成(`RequestPreprocessor` → `ExecutionMode`)。
|
||||||
|
- **Rate limiting:** webhook 端点必须在签名验证前应用 per-IP rate limiting(默认 100 req/min),超限返回 429。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 飞书消息 → webhook → 签名验证 → Agent 处理 → 响应返回飞书
|
- 飞书消息 → webhook → 签名验证 → Agent 处理 → 响应返回飞书
|
||||||
- 无效签名请求被拒绝
|
- 无效签名请求被拒绝
|
||||||
|
|
@ -353,6 +382,7 @@ flowchart LR
|
||||||
- `src/agentkit/channels/wecom.py` — 企微适配器
|
- `src/agentkit/channels/wecom.py` — 企微适配器
|
||||||
- `src/agentkit/channels/slack.py` — Slack 适配器
|
- `src/agentkit/channels/slack.py` — Slack 适配器
|
||||||
- **Patterns:** 遵循 U11 飞书模式;平台特定签名验证(钉钉 token、企微 EncodingAESKey、Slack signing secret)。
|
- **Patterns:** 遵循 U11 飞书模式;平台特定签名验证(钉钉 token、企微 EncodingAESKey、Slack signing secret)。
|
||||||
|
- **Rate limiting:** webhook 端点必须在签名验证前应用 per-IP rate limiting(默认 100 req/min),超限返回 429。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 每个平台端到端消息流
|
- 每个平台端到端消息流
|
||||||
- 每个平台签名验证拒绝无效请求
|
- 每个平台签名验证拒绝无效请求
|
||||||
|
|
@ -366,7 +396,10 @@ flowchart LR
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/mcp/server.py` — 重构为路由工厂(`create_mcp_router()`)
|
- `src/agentkit/mcp/server.py` — 重构为路由工厂(`create_mcp_router()`)
|
||||||
- `src/agentkit/server/app.py` — 挂载 MCP 路由到 `/api/v1/mcp/`
|
- `src/agentkit/server/app.py` — 挂载 MCP 路由到 `/api/v1/mcp/`
|
||||||
- **Patterns:** `require_permission` 依赖注入;`APIKeyHeader` 认证;现有 `ToolRegistry.list_tools()` 暴露为 MCP 工具。
|
- **Patterns:** `require_permission` 依赖注入;`APIKeyHeader` 认证;现有 `ToolRegistry.list_tools()` 暴露为 MCP 工具。向后兼容:现有独立 `mcp/server.py` 的客户端可能硬编码旧路径,迁移期间保留旧路径的 301 重定向到 `/api/v1/mcp/`(至少 1 个版本周期),并在发布说明中标注破坏性变更。
|
||||||
|
- **API Key → permission mapping:** MCP 必须使用 per-client API keys(非全局 server key),每个 key 绑定到一个 synthetic principal,带显式 permission scope(`Permission` bits 的子集)和可选 KB-allowlist。MCP per-client API keys 必须存储在 U10 的加密 secrets store 中(非 clients.yaml 明文)。`pair` CLI 命令应扩展以 mint MCP-scoped keys。
|
||||||
|
- **Port-change breakage:** 现有 `mcp/server.py` 运行在 port 8080;合并到主 app(port 8001)会破坏硬编码端口的客户端。要么保留 reverse-proxy/redirect 从 8080 → 主 app,要么文档化 port 8080 deprecation 与迁移时间线。
|
||||||
|
- **MCP endpoint permission levels:** `tools/list` = member,`tools/call` = member,publish = `Permission.SYSTEM_CONFIG`(注意:`Permission.ADMIN` 不存在 — admin role 拥有 `SYSTEM_CONFIG` 权限)。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 无认证调用 `/api/v1/mcp/tools/list` 被拒绝(401)
|
- 无认证调用 `/api/v1/mcp/tools/list` 被拒绝(401)
|
||||||
- 有效 API Key 调用返回工具列表
|
- 有效 API Key 调用返回工具列表
|
||||||
|
|
@ -379,11 +412,13 @@ flowchart LR
|
||||||
### U14. Skill/专家团队 MCP 发布
|
### U14. Skill/专家团队 MCP 发布
|
||||||
|
|
||||||
- **Goal:** 支持 Skill/专家团队发布为 MCP 工具。
|
- **Goal:** 支持 Skill/专家团队发布为 MCP 工具。
|
||||||
|
- **Depends on:** U13(U14 扩展 `mcp/server.py`,U13 先重构该文件 — U13 必须先完成)
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/mcp/publisher.py` — Skill/Team → MCP Tool 适配器
|
- `src/agentkit/mcp/publisher.py` — Skill/Team → MCP Tool 适配器
|
||||||
- `src/agentkit/server/routes/mcp_publish.py` — 发布管理端点
|
- `src/agentkit/server/routes/mcp_publish.py` — 发布管理端点
|
||||||
- `src/agentkit/mcp/server.py` — 扩展工具列表包含已发布 Skill/Team
|
- `src/agentkit/mcp/server.py` — 扩展工具列表包含已发布 Skill/Team
|
||||||
- **Patterns:** Tool 适配器包装(Skill/Team → `Tool` 接口);管理员级授权(`Permission.ADMIN`);配置字段(工具名称/描述/输入 schema/鉴权方式/速率限制)。
|
- **Patterns:** Tool 适配器包装(Skill/Team → `Tool` 接口);管理员级授权使用 `Permission.SYSTEM_CONFIG`(admin role 拥有此权限;`Permission.ADMIN` 不存在)— 或在 enum 中新增 `Permission.MCP_PUBLISH`。配置字段(工具名称/描述/输入 schema/鉴权方式/速率限制)。
|
||||||
|
- **Dangerous tools:** 包装危险工具(terminal、file)的已发布 Skill 必须要求显式 per-tool admin approval,并默认排除出 MCP 发布,需 opt-in flag。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 管理员发布 Skill 为 MCP 工具
|
- 管理员发布 Skill 为 MCP 工具
|
||||||
- 非管理员发布被拒绝
|
- 非管理员发布被拒绝
|
||||||
|
|
@ -399,11 +434,13 @@ flowchart LR
|
||||||
### U15. LiteLLM Provider 替换
|
### U15. LiteLLM Provider 替换
|
||||||
|
|
||||||
- **Goal:** 用 LiteLLM 替换 6 个直接 API provider 适配器。
|
- **Goal:** 用 LiteLLM 替换 6 个直接 API provider 适配器。
|
||||||
|
- **Depends on:** U10(凭证迁移到 secrets store 要求 U10 的 secrets store 先建好)
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/llm/providers.py` — 重写为 LiteLLM 统一接口
|
- `src/agentkit/llm/providers/` — 重写为 LiteLLM 统一接口(package 目录,含 6 个 provider 模块 + `tracker.py` + `usage_store.py`)。明确排除 `tracker.py` 和 `usage_store.py` 出 rewrite 范围(usage tracking 保留)。
|
||||||
- `src/agentkit/llm/gateway.py` — 适配上层网关逻辑
|
- `src/agentkit/llm/gateway.py` — 适配上层网关逻辑
|
||||||
- `src/agentkit/llm/config.py` — provider 配置更新
|
- `src/agentkit/llm/config.py` — provider 配置更新
|
||||||
- **Patterns:** LiteLLM `completion()` / `acompletion()` 统一接口;保留自研 fallback 链/用量追踪/部门级配额;`RemoteLLMProvider` 保留不动。
|
- **Patterns:** LiteLLM `completion()` / `acompletion()` 统一接口;保留自研 fallback 链/用量追踪/部门级配额;`RemoteLLMProvider` 保留不动。本单元同时负责迁移现有明文 `ProviderConfig.api_key` 到 U10 的加密 secrets store(读取现有 DB 列 → 加密写入 → 验证 → 删除明文列或标记 deprecated)。
|
||||||
|
- **Migration safety:** dual-write/dual-read 窗口 — 新加密列与明文列并行写入;读取优先 encrypted,fallback 到 plaintext。明文列在每行加密值验证后立即用 NULL 覆盖(非仅标记 deprecated)。明文列在迁移完成(100% 行迁移+验证)后的下一个 release 中 drop。per-row 迁移状态 flag 用于 partial-failure resume。文档化 rollback procedure。
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 6 个 provider 通过 LiteLLM 调用成功(OpenAI/Anthropic/Gemini/Doubao/Wenxin/Yuanbao)
|
- 6 个 provider 通过 LiteLLM 调用成功(OpenAI/Anthropic/Gemini/Doubao/Wenxin/Yuanbao)
|
||||||
- fallback 链在 provider 失败时切换
|
- fallback 链在 provider 失败时切换
|
||||||
|
|
@ -435,10 +472,17 @@ flowchart LR
|
||||||
### U17. LiteLLM 语义缓存集成
|
### U17. LiteLLM 语义缓存集成
|
||||||
|
|
||||||
- **Goal:** 用 LiteLLM 内置 Redis Semantic Cache 替换自研语义缓存。
|
- **Goal:** 用 LiteLLM 内置 Redis Semantic Cache 替换自研语义缓存。
|
||||||
|
- **Depends on:** U15(U17 修改 `gateway.py` 缓存配置,U15 也修改 `gateway.py` — U15 必须先完成 provider 替换,U17 在此基础上叠加缓存配置);U6(per-KB "caching disabled" flag 在 U6 KB 设置中定义,U17 必须读取此 flag)
|
||||||
- **Files:**
|
- **Files:**
|
||||||
- `src/agentkit/llm/cache.py` — 重写为 LiteLLM 缓存配置(或废弃,由 gateway 直接配置)
|
- `src/agentkit/llm/cache.py` — 重写为 LiteLLM 缓存配置(或废弃,由 gateway 直接配置)
|
||||||
- `src/agentkit/llm/gateway.py` — 集成 LiteLLM caching 配置
|
- `src/agentkit/llm/gateway.py` — 集成 LiteLLM caching 配置
|
||||||
- **Patterns:** LiteLLM `RedisSemanticCache`;阈值调优(默认 0.87);缓存 key 包含 system prompt + temperature。
|
- **Patterns:** LiteLLM `RedisSemanticCache`;阈值调优(默认 0.87);缓存 key 必须显式配置包含 system prompt + temperature(LiteLLM 默认 cache key 排除 system prompt,会导致不同 system prompt 的查询误命中缓存 — 需通过 `cache_params` 显式覆盖)。
|
||||||
|
- **Security & confidentiality (reframed from quality):** 缓存风险本质是 SECURITY/confidentiality 问题,非仅质量问题。要求:
|
||||||
|
- (a) cache key 必须包含 tenant/user scope(per-user cache namespace),杜绝跨用户碰撞。
|
||||||
|
- (b) 对源自 ACL-scoped KBs 的 `model_opt` 响应,要么禁用缓存,要么在 cache key 中包含 KB-ACL-scope hash(hash 必须包含 ACL 版本号或 permission-set hash,ACL 变更时必须触发缓存失效)。
|
||||||
|
- (c) 在 KB 设置(U6)中增加 per-KB "caching disabled" flag。
|
||||||
|
- (d) LiteLLM 必须以 library mode(in-process)运行 — 若必须 proxy mode,强制 mTLS。
|
||||||
|
- (e) 测试场景:"User A 查询不返回 User B 的缓存响应,即使语义相似。"
|
||||||
- **Test scenarios:**
|
- **Test scenarios:**
|
||||||
- 语义相似查询命中缓存
|
- 语义相似查询命中缓存
|
||||||
- 不同 system prompt 不命中缓存
|
- 不同 system prompt 不命中缓存
|
||||||
|
|
@ -446,6 +490,14 @@ flowchart LR
|
||||||
- 阈值调优生效
|
- 阈值调优生效
|
||||||
- **Verification:** `pytest tests/unit/llm/test_cache.py`
|
- **Verification:** `pytest tests/unit/llm/test_cache.py`
|
||||||
|
|
||||||
|
### Cross-Unit Integration Tests
|
||||||
|
|
||||||
|
- `tests/integration/test_upload_pipeline.py` — upload → sanitize → parse → segment → vectorize → index → retrieve(覆盖 U3+U7+U4)
|
||||||
|
- `tests/integration/test_llm_gateway_e2e.py` — LiteLLM provider + fallback + cache + usage tracking + quota(覆盖 U15+U17)
|
||||||
|
- `tests/integration/test_mcp_e2e.py` — auth + published Skill tool + existing tool call(覆盖 U13+U14)
|
||||||
|
- `tests/integration/test_rag_agent_runtime.py` — Agent → MemoryRetriever → rag_platform retrieval with ACL(覆盖 U2+U4+RetrievalPrincipal)
|
||||||
|
- 所有测试按项目约定标记 `@pytest.mark.integration`。
|
||||||
|
|
||||||
## Scope Boundaries
|
## Scope Boundaries
|
||||||
|
|
||||||
### In scope
|
### In scope
|
||||||
|
|
@ -475,25 +527,102 @@ flowchart LR
|
||||||
- **性能姿态:** 文档向量化从同步(阻塞事件循环)变为 TaskIQ 异步;检索从单索引变为双索引 + rerank(增加延迟但提升相关性)。
|
- **性能姿态:** 文档向量化从同步(阻塞事件循环)变为 TaskIQ 异步;检索从单索引变为双索引 + rerank(增加延迟但提升相关性)。
|
||||||
- **共享基础设施:** Redis 新增 TaskIQ broker 角色(与现有 bus/cache 共存);PostgreSQL 新增 RAG 平台 schema。
|
- **共享基础设施:** Redis 新增 TaskIQ broker 角色(与现有 bus/cache 共存);PostgreSQL 新增 RAG 平台 schema。
|
||||||
- **Agent/工具对等:** Agent 运行时通过 RAG 平台检索 KB 内容;外部 AI 系统通过 MCP 调用已发布 Skill/团队。
|
- **Agent/工具对等:** Agent 运行时通过 RAG 平台检索 KB 内容;外部 AI 系统通过 MCP 调用已发布 Skill/团队。
|
||||||
|
- **Agent runtime retrieval path:** `MemoryRetriever` rewire + user-context propagation for ACL(RetrievalPrincipal 概念,见 KTD11)。
|
||||||
- **向后兼容:** `LocalRAGService` 对 KB 场景废弃但保留(Agent 记忆仍用);`RemoteLLMProvider` 保留不动;现有 `ToolRegistry` 工具仍可通过 MCP 访问。
|
- **向后兼容:** `LocalRAGService` 对 KB 场景废弃但保留(Agent 记忆仍用);`RemoteLLMProvider` 保留不动;现有 `ToolRegistry` 工具仍可通过 MCP 访问。
|
||||||
|
|
||||||
|
### Redis Resource Isolation
|
||||||
|
|
||||||
|
每个 consumer 的 key-prefix 契约:`taskiq:`、`litellm-cache:`、`bus:`、`wm:`。每 consumer 的内存预算估算(尤其 LiteLLM semantic cache 存储 embeddings)。必需的 `maxmemory-policy`:推荐 `noeviction` 用于 TaskIQ broker — fail loud 而非静默丢任务。LiteLLM semantic cache 应使用独立 Redis DB index(如 `db=2`)以隔离内存压力。
|
||||||
|
|
||||||
## Risks & Dependencies
|
## Risks & Dependencies
|
||||||
|
|
||||||
- **LlamaIndex breaking changes:** LlamaIndex 频繁发布 breaking changes。缓解:版本锁定(`pyproject.toml` pin major version)+ 集成测试覆盖核心管道。
|
- **LlamaIndex breaking changes:** LlamaIndex 频繁发布 breaking changes。缓解:版本锁定(`pyproject.toml` pin major version)+ 集成测试覆盖核心管道。
|
||||||
- **jieba 中文分词质量:** jieba 默认词典可能不覆盖领域术语。缓解:术语表通过 `jieba.load_userdict()` 扩展;检索测试覆盖中文场景。
|
- **jieba 中文分词质量:** jieba 默认词典可能不覆盖领域术语。缓解:术语表通过 `jieba.load_userdict()` 扩展;检索测试覆盖中文场景。
|
||||||
- **TaskIQ 成熟度:** TaskIQ 社区较小(~2k★)。缓解:API 简单,必要时可替换为 SAQ 或回退到 ProcessPoolExecutor。
|
- **TaskIQ 成熟度:** TaskIQ 社区较小(~2k★)。缓解:API 简单,必要时可替换为 SAQ(asyncio-native)或 Dramatiq(async adapter)。注意 ProcessPoolExecutor 不可作为 fallback — 它不兼容 asyncio 事件循环(会在 worker 中阻塞),仅适用于 CPU-bound 同步任务。
|
||||||
- **LiteLLM 中文 provider 覆盖:** Wenxin/Yuanbao 通过 OpenAI 兼容端点,可能缺少 provider 特定功能。缓解:feature-gap 分析在 U15 实施时执行;保留自定义 handler 作为 fallback。
|
- **LiteLLM 中文 provider 覆盖:** Wenxin/Yuanbao 通过 OpenAI 兼容端点,可能缺少 provider 特定功能。缓解:feature-gap 分析在 U15 实施时执行;保留自定义 handler 作为 fallback。
|
||||||
- **MCP Server 合并破坏现有集成:** 合并至主 app 可能影响现有 MCP 客户端调用。缓解:保持 `/api/v1/mcp/` 路径与现有 MCP 协议兼容;迁移测试。
|
- **MCP Server 合并破坏现有集成:** 合并至主 app 可能影响现有 MCP 客户端调用。缓解:保持 `/api/v1/mcp/` 路径与现有 MCP 协议兼容;迁移测试。
|
||||||
- **向后兼容性验证:** R15-R17 替换核心组件,"现有功能行为不变"是假设非验证。缓解:每个替换单元必须有 feature-parity 测试(现有行为 → 新实现行为对比)。
|
- **向后兼容性验证:** R15-R17 替换核心组件,"现有功能行为不变"是假设非验证。缓解:每个替换单元必须有 feature-parity 测试(现有行为 → 新实现行为对比)。
|
||||||
- **GPL v3 合规边界:** MaxKB 作为备选集成方案的 GPL v3 许可证。缓解:本计划不实施 MaxKB 集成(仅作备选);若未来集成,通过 REST API 独立服务调用(不修改/分发 MaxKB 代码)。
|
- **GPL v3 合规边界:** MaxKB 作为备选集成方案的 GPL v3 许可证。缓解:本计划不实施 MaxKB 集成(仅作备选);若未来集成,通过 REST API 独立服务调用(不修改/分发 MaxKB 代码)。
|
||||||
|
- **Redis outage:** bus + TaskIQ + cache + WorkingMemory 同时失败 — 缓解:Redis HA(Sentinel/Cluster)是生产环境的硬依赖,或接受 degraded mode 并文档化 blast radius。
|
||||||
|
- **MCP API Key scope creep:** 全局 key 复用授予无界访问。缓解:per-client API keys 带显式 permission scope。
|
||||||
|
- **TaskIQ worker compromise via malicious document:** 缓解:argument validation、resource caps、error sanitization、worker privilege separation。
|
||||||
|
- **LiteLLM semantic cache cross-tenant data leakage:** 缓解:per-user cache namespace、ACL-scope hash in cache key、per-KB caching-disable flag。
|
||||||
|
- **Master key leakage:** 需 incident-response runbook:detection、emergency re-encrypt、historical-credential revocation。(从 Deferred 提升。)
|
||||||
|
- **Document parsing supply-chain attacks (zip bombs, XXE, SSRF):** 缓解:decompression limits、XML hardening、egress filtering。
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
1. **门户触达(P2)反转门户价值主张:** 门户平台核心价值是触达,但多端接入在 P2。是否在 P1 并行交付至少一个高价值渠道(如飞书 IM)?默认假设:不并行,P1 聚焦 RAG 管道,P2 再做多端。
|
1. **门户触达(P2)反转门户价值主张:** 门户平台核心价值是触达,但多端接入在 P2。是否在 P1 并行交付至少一个高价值渠道(如飞书 IM)?默认假设:不并行,P1 聚焦 RAG 管道,P2 再做多端。
|
||||||
2. **R11-R13 是技术债非产品需求:** R15-R17(原 R11-R13)成功标准是"现有功能行为不变"(零用户可见影响),无 Actor 受益。是否移至独立工程债轨道?默认假设:保留在 P3,作为 commodity 层降本。
|
2. **R15-R17 是技术债非产品需求:** R15-R17 成功标准是"现有功能行为不变"(零用户可见影响),无 Actor 受益。是否移至独立工程债轨道?默认假设:保留在 P3,作为 commodity 层降本。
|
||||||
3. **rerank 模型选择:** U5 rerank 模型未指定(Cohere Rerank vs BGE-Reranker vs 其他)。默认假设:API-based(Cohere Rerank 或 BGE-Reranker via Xinference),可配置。
|
3. **rerank 模型选择:** U5 rerank 模型未指定(Cohere Rerank vs BGE-Reranker vs 其他)。默认假设:API-based(Cohere Rerank 或 BGE-Reranker via Xinference),可配置。
|
||||||
4. **多端 onboarding 流程细节:** U10-U12 的管理员配置流程(webhook URL 生成、app 凭证配置、连通性测试)需在实施时细化。默认假设:admin 导航到渠道配置 → 选择平台 → 输入凭证 → 系统生成 webhook URL → 管理员在平台配置 → 连通性测试。
|
4. **多端 onboarding 流程细节:** U10-U12 的管理员配置流程(webhook URL 生成、app 凭证配置、连通性测试)需在实施时细化。默认假设:admin 导航到渠道配置 → 选择平台 → 输入凭证 → 系统生成 webhook URL → 管理员在平台配置 → 连通性测试。
|
||||||
|
|
||||||
|
## Deferred / Open Questions
|
||||||
|
|
||||||
|
### From 2026-06-24 review
|
||||||
|
|
||||||
|
- **Master key 泄露恢复流程未定义** — KTD8/U10 (P1, security-lens, confidence 75)
|
||||||
|
|
||||||
|
KTD8 定义了 master key 轮换的双密钥窗口策略,但未定义 master key 泄露后的恢复流程。泄露场景下所有加密凭证需立即 re-encrypt 到新 key,但旧 key 已泄露意味着历史数据暴露。需定义:泄露检测机制、紧急轮换流程、历史凭证失效策略、审计追溯。
|
||||||
|
|
||||||
|
- **Per-KB ACL 与部门过滤器交互未解决** — U2/KTD5 (P1, adversarial, confidence 75)
|
||||||
|
|
||||||
|
KTD5 定义 per-KB ACL(owner/viewer)与部门级过滤并行运行,但两者交互未明确。当用户属于部门 A 且被授权 KB X(属于部门 B)时,ACL 过滤是否覆盖部门过滤?部门过滤是否作为 ACL 的前置筛选?交集还是并集?需在 U2 实施前明确语义。
|
||||||
|
|
||||||
|
- **R17 与 origin 的缓存决策相矛盾** — R17/KTD7 (P1, product-lens, confidence 75)
|
||||||
|
|
||||||
|
Origin 需求文档中缓存策略是"保留自研语义缓存",但 R17 将其替换为 LiteLLM 内置 Redis Semantic Cache。KTD7 已调和(P1/P2 保留自研,P3 替换),但 origin 文档未更新。需确认 origin 文档是否同步修订,或在本计划中标注 origin 偏离。
|
||||||
|
|
||||||
|
- **kb_acl 存储位置与 KB 数据存储不一致** — U2 (P2, feasibility, confidence 75)
|
||||||
|
|
||||||
|
U2 将 `kb_acl` 表放在 `server/auth/models.py`(现有 auth SQLite?),但 KB 元数据在 PostgreSQL。跨库 JOIN 不可行,ACL 过滤需应用层两次查询。需确认 `server/auth/models.py` 的实际数据库后端,或将 `kb_acl` 移至 `rag_platform/` PG schema。
|
||||||
|
|
||||||
|
- **语义缓存保留期和敏感数据策略缺失** — U17 (P2, security-lens, confidence 75)
|
||||||
|
|
||||||
|
U17 用 LiteLLM Redis Semantic Cache 替换自研缓存,但未定义缓存保留期(TTL)和敏感数据策略。语义缓存可能存储包含 PII 的 LLM 响应,Redis 持久化策略需明确:TTL 上限、敏感 KB 禁用缓存选项、缓存清除 API。
|
||||||
|
|
||||||
|
- **Per-tool MCP auth 方法未充分指定** — U14 (P2, security-lens, confidence 75)
|
||||||
|
|
||||||
|
U14 配置字段包含"鉴权方式"但未列举可选值。MCP 工具的 per-tool auth 是 API Key、JWT、还是 OAuth?不同鉴权方式的客户端体验和密钥管理差异显著。需在 U14 实施前枚举支持的 auth 方法及其适用场景。
|
||||||
|
|
||||||
|
- **Standalone MCP auth middleware 未考虑** — U13 (P2, adversarial, confidence 75)
|
||||||
|
|
||||||
|
U13 将 MCP Server 合并至主 app 并复用 `require_permission`,但现有 `mcp/server.py` 作为独立 app 运行时(如开发模式或独立部署)的 auth middleware 未考虑。合并后是否完全废弃独立运行模式?若保留,独立模式如何复用主 app 的 auth 依赖?
|
||||||
|
|
||||||
|
- **扩展 LocalRAGService vs 替换未考虑** — U1/KTD9 (P2, adversarial, confidence 75)
|
||||||
|
|
||||||
|
KTD9 新建 `rag_platform/` 并废弃 `LocalRAGService` 对 KB 场景的使用。但 `LocalRAGService` 已有 pgvector 集成、分块、嵌入逻辑。是否考虑扩展 `LocalRAGService` 而非新建模块?新建意味着代码重复(解析/嵌入/检索逻辑),扩展意味着职责混合(Agent 记忆 + KB 场景)。需确认新建的收益超过重复成本。
|
||||||
|
|
||||||
|
- **Preview 确认 vs 自动转换未解决** — U9 (P2, design-lens, confidence 75)
|
||||||
|
|
||||||
|
状态机图显示 `segmenting → vectorizing: 预览确认/自动`,但 U9 前端未明确:预览后是必须用户确认才向量化,还是可配置自动转换?默认行为是什么?自动转换时预览是否仍展示?需在 U9 实施前明确交互模式。
|
||||||
|
|
||||||
|
### From 2026-06-24 review (Round 2)
|
||||||
|
|
||||||
|
- **Alembic migration infrastructure not present in codebase** — U2/U15 (P1, feasibility, confidence 75)
|
||||||
|
|
||||||
|
Plan references SQLAlchemy async migrations and "dual-write/dual-read" migration windows for U15, but the codebase has no Alembic setup. U2 creates new PG tables (kb_acl, KB metadata) and U15 migrates ProviderConfig.api_key — both need schema migrations. Implementer must either introduce Alembic or document the manual DDL approach. Affects: U2, U15, and any unit adding/modifying DB schema.
|
||||||
|
|
||||||
|
- **Cross-database breakage: department_kb_bindings in SQLite vs KB metadata in PG** — U2 (P1, feasibility, confidence 75)
|
||||||
|
|
||||||
|
Existing `department_kb_bindings` may live in SQLite (default config) while U2 moves KB metadata to PostgreSQL. Cross-database JOINs are impossible. Need to either migrate `department_kb_bindings` to PG in U2 or document the single-DB (PG-only) deployment requirement. Check `server/auth/models.py` and current DB config before implementing.
|
||||||
|
|
||||||
|
- **pair CLI stores API keys in plaintext YAML** — U13 (P2, security-lens, confidence 75)
|
||||||
|
|
||||||
|
U13 requires MCP per-client API keys to be stored in U10's encrypted secrets store, but the existing `pair` CLI command (`cli/` module) writes API keys to plaintext YAML files. The plan does not address migrating the `pair` CLI output to the encrypted store. Implementer must update `pair` CLI to write to the secrets store or document the transition path.
|
||||||
|
|
||||||
|
- **MemoryRetriever rewiring + RetrievalPrincipal no owning unit** — KTD9/KTD11 (P2, coherence, confidence 75)
|
||||||
|
|
||||||
|
KTD9 states "MemoryRetriever 将在新的单元中重新接线" but no implementation unit claims ownership of this rewiring. KTD11 defines RetrievalPrincipal but no unit explicitly implements it as a standalone component — it's implied across U4/U10/U14. Implementer must decide: create a new unit, or fold RetrievalPrincipal into U1/U4 and MemoryRetriever rewiring into U4. Risk: both get dropped silently.
|
||||||
|
|
||||||
|
- **LlamaIndex jieba SentenceSplitter integration unverified** — U3/KTD6 (P2, feasibility, confidence 50)
|
||||||
|
|
||||||
|
KTD6 chooses application-layer jieba tokenization for PG full-text search, and U3 uses LlamaIndex IngestionPipeline for segmentation. Whether LlamaIndex's SentenceSplitter can be composed with jieba tokenization for Chinese text is unverified. If LlamaIndex's splitter doesn't support custom tokenizers, a custom transformation node or pre-processing step is needed. Implementer should spike this in U1/U3 before committing to the approach.
|
||||||
|
|
||||||
|
- **MCP 301 redirect port 8080 requires standalone listener** — U13 (P3, feasibility, confidence 50)
|
||||||
|
|
||||||
|
U13's backward compatibility plan offers "301 重定向从旧路径" but the existing `mcp/server.py` runs on port 8080 while the main app runs on port 8001. A 301 redirect from port 8080 requires a standalone listener on that port, which defeats the purpose of merging into the main app. Implementer should either: (a) document port 8080 deprecation without redirect, (b) use a reverse proxy, or (c) run a minimal redirect-only listener on 8080 during the transition period.
|
||||||
|
|
||||||
## Sources / Research
|
## Sources / Research
|
||||||
|
|
||||||
- **LlamaIndex 2026:** 14 index types, sparse+dense hybrid retrieval, auto-rerank, pgvector first-class support, LlamaParse for complex PDFs. Pitfall: frequent breaking changes. — https://blog.csdn.net/yanxilou/article/details/162178538
|
- **LlamaIndex 2026:** 14 index types, sparse+dense hybrid retrieval, auto-rerank, pgvector first-class support, LlamaParse for complex PDFs. Pitfall: frequent breaking changes. — https://blog.csdn.net/yanxilou/article/details/162178538
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue