Files
AIRegulation-DocAnalysis/docs/architecture/backend-project-architecture.md

718 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Backend Project Architecture
## 1. Purpose
本文档定义当前 backend 的目标态架构,用于在保持单服务部署的前提下,将系统整理为职责清晰、边界稳定、可替换实现的模块化结构。本文档的重点不是描述理想化分层,而是基于当前真实代码形态,明确后续重构时必须遵守的模块职责、依赖方向、内部稳定接口和替换边界。
本文档与 `docs/rfc/backend-api-parsing-embedding-migration-requirements.md` 的关系如下:
- RFC 负责冻结本轮迁移需求、范围、风险和约束。
- 本文档负责冻结目标模块边界、依赖规则和实现组织方式。
- 后续任何代码重构、能力替换或底座升级,都应同时满足 RFC 与本文档。
## 2. Current-State Problems
基于当前代码,后端已经具备以下能力:
- 文档上传、下载、列表
- 文档解析与切片
- 向量化与 Milvus 入库
- 检索
- 基于 RAG 的 Agent 问答 workflow
但这些能力当前主要是“可运行”,还不是“结构清晰、便于替换、便于演进”的状态。核心问题如下。
### 2.1 `DocumentProcessor` 责任过载
`backend/app/services/document_processor.py` 当前同时承担:
- 文档解析
- 摘要生成
- 分块
- 向量化
- Milvus 入库
- 检索入口
这使上传处理链路、检索链路与基础设施初始化逻辑耦合在一个大类中。流程编排与具体实现没有边界,后续无论替换 parser、embedding、vector store 还是增加文档状态管理,都会直接影响同一个类。
### 2.2 检索逻辑缺少稳定边界
`backend/app/services/rag/retriever.py` 当前同时管理:
- embedder 初始化
- Milvus 连接与 collection lifecycle
- 检索执行
- 结果映射
这意味着“检索能力”不是一个稳定的业务能力接口,而是一个直接依赖具体 embedding 和 Milvus 实现的复合服务。后续如果从 `BGE-M3 + hybrid search` 切到 `1536 dense-only` 或替换向量索引实现,会直接影响检索服务本身。
### 2.3 `QAAgent` 责任过载
`backend/app/services/agent/qa_agent.py` 当前同时承担:
- 检索调用
- 上下文构建
- Prompt 选择
- LLM 调用
- SSE 流式问答流程
- 会话 workflow 编排
这导致 Agent workflow 与检索底座、LLM provider、上下文构造逻辑紧耦合。后续切换 LLM provider、替换 session store、复用 retrieval 能力时,影响面会扩散到整个 Agent 实现。
### 2.4 API 层直接编排具体服务
当前 API 路由主要在:
- `backend/app/api/routes/documents.py`
- `backend/app/api/routes/knowledge.py`
- `backend/app/api/routes/agent.py`
这些路由直接实例化具体服务类,例如 `DocumentProcessor``QAAgent``MinIOClient`。这意味着:
- API 层不仅处理 transport concerns也在做业务编排
- 路由层知道过多内部实现细节
- 后续如果内部模块调整,路由层也要跟着改
### 2.5 文档元数据与对象存储组织方式耦合
当前文档列表与下载逻辑高度依赖 MinIO 对象命名方式和对象遍历结果。对象存储目前承担了部分“业务真相”的角色,但对象存储只适合作为文件二进制载体,不适合作为完整文档元数据和状态管理的唯一来源。
### 2.6 `knowledge` 与 `agent` 共享检索底座的边界不清晰
当前 `/knowledge/*``/agent/*` 都依赖检索能力,但共享方式不够清晰:
- `knowledge` 通过 `DocumentProcessor.search()` 访问检索
- `agent` 通过 `Retriever` 访问检索
这会导致同一检索能力未来演进成两条链路,难以统一检索策略、元数据模型和可替换边界。
## 3. Architecture Goals
本项目后端的目标态架构必须满足以下目标。
### 3.1 单服务部署
系统继续保持单服务部署,不拆分为多个微服务。架构治理发生在单服务内部,通过清晰模块边界实现高内聚低耦合,而不是通过进程级拆分回避设计问题。
### 3.2 高内聚、低耦合优先级最高
后续模块设计以“一个模块只承载一类稳定职责”为原则。跨能力流程统一在编排层组织,不允许继续把 parser、embedding、storage、retrieval、LLM workflow 堆进同一个服务类。
### 3.3 外部 API 尽量保持兼容
现有前端与外部调用方依赖的主接口保持不变优先,包括但不限于:
- `/api/v1/documents/*`
- `/api/v1/knowledge/*`
- `/api/v1/agent/*`
内部可以重组,但外部接口不应因为内部重构而被迫大改。
### 3.4 关键能力必须可替换
以下能力必须通过稳定端口隔离实现细节:
- 文档解析
- 分块构建
- 向量化
- 向量索引
- 检索
- LLM 回答生成
- 会话存储
- 原始文件存储
后续替换方案时,只允许替换实现,不允许穿透影响其他模块。
### 3.5 `knowledge` 与 `agent` 共用同一检索底座
检索必须被视为独立的业务能力,由统一的 retrieval application service 对外暴露。`knowledge``agent` 必须复用同一个 retrieval 底座,避免两套召回策略、两套元数据模型、两套 adapter。
### 3.6 依赖必须单向流动
系统必须形成稳定的单向依赖关系:
- `api -> application -> domain`
- `application -> infrastructure` 通过端口/实现绑定
- `infrastructure -> external systems`
不允许出现基础设施实现反向驱动业务编排,也不允许 domain 依赖 Web 或第三方 SDK。
## 4. Target Module Layout
目标目录结构如下:
```text
backend/app/
api/
application/
documents/
knowledge/
agent/
domain/
documents/
retrieval/
conversation/
infrastructure/
storage/
vectorstore/
parser/
embedding/
llm/
session/
shared/
```
该结构是本项目 backend 的目标态模块布局。后续实现可以渐进迁移,但职责边界不能偏离。
### 4.1 `api`
职责:
- HTTP 路由注册
- 请求参数校验
- 响应模型映射
- 异常转换
- SSE 事件格式输出
非职责:
- 不直接组织完整业务流程
- 不直接访问 MinIO、Milvus、Parser SDK、LLM SDK
- 不直接 new 具体基础设施客户端
### 4.2 `application`
职责:
- 用例编排
- 跨领域能力协作
- 业务流程统一入口
- workflow 级别的状态推进
非职责:
- 不直接依赖第三方 SDK
- 不承担具体存储、向量库、解析器实现细节
### 4.3 `domain`
职责:
- 核心业务对象
- 领域术语
- 稳定端口接口
- 统一元数据模型
- 检索结果模型
- 会话消息模型
非职责:
- 不依赖 FastAPI
- 不依赖 MinIO、Milvus、LLM SDK
- 不依赖路由请求响应模型
### 4.4 `infrastructure`
职责:
- 外部系统适配器实现
- 第三方 SDK 封装
- provider-specific 配置适配
- 数据格式转换
包含但不限于:
- MinIO binary store
- Milvus vector index
- Aliyun / local parser adapter
- OpenAI-compatible embedding adapter
- DeepSeek / Qwen LLM adapter
- in-memory / Redis session store
### 4.5 `shared`
职责:
- 配置
- 日志
- 通用异常
- 通用工具
- 公共基础设施无关组件
非职责:
- 不承载业务编排
- 不变成新的 `services` 大杂烩目录
## 5. Module Responsibilities
### 5.1 `api`
`api` 是 transport 层,只关心请求进来和响应出去的表达方式。它应该把请求转换为 application service 的输入,把 application service 的结果转换为 HTTP 响应。
`api` 不应该知道:
- MinIO bucket 怎么组织
- Milvus collection 怎么建
- parser 是本地还是阿里云
- embedding 是本地模型还是 API
- session 是内存还是 Redis
### 5.2 `application`
`application` 是业务编排层,是系统内唯一允许跨模块组织完整流程的层。它应该定义稳定的用例服务,而不是把流程散落在路由或基础设施实现中。
本项目至少固定以下 4 类 application service
- `DocumentCommandService`
- `DocumentQueryService`
- `KnowledgeRetrievalService`
- `AgentConversationService`
### 5.3 `domain`
`domain` 层定义系统内部真正稳定的概念,例如:
- `Document`
- `DocumentStatus`
- `ParsedDocument`
- `Chunk`
- `RetrievalQuery`
- `RetrievedChunk`
- `ConversationSession`
- `ConversationMessage`
- `AnswerSource`
这些对象必须脱离具体技术实现,成为 parser、embedding、vector index、agent workflow 之间的公共契约。
### 5.4 `infrastructure`
`infrastructure` 只负责“怎么接某个外部系统”,不负责“业务上应该先做什么后做什么”。例如:
- MinIO adapter 负责上传和下载文件
- Milvus adapter 负责 upsert/search/delete
- Qwen / DeepSeek adapter 负责生成回答
- Aliyun parser adapter 负责把解析结果映射成统一 `ParsedDocument`
### 5.5 `shared`
`shared` 只放横切能力。任何和文档 ingest、检索、问答编排直接相关的业务逻辑都不应该放进 `shared`
## 6. Stable Internal Ports
以下端口是系统内部稳定契约。后续方案替换时,只能替换实现,不允许改动上层 application service 的调用方式,也不允许影响 sibling 模块。
### 6.1 `DocumentRepository`
职责:
- 管理文档元数据
- 管理文档状态
- 管理统计字段,例如 chunk 数、索引状态、摘要状态
说明:
- 列表和状态查询应以 `DocumentRepository` 为主,而不是直接遍历对象存储。
### 6.2 `DocumentBinaryStore`
职责:
- 保存原始文件
- 下载原始文件
- 删除原始文件
- 处理对象存储相关细节
说明:
- 替换 MinIO 或对象存储方案时,只替换该实现。
### 6.3 `DocumentParser`
职责:
- 输入原始文件
- 输出统一结构化解析结果
说明:
- 本地 PDF/MinerU 或阿里云解析只能作为实现差异,不能外溢到业务流程层。
### 6.4 `ChunkBuilder`
职责:
- 输入统一解析结果
- 输出统一 chunk 模型
说明:
- chunk 规则变化只能影响该端口实现,不应影响 retrieval、agent 或 API。
### 6.5 `EmbeddingProvider`
职责:
- 输入文本列表
- 输出 embedding 向量结果
说明:
- 从本地模型切到 OpenAI-compatible embedding只替换该实现。
### 6.6 `VectorIndex`
职责:
- upsert chunks
- delete by document
- search by query vector
- 管理索引内部 schema
说明:
- Milvus schema 或向量库替换,只能影响该层。
### 6.7 `Retriever`
职责:
- 基于 query、filter、top_k 返回统一检索结果
说明:
- `Retriever` 是业务侧的检索端口,不应再直接持有 embedder、Milvus lifecycle 和 provider-specific 逻辑。
### 6.8 `AnswerGenerator`
职责:
- 基于 query 与 context 生成最终回答
- 屏蔽具体 LLM provider 差异
说明:
- DeepSeek、Qwen 或其他模型切换时,只替换该实现。
### 6.9 `ConversationStore`
职责:
- 创建和读取 session
- 持久化消息历史
- 管理会话生命周期
说明:
- 从内存实现切到 Redis 或数据库实现时,只替换该实现。
## 7. Application Services
### 7.1 `DocumentCommandService`
职责:
- 接收文档上传命令
- 生成 `doc_id`
- 保存原始文件
- 触发解析、分块、向量化、入库
- 更新文档状态和统计信息
- 返回最终处理结果
说明:
- 当前 `DocumentProcessor` 的“流程编排”职责在目标态应迁移到这里。
- parser、chunker、embedder、vector index 的具体实现不应继续塞进一个大类里统一管理。
### 7.2 `DocumentQueryService`
职责:
- 文档列表
- 文档下载
- 文档状态查询
- 文档管理视图查询
说明:
- 列表和状态查询应基于 `DocumentRepository`
- 下载应通过 `DocumentBinaryStore`
- 不再依赖 MinIO 对象结构作为业务视图主来源
### 7.3 `KnowledgeRetrievalService`
职责:
- 对外提供统一检索能力
- 管理 retrieval query 到 retrieval result 的业务转换
-`/knowledge/*` 和 Agent workflow 共用
说明:
- 当前 `knowledge``agent` 必须统一依赖这一层,不允许各自再维护一套检索流程。
### 7.4 `AgentConversationService`
职责:
- 统一管理问答 workflow
- 读取或创建会话
- 调用 `KnowledgeRetrievalService`
- 构建问答上下文
- 调用 `AnswerGenerator`
- 保存回答和引用来源
说明:
- 当前 `QAAgent` 的 workflow 编排职责在目标态应迁移到这里,或被其吸收后只保留 façade 角色。
- SSE 与普通问答必须共用这一层,不允许复制业务编排逻辑。
## 8. Core Workflows
### 8.1 文档上传入库链路
目标流程如下:
1. `api/documents` 接收上传请求并完成输入校验。
2. `DocumentCommandService` 生成 `doc_id`,初始化文档记录和状态。
3. `DocumentBinaryStore` 保存原始文件。
4. `DocumentParser` 对原始文件执行解析,输出统一结构化结果。
5. `ChunkBuilder` 将解析结果转换为统一 chunk 集合。
6. `EmbeddingProvider` 为 chunks 生成向量。
7. `VectorIndex` 将 chunks 与 vectors 写入索引。
8. `DocumentRepository` 更新文档状态、chunk 数量、索引状态、元数据。
9. API 返回处理结果。
约束:
- 上传处理链路的主编排必须只存在于 `DocumentCommandService`
- 不允许再由 route 或基础设施类直接组织全流程
### 8.2 文档查询链路
目标流程如下:
1. `api/documents` 调用 `DocumentQueryService`
2. 文档列表与状态查询通过 `DocumentRepository`
3. 文档下载通过 `DocumentBinaryStore`
4. 对象存储命名规则只作为实现细节,不作为最终业务真相
约束:
- 文档“存在、状态、统计信息”必须有稳定元数据模型
- 不允许继续通过对象存储遍历结果拼出全部业务语义
### 8.3 Agent 问答链路
目标流程如下:
1. `api/agent` 接收问答请求
2. `AgentConversationService` 读取或创建 session
3. `KnowledgeRetrievalService` 统一执行检索
4. `AnswerGenerator` 基于 query 和 retrieval context 生成回答
5. `ConversationStore` 保存消息历史和引用来源
6. API 将结果以普通 JSON 或 SSE 格式输出
约束:
- 普通问答和 SSE 问答只允许输出形式不同
- 业务编排链必须完全复用
- 检索能力必须来自同一 `KnowledgeRetrievalService`
## 9. Dependency Rules
系统内部依赖方向固定如下:
```text
api -> application -> domain
application -> infrastructure (through ports)
infrastructure -> external systems
```
具体规则如下:
- `api` 可以依赖 `application` 和 API 自己的 request/response models
- `application` 可以依赖 `domain` 和端口绑定后的 infrastructure 实现
- `domain` 不能依赖 `api``infrastructure`
- `infrastructure` 可以依赖 `domain` 定义的端口和数据模型,但不能反向驱动 application 逻辑
## 10. Migration Mapping From Current Code
当前关键代码到目标模块的映射如下。
### 10.1 文档处理
当前:
- `backend/app/services/document_processor.py`
目标:
- 其流程编排职责迁移到 `application/documents/DocumentCommandService`
- 解析、分块、向量、入库分别通过端口接入
- 检索入口从该类中剥离,不再由 ingest orchestration 承担 search 职责
### 10.2 检索
当前:
- `backend/app/services/rag/retriever.py`
目标:
- `domain/retrieval` 中定义 `Retriever` 端口和统一检索结果模型
- `infrastructure/vectorstore` 中承载具体检索实现
- `application/knowledge/KnowledgeRetrievalService` 作为统一检索用例入口
### 10.3 Agent Workflow
当前:
- `backend/app/services/agent/qa_agent.py`
目标:
- workflow 编排职责迁移到 `application/agent/AgentConversationService`
- 具体 LLM 调用走 `AnswerGenerator`
- 具体 session 读写走 `ConversationStore`
- 检索统一走 `KnowledgeRetrievalService`
### 10.4 存储
当前:
- `backend/app/services/storage/minio_client.py`
- `backend/app/services/storage/milvus_client.py`
目标:
- MinIO 迁移到 `infrastructure/storage`
- Milvus 迁移到 `infrastructure/vectorstore`
### 10.5 解析
当前:
- `backend/app/services/parser/*`
- `backend/app/services/parser/mineru_parser.py`
目标:
- 全部迁移到 `infrastructure/parser`
- 对外只暴露统一 `DocumentParser` 端口实现
### 10.6 向量化
当前:
- `backend/app/services/embedding/*`
目标:
- 迁移到 `infrastructure/embedding`
- 对外只暴露统一 `EmbeddingProvider`
### 10.7 LLM
当前:
- `backend/app/services/llm/*`
目标:
- 迁移到 `infrastructure/llm`
-`AnswerGenerator` 屏蔽 provider 差异
### 10.8 会话
当前:
- `backend/app/services/agent/session_manager.py`
目标:
- 迁移到 `infrastructure/session`
- 对外通过 `ConversationStore` 暴露
### 10.9 API 模型与内部模型
当前:
- `backend/app/api/models/*`
- `backend/app/schemas/*`
目标:
- 对外 request/response model 保留在 `api`
- 内部 DTO / VO / domain object 收敛到 `application``domain`
- 不允许 API model 直接渗透到 domain
## 11. Technology Replacement Boundaries
### 11.1 本地解析 / MinerU -> 阿里云文档解析
替换原则:
- 只替换 `DocumentParser` adapter
- `DocumentCommandService` 不应感知解析提供商差异
- `ChunkBuilder` 只接收统一解析结果模型
### 11.2 BGE-M3 -> OpenAI-compatible embedding
替换原则:
- 只替换 `EmbeddingProvider`
- `KnowledgeRetrievalService``DocumentCommandService` 不应感知 embedding 来源变化
### 11.3 Milvus `1024 + sparse` -> `1536 dense-only`
替换原则:
- 只替换 `VectorIndex` 实现
- collection schema、index 参数、dense-only search 属于 index 内部实现细节
- 上层 retrieval 和 agent workflow 不应因为 schema 变化而改业务接口
### 11.4 DeepSeek / Qwen 切换
替换原则:
- 只替换 `AnswerGenerator` 背后的 provider adapter
- 上层 conversation workflow 不应直接依赖具体模型 SDK
### 11.5 内存 session -> Redis / DB session
替换原则:
- 只替换 `ConversationStore`
- API 和 application service 不应感知 session 持久化细节
## 12. Guardrails
后续所有 backend 重构和新增功能必须遵守以下规则:
- 禁止 `api/routes` 直接实例化 parser、embedder、Milvus、MinIO、LLM client
- 禁止 `application` 层直接 import 第三方 SDK
- 禁止 `domain` 层依赖 FastAPI、Pydantic route model、MinIO SDK、Milvus SDK、LLM SDK
- 禁止 SSE 和普通问答各自维护独立 workflow
- 禁止把对象存储命名规则作为唯一业务元数据来源
- 禁止新建第二个“大一统流程类”替代 `DocumentProcessor`
- 禁止 `knowledge``agent` 各自维护独立检索实现
- 禁止 parser、embedding、vector index、llm provider 的替换穿透到 API 层
## 13. Architecture Review Checklist
后续评审和重构验收时,至少核对以下问题:
1. 上传、下载、列表、解析、切片、向量、入库、检索、Agent Workflow 是否都映射到了明确模块。
2. 系统是否仍保持单服务,而不是被动演化成伪微服务结构。
3. 是否存在唯一、清晰的目标目录结构。
4. 是否定义了稳定端口列表。
5. 是否定义了文档上传入库、文档查询、Agent 问答三条核心 workflow。
6. 是否定义了单向依赖方向。
7. 是否明确列出了架构禁令。
8. 是否定义了当前关键代码到目标模块的映射。
9. 是否明确定义了 parser、embedding、vector index、LLM、session store 的替换边界。
10. 是否明确 `knowledge``agent` 共用同一 retrieval 底座。
11. 是否明确 API 层只负责 transport concerns不再直接承担业务编排。
12. 是否保证后续替换方案时,上层 application service 与外部 API 契约不被迫变化。