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

19 KiB
Raw Blame History

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

这些路由直接实例化具体服务类,例如 DocumentProcessorQAAgentMinIOClient。这意味着:

  • API 层不仅处理 transport concerns也在做业务编排
  • 路由层知道过多内部实现细节
  • 后续如果内部模块调整,路由层也要跟着改

2.5 文档元数据与对象存储组织方式耦合

当前文档列表与下载逻辑高度依赖 MinIO 对象命名方式和对象遍历结果。对象存储目前承担了部分“业务真相”的角色,但对象存储只适合作为文件二进制载体,不适合作为完整文档元数据和状态管理的唯一来源。

2.6 knowledgeagent 共享检索底座的边界不清晰

当前 /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 knowledgeagent 共用同一检索底座

检索必须被视为独立的业务能力,由统一的 retrieval application service 对外暴露。knowledgeagent 必须复用同一个 retrieval 底座,避免两套召回策略、两套元数据模型、两套 adapter。

3.6 依赖必须单向流动

系统必须形成稳定的单向依赖关系:

  • api -> application -> domain
  • application -> infrastructure 通过端口/实现绑定
  • infrastructure -> external systems

不允许出现基础设施实现反向驱动业务编排,也不允许 domain 依赖 Web 或第三方 SDK。

4. Target Module Layout

目标目录结构如下:

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 共用

说明:

  • 当前 knowledgeagent 必须统一依赖这一层,不允许各自再维护一套检索流程。

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

系统内部依赖方向固定如下:

api -> application -> domain
application -> infrastructure (through ports)
infrastructure -> external systems

具体规则如下:

  • api 可以依赖 application 和 API 自己的 request/response models
  • application 可以依赖 domain 和端口绑定后的 infrastructure 实现
  • domain 不能依赖 apiinfrastructure
  • 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 收敛到 applicationdomain
  • 不允许 API model 直接渗透到 domain

11. Technology Replacement Boundaries

11.1 本地解析 / MinerU -> 阿里云文档解析

替换原则:

  • 只替换 DocumentParser adapter
  • DocumentCommandService 不应感知解析提供商差异
  • ChunkBuilder 只接收统一解析结果模型

11.2 BGE-M3 -> OpenAI-compatible embedding

替换原则:

  • 只替换 EmbeddingProvider
  • KnowledgeRetrievalServiceDocumentCommandService 不应感知 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
  • 禁止 knowledgeagent 各自维护独立检索实现
  • 禁止 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. 是否明确 knowledgeagent 共用同一 retrieval 底座。
  11. 是否明确 API 层只负责 transport concerns不再直接承担业务编排。
  12. 是否保证后续替换方案时,上层 application service 与外部 API 契约不被迫变化。