后端服务架构.md 25 KB

后端服务架构

本文引用的文件

  • App.java
  • application.yaml
  • application-dev.yaml
  • application-pro.yaml
  • pom.xml

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与扩展性
  8. 故障排查指南
  9. 结论

简介

本文档描述 ai-server 后端服务的整体架构。它基于 Spring Boot,面向“调查研判”场景,把平台管理、Agent 智能体、数据清洗、知识检索、图谱、通话、人员、传输、轨迹、治理、搜索、数据迁移、外部系统、嵌入向量等能力按模块组织;通过 MyBatis-Plus 与 Dynamic Datasource 同时连接 PostgreSQL/pgvector、Redis、DuckDB 等业务与临时数据源,并使用 Sa-Token 完成认证、SSE 实现长连接心跳保活,配合定时任务与工作空间清理保障多用户并发下的稳定性。

项目结构

ai-server 采用 Spring Boot 模块化布局:

  • com.zsjz.ai.App 是应用入口,开启定时调度并负责工作空间初始化。
  • com.zsjz.ai.module.* 按业务域划分模块:plat、agent、aiclean、knowledge、graph、person、call、trans、track、govern、search、dm、otg、external、embedding。
  • src/main/resources 提供统一配置:通用配置在 application.yaml,开发环境在 application-dev.yaml,生产环境在 application-pro.yaml。
  • pom.xml 声明 Spring Boot、Sa-Token、MyBatis-Plus、Dynamic Datasource、AgentScope、pgvector、DuckDB、ONNX Runtime 等依赖。

    graph TB
    App["应用入口<br/>App.java"] --> ModulePlat["平台模块<br/>module/plat"]
    App --> ModuleAgent["智能体模块<br/>module/agent"]
    App --> ModuleAiclean["AI 清洗模块<br/>module/aiclean"]
    App --> ModuleKnowledge["知识库模块<br/>module/knowledge"]
    App --> ModuleGraph["图谱模块<br/>module/graph"]
    App --> ModulePerson["人员模块<br/>module/person"]
    App --> ModuleCall["通话模块<br/>module/call"]
    App --> ModuleTrans["传输模块<br/>module/trans"]
    App --> ModuleTrack["轨迹模块<br/>module/track"]
    App --> ModuleGovern["治理模块<br/>module/govern"]
    App --> ModuleSearch["搜索模块<br/>module/search"]
    App --> ModuleDm["数据迁移模块<br/>module/dm"]
    App --> ModuleOtg["OTG 模块<br/>module/otg"]
    App --> ModuleExternal["外部系统模块<br/>module/external"]
    App --> ModuleEmbedding["嵌入向量模块<br/>module/embedding"]
    

图示来源

  • App.java:10-24

章节来源

  • App.java:10-49
  • application.yaml:1-193
  • application-dev.yaml:1-87
  • application-pro.yaml:1-87
  • pom.xml:1-405

核心组件

  • Spring Boot 应用入口:App 类使用 @EnableScheduling 和 @SpringBootApplication,负责启动容器、注册定时任务,并在启动时检查或重建运行工作空间。
  • 模块化组织原则:每个业务领域一个 module/* 包,通常包含 controller、service、mapper/entity、vo/dto、工具类等;模块之间通过共享的 common.model 实体、数据库和数据访问层协作,而不是直接互相调用 UI 或 HTTP 层。
  • 多数据源体系:
    • 主数据源 PostgreSQL,安装 pgvector 后支持向量检索。
    • Redis 用于 Sa-Token 会话、缓存与 AgentScope 内存扩展。
    • DuckDB 作为案件级临时分析库,由动态路由控制打开数量、线程数和内存预算。
  • 认证与安全:Sa-Token 以 x-token 请求头传递令牌,默认单端登录,可切换为多端并发;登录态存储依赖 Redis。
  • 长连接与会话保活:SSE 心跳由定时任务驱动,定期向活跃连接发送心跳,防止代理超时断开。
  • AgentScope RAG 与向量检索:通过 AgentScope Harness、DashScope/Ollama/OpenAI 模型扩展、Redis 扩展与 Simple RAG,结合本地 ONNX bge-m3 或在线 embedding 模型,为表结构语义检索、附件分块召回、模板匹配候选召回提供服务。
  • Python 执行器与附件处理:Agent 通过工具调用 Python 执行器读取大表格,附件上传经 Tika 解析,小内容内联消息,大文档走分块 RAG。
  • 工作空间与定时清理:启动时恢复运行工作空间;定时任务回收过期会话对应的 DuckDB 数据源、清理孤儿附件、维护 AI 清洗批次状态。

章节来源

  • App.java:10-49
  • application.yaml:1-193
  • pom.xml:1-405

架构总览

ai-server 的整体职责可以理解为:对外暴露 REST/SSE 接口,对内协调平台元数据、Agent 推理、RAG 检索、向量索引、临时数据分析、外部数据接入以及图谱与治理结果输出。

graph TB
    Client["前端或桌面客户端"] --> Api["REST/SSE 接口层"]
    Api --> Plat["平台与认证<br/>plat"]
    Api --> Agent["Agent 编排与工具<br/>agent"]
    Api --> Clean["AI 清洗流程<br/>aiclean"]
    Api --> Knowledge["知识文档解析与索引<br/>knowledge"]
    Api --> Embedding["嵌入向量服务<br/>embedding"]
    Api --> Graph["图谱构建与查询<br/>graph"]
    Api --> Person["人员库<br/>person"]
    Api --> Call["通话记录与分析<br/>call"]
    Api --> Trans["数据传输<br/>trans"]
    Api --> Track["轨迹数据<br/>track"]
    Api --> Govern["治理规则与结果<br/>govern"]
    Api --> Search["搜索与检索<br/>search"]
    Api --> Dm["数据迁移<br/>dm"]
    Api --> Otg["OTG 时序与特殊日期<br/>otg"]
    Api --> External["外部系统对接<br/>external"]

    Agent --> Knowledge
    Agent --> Embedding
    Agent --> Clean
    Agent --> Graph
    Agent --> Person
    Agent --> Call
    Agent --> Trans
    Agent --> Track
    Agent --> Govern
    Agent --> Search
    Agent --> External

该图展示了模块间的高层协作:Agent 是调查场景中的“中枢”,调用各业务模块能力,并结合 RAG、向量检索与 Python 执行器完成复杂分析。

详细组件分析

应用入口与启动流程

App 类承担两个职责:

  1. 启用 Spring Boot 与定时调度。
  2. 在启动时检查运行工作空间是否存在;若不存在或锁文件缺失,则从原始路径复制工作空间并创建锁文件,保证多进程或重启后的工作区一致性。

    flowchart TD
    Start["JVM 启动"] --> RunApp["SpringApplication.run(App.class)"]
    RunApp --> EnableScheduling["启用 @EnableScheduling"]
    EnableScheduling --> CheckWorkspace["检查工作空间"]
    CheckWorkspace --> Exists{"工作空间存在?"}
    Exists -->|否| CopyFromOriginal["从原始路径复制工作空间"]
    Exists -->|是| HasLock{"存在锁文件?"}
    HasLock -->|否| ForceOverride["强制覆盖模式:删除旧运行目录并重建"]
    HasLock -->|是| Ready["就绪"]
    CopyFromOriginal --> Ready
    ForceOverride --> Ready
    

图示来源

  • App.java:10-49

章节来源

  • App.java:10-49

多数据源配置:PostgreSQL、Redis、DuckDB

配置文件将三类数据源解耦:

  • PostgreSQL + pgvector:作为主数据源,承载平台元数据、Agent 会话、知识索引、图谱节点关系、清洗模板、通话与人员等持久化数据;pgvector 扩展支撑余弦相似度向量检索。
  • Redis:承载 Sa-Token 会话、AgentScope 记忆扩展与部分缓存。
  • DuckDB:按案件实例打开,用于临时分析、ETL 中间结果、AI 清洗中间表等。

    graph TB
    PGB["PostgreSQL<br/>pgvector"]
    Redis["Redis"]
    DuckDB["DuckDB<br/>案件临时库"]
    
    Plat["plat 模块"] --> PGB
    Plat --> Redis
    Agent["agent 模块"] --> Redis
    Aiclean["aiclean 模块"] --> PGB
    Aiclean --> DuckDB
    Knowledge["knowledge 模块"] --> PGB
    Embedding["embedding 模块"] --> PGB
    Graph["graph 模块"] --> PGB
    Person["person 模块"] --> PGB
    Call["call 模块"] --> PGB
    Trans["trans 模块"] --> PGB
    Track["track 模块"] --> PGB
    Govern["govern 模块"] --> PGB
    Search["search 模块"] --> PGB
    Dm["dm 模块"] --> PGB
    Otg["otg 模块"] --> PGB
    External["external 模块"] --> PGB
    

图示来源

  • application.yaml:1-193
  • application-dev.yaml:1-87
  • application-pro.yaml:1-87

关键配置要点:

  • Dynamic Datasource 的主库 key 固定为 master,strict 模式避免误写 SQL 落到主库。
  • Redis host/port 通过环境变量覆盖。
  • DuckDB 通过 zsjz.duckdb.* 配置控制最大同时打开数、内存比例、每实例线程数与兜底回收间隔。
  • MyBatis-Plus 别名包指向 com.zsjz.ai.common.model.*.entity,所有模块实体集中放在 common 下,避免跨模块命名冲突。

章节来源

  • application.yaml:1-193
  • application-dev.yaml:1-87
  • application-pro.yaml:1-87

Sa-Token 认证流程

Sa-Token 在本项目中仅做认证,不做细粒度权限控制;唯一管理员标识来自 sys_user.is_admin。令牌名固定为 x-token,有效期 30 天,关闭活跃超时,避免长时间研判中被登出;默认单端登录,后登录顶掉前登录。

sequenceDiagram
    participant Frontend as "前端"
    participant Server as "ai-server"
    participant Stp as "Sa-Token"
    participant Redis as "Redis"

    Frontend->>Server: "携带 x-token 发起请求"
    Server->>Stp: "校验令牌有效性"
    Stp->>Redis: "读取会话信息"
    Redis-->>Stp: "返回会话"
    Stp-->>Server: "认证成功或失败"
    Server-->>Frontend: "业务响应或 401"

    Note over Frontend,Redis: "Redis 不可用时,所有 StpUtil 调用会抛异常"

图示来源

  • application-dev.yaml:49-87
  • application-pro.yaml:49-87

安全约束与建议:

  • 前端必须使用 x-token 请求头,否则无法携带登录态。
  • 生产环境建议保持单端登录,避免账号被多设备长期占用。
  • Redis 是硬依赖;若部署中 Redis 不可用,应优先修复基础设施,而不是降级认证。

章节来源

  • application-dev.yaml:49-87
  • application-pro.yaml:49-87

SSE 心跳保活机制

SSE 心跳由 Spring 调度任务驱动,默认每 5 秒遍历活跃连接并发送心跳,防止反向代理或浏览器因空闲超时断开连接。为避免批量任务阻塞心跳,调度线程池默认扩容到 4 个线程,使心跳、数据源回收、AI 清洗看门狗、附件孤儿清理相互隔离。

flowchart TD
    Scheduler["Spring 调度器<br/>size=4"] --> Heartbeat["SSE 心跳任务"]
    Scheduler --> Reaper["DuckDB 会话回收"]
    Scheduler --> Cleaner["AI 清洗看门狗"]
    Scheduler --> OrphanCleaner["附件孤儿清理"]

    Heartbeat --> ActiveConnections["遍历活跃 SSE 连接"]
    ActiveConnections --> SendPing["发送心跳事件"]
    SendPing --> KeepAlive["保持连接存活"]

图示来源

  • application.yaml:14-29
  • App.java:10-24

注意事项:

  • 如果某个定时任务出现慢查询或阻塞,仍可能影响同调度器上的其他任务,因此监控调度任务耗时很重要。
  • 生产环境中应根据并发连接数调整调度线程池大小。

章节来源

  • application.yaml:14-29
  • App.java:10-24

定时任务与工作空间清理

启动时的工作空间逻辑会判断运行目录与锁文件:

  • 若运行工作空间不存在,则从原始路径复制并创建锁文件。
  • 若存在但无锁文件,则以强制覆盖模式删除旧运行目录并重新复制。

定时任务方面,除了 SSE 心跳,还包括:

  • 回收已过期会话但仍打开的 DuckDB 数据源。
  • 清理 24 小时未发消息的附件孤儿。
  • 维护 AI 清洗批次的 token 预算、超时与失败恢复。

    flowchart TD
    Startup["应用启动"] --> WorkspaceCheck["检查工作空间"]
    WorkspaceCheck --> InitDone["工作空间就绪"]
    InitDone --> ScheduleStart["启动定时任务"]
    ScheduleStart --> HeartbeatTask["SSE 心跳"]
    ScheduleStart --> DataSourceReaper["DuckDB 泄漏回收"]
    ScheduleStart --> AiCleanWatchdog["AI 清洗看门狗"]
    ScheduleStart --> AttachmentOrphanCleaner["附件孤儿清理"]
    

图示来源

  • App.java:10-49
  • application.yaml:14-29

章节来源

  • App.java:10-49
  • application.yaml:14-29

Agent 模块与其他模块的协作

Agent 模块是调查场景中的智能编排层,主要职责包括:

  • 管理 Agent 会话与消息流。
  • 注册并调用业务工具,如表结构语义检索、Python 执行器、附件 RAG、外部系统查询等。
  • 与 knowledge、embedding、aiclean、graph、person、call、trans、track、govern、search、external 等模块协作,把多源数据整合成调查结论。

    graph LR
    Agent["agent 模块"] --> ToolRegistry["工具注册中心"]
    ToolRegistry --> TableSchema["表结构语义检索"]
    ToolRegistry --> PythonExec["Python 执行器"]
    ToolRegistry --> AttachmentRAG["附件 RAG"]
    ToolRegistry --> ExternalApi["外部系统接口"]
    
    TableSchema --> Knowledge["knowledge 模块"]
    AttachmentRAG --> Embedding["embedding 模块"]
    Agent --> Aiclean["aiclean 模块"]
    Agent --> Graph["graph 模块"]
    Agent --> Person["person 模块"]
    Agent --> Call["call 模块"]
    Agent --> Trans["trans 模块"]
    Agent --> Track["track 模块"]
    Agent --> Govern["govern 模块"]
    Agent --> Search["search 模块"]
    Agent --> External["external 模块"]
    

图示来源

  • pom.xml:1-405
  • application.yaml:1-193

Agent 协作的关键点:

  • 向量检索与 RAG 可同时服务于表结构检索和附件问答。
  • 大表格不走内联消息,而是通过 Python 执行器全量读取计算。
  • 向量模型可在本地 ONNX 与在线 embedding 之间切换,缺省行为见 zsjz.embedding.enabled。

章节来源

  • pom.xml:1-405
  • application.yaml:1-193

向量检索与嵌入模型

本项目支持两套向量模型路径:

  • 本地 bge-m3 ONNX 模型:输出 1024 维稠密向量,首次加载需加载 tokenizer.json 与 model.onnx,常驻堆外内存约 2.5~3GB,懒加载不阻塞启动。
  • 在线 embedding 模型:当本地开关关闭且系统中存在 type=embedding 的活跃模型时使用。

    flowchart TD
    Config["zsjz.embedding.enabled"] --> Local{"是否启用本地模型?"}
    Local -->|是| LoadOnnx["加载 ONNX 模型<br/>bge-m3"]
    Local -->|否| UseOnline["使用在线 embedding 模型"]
    LoadOnnx --> VectorIndex["向量索引"]
    UseOnline --> VectorIndex
    VectorIndex --> PgVector["pgvector 表"]
    VectorIndex --> Redis["AgentScope Redis 扩展"]
    

图示来源

  • application.yaml:72-120
  • pom.xml:1-405

风险与建议:

  • 本地模型首次加载较慢,生产环境可通过预热参数提前加载。
  • 向量索引重建需限制文档上限,防止模板库膨胀打爆内存或 token 预算。
  • 若 pgvector 未安装或向量模型不可用,相关能力应 fail-open,不影响清洗主链路。

章节来源

  • application.yaml:72-120
  • pom.xml:1-405

附件与 Workspace Catalog

附件处理分为三条路径:

  1. 小内容内联进入消息。
  2. 大表格只传路径,由 Agent 调用 Python 执行器读取。
  3. 大文档分块向量化,问题到来时按 top-k 召回片段注入上下文。

附件按 userId、caseId、sessionId 三层隔离,并具备 24 小时孤儿清理机制。Workspace Catalog 则与 Agent 工作空间、DuckDB 案件库、文件存储根目录共同构成调查工作区的数据边界。

flowchart TD
    Upload["附件上传"] --> SizeCheck{"文件大小与类型"}
    SizeCheck --> Small["小内容内联消息"]
    SizeCheck --> Sheet["大表格<br/>路径传给 Agent"]
    SizeCheck --> Doc["大文档<br/>分块向量化"]

    Sheet --> PythonExec["Python 执行器"]
    Doc --> Chunking["分块与重叠"]
    Chunking --> EmbeddingSvc["向量嵌入"]
    EmbeddingSvc --> Index["pgvector/RAG 索引"]

图示来源

  • application.yaml:121-165

章节来源

  • application.yaml:121-165

数据迁移、外部系统与治理模块

  • dm 模块:负责数据迁移、清洗错误日志等与数据入库相关的逻辑。
  • otg 模块:提供时序数据与特殊日期相关服务,常用于时间维度分析。
  • external 模块:对接外部系统数据源,例如交通违章等第三方数据。
  • govern 模块:承载治理规则与治理结果,常与 graph、person、call 等模块联动产出调查结论。

这些模块在调查场景中共同构成“数据接入 → 清洗 → 分析 → 治理 → 可视化”的完整链路。

章节来源

  • application.yaml:1-193

依赖关系分析

ai-server 的核心运行时依赖如下:

graph TB
    SB["Spring Boot Web"] --> MP["MyBatis-Plus"]
    MP --> DDS["Dynamic Datasource"]
    DDS --> PG["PostgreSQL"]
    DDS --> DuckDB
    SB --> RedisClient["Spring Data Redis"]
    SB --> SaToken["Sa-Token"]
    SB --> AgentScope["AgentScope Harness"]
    AgentScope --> DashScope["DashScope 模型扩展"]
    AgentScope --> Ollama["Ollama 模型扩展"]
    AgentScope --> OpenAI["OpenAI 模型扩展"]
    AgentScope --> Mem0["Mem0 扩展"]
    AgentScope --> RAGSimple["Simple RAG 扩展"]
    AgentScope --> RedisExt["Redis 扩展"]
    SB --> PgVector["pgvector Java SDK"]
    SB --> DJL["DJL HuggingFace Tokenizers"]
    SB --> ONNX["ONNX Runtime"]

图示来源

  • pom.xml:1-405

依赖耦合特点:

  • 数据访问层通过 MyBatis-Plus 抽象 Mapper,Dynamic Datasource 屏蔽多数据源细节。
  • AgentScope 作为智能体运行时,聚合多种模型与 RAG 扩展,便于扩展新模型或存储。
  • 向量能力同时依赖 pgvector 与 ONNX/DJL,本地与在线两条路径共存。

章节来源

  • pom.xml:1-405

性能与扩展性

性能特性

  • 调度线程池隔离:SSE 心跳、DuckDB 回收、AI 清洗看门狗、附件清理共用独立调度线程池,降低批量任务对实时连接的影响。
  • DuckDB 资源预算:通过最大同时打开数、物理内存比例、每实例线程数控制临时分析负载,避免多人开案导致资源叠加。
  • 附件处理限流:非表格文件 AI 识别有并发上限、排队上限与超时控制,防止一次拖入大量文件耗尽堆或模型额度。
  • 向量嵌入批处理:支持批量推理、序列长度截断、ONNX 并行线程与优化级别可调。
  • MyBatis 缓存关闭:默认关闭二级缓存,避免多数据源环境下缓存不一致。

多节点扩展建议

  • 前置网关:建议在 ai-server 前放置 Nginx 或 API 网关,统一转发 /js/a 上下文路径,并处理 SSL、跨域与负载均衡。
  • Redis 集群:Sa-Token 与 AgentScope 扩展依赖 Redis,多节点应使用共享 Redis 集群或托管服务。
  • PostgreSQL 高可用:主库承载平台元数据与向量索引,建议使用主从或云托管 PostgreSQL,确保 pgvector 扩展可用。
  • DuckDB 本地特性:DuckDB 是进程内嵌入式数据库,不适合跨节点共享;多节点扩缩容时应注意每个实例拥有独立 DuckDB 文件。
  • 文件存储:本地文件根目录目前由 storage.local.root-path 控制,多节点应改用共享对象存储或挂载一致文件系统。
  • 向量索引同步:本地 bge-m3 模型权重与索引文件在多节点间需要一致性分发;在线 embedding 则依赖外部模型服务可用性。
  • 调度任务幂等:清理与回收任务应具备幂等性与重试策略,避免多实例重复清理造成副作用。
  • 监控与告警:重点监控 SSE 连接数、调度任务耗时、Redis 延迟、PostgreSQL 连接数、DuckDB 打开数、向量模型加载时间与附件解析队列积压。

[本节为通用性能与扩展建议,不直接分析具体代码文件]

故障排查指南

登录失败或令牌无效

  • 检查前端是否正确设置 x-token 请求头。
  • 检查 Redis 是否可达,因为 Sa-Token 会话依赖 Redis。
  • 检查是否被后登录顶下线;确认 sa-token.is-concurrent 是否符合预期。

章节来源

  • application-dev.yaml:49-87
  • application-pro.yaml:49-87

SSE 连接频繁断开

  • 检查 Spring 调度线程池是否过小,导致心跳任务被慢任务阻塞。
  • 检查代理服务器或浏览器空闲超时配置。
  • 观察 SSE 心跳任务执行耗时与活跃连接数。

章节来源

  • application.yaml:14-29
  • App.java:10-24

DuckDB 资源耗尽或打开数过多

  • 检查 zsjz.duckdb.max-open-cases 是否过小或过大。
  • 检查 memory-ratio 与 threads-per-instance 是否导致单实例占用过高。
  • 查看兜底回收器初始延迟与扫描间隔是否合理。

章节来源

  • application.yaml:37-71

向量检索不可用

  • 检查 pgvector 扩展是否安装。
  • 检查 zsjz.embedding.enabled 与 model-path 是否指向有效模型目录。
  • 检查本地模型首次加载是否成功,或是否回退到在线 embedding 模型。
  • 检查模板向量索引重建是否触发,是否超过最大文档数。

章节来源

  • application.yaml:72-165
  • pom.xml:1-405

附件解析缓慢或失败

  • 检查 Tika 文档大小与表格大小上限。
  • 检查非表格文件 AI 识别的并发数、排队数与超时时间。
  • 检查 RAG 分块大小、重叠、最大块数与 top-k 是否合理。

章节来源

  • application.yaml:121-165

工作空间异常

  • 检查运行工作空间与锁文件是否存在。
  • 检查原始路径是否可读写。
  • 确认是否触发了强制覆盖模式。

章节来源

  • App.java:10-49

结论

ai-server 是一个围绕调查研判场景构建的多模块 Spring Boot 服务。它以 App 为入口,通过模块化设计把平台、Agent、清洗、知识、图谱、通话、人员、传输、轨迹、治理、搜索、迁移、外部系统与嵌入向量能力解耦;通过 MyBatis-Plus 与 Dynamic Datasource 统一管理 PostgreSQL、Redis、DuckDB;通过 Sa-Token 提供轻量认证;通过 SSE 与定时任务保障长连接与资源回收;通过 AgentScope RAG、向量检索与 Python 执行器形成“数据—智能—结论”的调查闭环。

在生产部署中,应重点关注 Redis 与 PostgreSQL 的可用性、DuckDB 的资源预算、向量模型的加载成本、附件解析队列的背压控制,以及多节点间文件存储与模型权重的同步策略。