本文引用的文件
本文面向 ai-server Spring Boot 应用,聚焦其应用入口、包扫描范围、模块依赖加载方式,以及基于 Spring Boot 3.5 + Java 25 的启动阶段横切能力。重点说明多数据源初始化、Redis 连接、Agent 工作区与系统目录初始化、全文检索索引创建、智能清洗批次恢复与看门狗、向量索引自举等 ApplicationRunner / CommandLineRunner / ApplicationListener 行为,并解释配置文件组织与环境变量覆盖机制。
ai-server 是 Maven 子模块,位于仓库根 pom.xml 的 modules 中;父 POM 继承 Spring Boot 3.5.16 的 starter parent,因此 Spring Boot 版本由父 POM 统一约束。ai-server 自身声明 Java 25,并通过多个 Spring Boot Starter 引入 Web、Validation、Data Redis、AOP、Sa-Token、MyBatis-Plus、动态数据源、AgentScope、PostgreSQL、pgvector、DuckDB、Tika、DJL HuggingFace Tokenizers 和 ONNX Runtime 等能力。
graph TB
RootPOM["根 POM<br/>Spring Boot 3.5.16"] --> ServerModule["ai-server 子模块<br/>Java 25"]
ServerModule --> SpringBootStarterWeb["spring-boot-starter-web"]
ServerModule --> SpringBootStarterValidation["spring-boot-starter-validation"]
ServerModule --> SpringBootStarterDataRedis["spring-boot-starter-data-redis"]
ServerModule --> SpringBootStarterAop["spring-boot-starter-aop"]
ServerModule --> SaToken["sa-token-spring-boot3-starter"]
ServerModule --> MybatisPlus["mybatis-plus-spring-boot3-starter"]
ServerModule --> DynamicDatasource["dynamic-datasource-spring-boot3-starter"]
ServerModule --> AgentScope["agentscope-harness 及扩展"]
ServerModule --> PostgreSQL["postgresql 驱动"]
ServerModule --> PgVector["com.pgvector pgvector"]
ServerModule --> DuckDB["duckdb_jdbc"]
ServerModule --> Tika["spring-ai-tika-document-reader"]
ServerModule --> DJL["ai.djl.huggingface tokenizers"]
ServerModule --> OnnxRuntime["onnxruntime"]
图示来源
章节来源
com.zsjz.ai.App 是 Spring Boot 启动入口,启用调度任务注解,并使用 SpringApplication.run 启动容器。application.yaml 定义通用配置与业务配置命名空间,application-dev.yaml 与 application-pro.yaml 按 profile 注入数据库、Redis、Elasticsearch、存储路径、Sa-Token 和 Neo4j 等环境差异配置。DataSourceConfig 替换动态数据源自动装配的默认路由,使 @DS("slave") 能结合案件上下文动态路由到 DuckDB 案件库。AppLoadEndEventListener(CommandLineRunner)、SearchIndexInitializer(ApplicationRunner)、AiCleanWatchdog(ApplicationReadyEvent)、AiCleanTemplateVectorIndex(ApplicationReadyEvent)在启动不同阶段执行清理、缓存预热、索引重建或恢复操作。章节来源
应用以 Spring Boot 容器为中心,启动后完成以下关键横切能力:
application.yaml 与各 profile 配置,启用定时任务线程池、跨域、上传大小限制、AI 研判、DuckDB 预算、向量模型开关、智能清洗配置、Agent RAG、Sa-Token 等。CaseRoutingDataSource 将业务 SQL 中的 @DS("slave") 路由到当前用户打开的案件 DuckDB 数据源。运行期定时任务:SSE 心跳、数据源兜底回收、智能清洗看门狗、附件清理等。
sequenceDiagram
participant JVM as "JVM"
participant App as "App 主类"
participant Spring as "Spring 容器"
participant Config as "配置与数据源"
participant Redis as "Redis"
participant Runner as "CommandLineRunner / ApplicationRunner"
participant Ready as "ApplicationReadyEvent 监听器"
participant Scheduler as "定时任务"
JVM->>App: 调用 main()
App->>Spring: SpringApplication.run(App.class, args)
Spring->>Config: 加载 application.yaml 与 profile
Spring->>Config: 初始化 PostgreSQL 与动态数据源
Spring->>Redis: 初始化 Redis 连接
Spring-->>Runner: 触发 CommandLineRunner / ApplicationRunner
Runner->>Runner: 创建系统目录与 Agent 工作区
Runner->>Runner: 初始化全局缓存与 RocksDB
Spring-->>Ready: 触发 ApplicationReadyEvent
Ready->>Ready: 恢复上一轮智能清洗批次
Ready->>Ready: 检测并可能重建模板向量索引
Spring->>Scheduler: 启动 SSE 心跳、数据源回收、看门狗等定时任务
图示来源
App 使用 @SpringBootApplication,因此 Spring Boot 默认从 com.zsjz.ai 开始扫描组件。代码中没有显式 @ComponentScan,也没有对 @MapperScan 做全局扫描;注释明确说明 Mapper 依靠类上的 @Mapper 注解注册。这意味着新增 Mapper 必须添加 @Mapper,否则会因找不到映射而启动失败或运行时异常。
classDiagram
class App {
+main(args)
+init()
}
class DataSourceConfig {
+caseRoutingDataSource(providers, properties)
}
class AppLoadEndEventListener {
+run(args)
}
class SearchIndexInitializer {
+run(args)
}
class AiCleanWatchdog {
+failOrphansOnStartup(event)
+failStale()
}
class AiCleanTemplateVectorIndex {
+onApplicationReady(event)
+rebuildAsync()
+isAvailable()
}
App --> DataSourceConfig : "容器装配"
App --> AppLoadEndEventListener : "启动后执行"
App --> SearchIndexInitializer : "启动后执行"
App --> AiCleanWatchdog : "应用就绪后执行"
App --> AiCleanTemplateVectorIndex : "应用就绪后执行"
图示来源
章节来源
项目使用 dynamic-datasource 管理 PostgreSQL 主库,同时通过 CaseRoutingDataSource 替换默认的 DynamicRoutingDataSource,使 @DS("slave") 能够根据当前会话中的 caseId 路由到 DuckDB 案件库。DataSourceConfig 把 dynamic-datasource 的 primary、strict、strategy、p6spy、seata、graceDestroy 等属性手动拷贝到自定义数据源上,否则这些配置会静默失效。
CaseDataSourceReaper 是一个定时兜底回收器:当用户会话自然过期、被顶下线或被踢下线时,如果没有对应的登出监听回调,已打开的案件 DuckDB 数据源可能长期占用内存配额。该回收器定期扫描已打开案件,判断归属 token 是否仍有效;若无效且没有业务探针报告“仍在运行”,则关闭数据源并清理对应缓存。它采用保守策略:查询会话状态失败时不释放,宁可多占一点内存也不误关正在使用的数据源。
flowchart TD
Start(["定时任务 reap"]) --> CheckOpen["是否有已打开的案件数据源?"]
CheckOpen -->|否| End(["结束本轮扫描"])
CheckOpen -->|是| Iterate["遍历每个 caseId"]
Iterate --> GetOwner["获取归属 userId 与 ownerToken"]
GetOwner --> SessionAlive{"归属会话是否存活?"}
SessionAlive -->|是| NextCase["下一个 caseId"]
SessionAlive -->|否| HasWork{"是否有在途任务?"}
HasWork -->|是| Skip["跳过回收,等待下一轮"]
HasWork -->|否| Close["关闭案件数据源并清理缓存"]
Close --> NextCase
NextCase -->|还有| Iterate
NextCase -->|无| End
图示来源
章节来源
Redis 通过 spring-boot-starter-data-redis 自动装配,并在 dev/pro 配置中指定 host、port。Sa-Token 的 Redis Jackson 集成使 Redis 成为登录态的硬依赖:Redis 不可用时 StpUtil.* 直接抛异常,没有降级实现。项目中多处使用 RedisTemplate 与 StringRedisTemplate,包括系统健康检查、缓存管理、登录活动记录、案件数据缓存等。
需要注意:Sa-Token 的生产默认单端登录,后登录会顶掉前登录;联调多端时可传入 -DSA_TOKEN_CONCURRENT=true 或通过环境变量覆盖。
章节来源
AppLoadEndEventListener 实现 CommandLineRunner,在 Spring 容器启动完成后执行目录初始化与缓存初始化:
CaseDataCache 不在启动时预热,因为未开案时 slave 数据源不存在,强行读取会导致启动失败;改为开案后按需加载。初始化 Tower 相关工具。
flowchart TD
Entry(["CommandLineRunner.run"]) --> InitDir["创建系统目录"]
InitDir --> GlobalCache["初始化全局缓存"]
GlobalCache --> RocksDB["初始化系统 RocksDB 数据"]
RocksDB --> CaseCacheNote["暂不预热案件缓存"]
CaseCacheNote --> Tower["初始化 Tower 工具"]
Tower --> Done(["完成"])
图示来源
章节来源
SearchIndexInitializer 实现 ApplicationRunner,在应用启动时尝试创建 Elasticsearch 全文检索索引。ES 不可用时仅记录错误日志,不阻断启动;索引会在首次写入时由底层服务重试创建。这种设计让全文检索成为增强能力而非启动硬性依赖。
章节来源
AiCleanWatchdog 承担两个职责:
ApplicationReadyEvent,将所有非终态的智能清洗批次标记为 FAILED,原因是虚拟线程不跨进程重启,进程重启后旧 worker 一定已死亡。用户可重新发起清洗,从而形成恢复路径。运行时看门狗:扫描长时间无进展的非终态批次,将其标记为 FAILED,以便用户重试。判定逻辑基于 update_time 与 stale-minutes 配置。
flowchart TD
Startup(["应用就绪"]) --> MarkFailed["将全部非终态批次标为 FAILED"]
MarkFailed --> LogRestart["记录恢复日志"]
Runtime(["定时扫描"]) --> CheckStale{"是否存在超时批次?"}
CheckStale -->|否| WaitNext["等待下一轮"]
CheckStale -->|是| MarkStale["标记为 FAILED 并记录原因"]
MarkStale --> WaitNext
图示来源
章节来源
AiCleanTemplateVectorIndex 负责表结构与 AI 模板元数据的向量索引。其启动自举逻辑监听 ApplicationReadyEvent:
整个链路遵循 fail-open:向量能力缺失时不阻断主业务,只是降低模板语义召回与 Agent 选表能力。
flowchart TD
Ready(["ApplicationReadyEvent"]) --> Enabled{"向量索引已启用?"}
Enabled -->|否| Exit["退出"]
Enabled -->|是| Available{"嵌入模型可用?"}
Available -->|否| LogUnavailable["记录不可用信息"]
LogUnavailable --> Exit
Available -->|是| Dimensions{"维度有效?"}
Dimensions -->|否| LogBadDimensions["记录维度无效"]
LogBadDimensions --> Exit
Dimensions -->|是| Exists{"向量表存在且有数据?"}
Exists -->|是| Exit
Exists -->|否| RebuildAsync["异步全量重建"]
图示来源
章节来源
spring-boot-starter-parent:3.5.16,因此 ai-server 运行于 Spring Boot 3.5 系列。java.version=25,ONNX Runtime 与 Java 25 配合时可能出现 restricted method 警告,可通过 JVM 参数 --enable-native-access=ALL-UNNAMED 消除。CaseRoutingDataSource 接入。Agent 能力:AgentScope harness 及 DashScope、Ollama、OpenAI 扩展,Mem0、RAG Simple、Redis 扩展。
graph LR
App["App 主类"] --> Spring["Spring Boot 3.5.16"]
Spring --> PostgreSQL["PostgreSQL"]
Spring --> PgVector["pgvector"]
Spring --> DuckDB["DuckDB 案件库"]
Spring --> Redis["Redis"]
Spring --> ES["Elasticsearch"]
Spring --> AgentScope["AgentScope 扩展"]
Spring --> Embedding["本地 bge-m3 / 在线 embedding"]
图示来源
章节来源
max-open-cases、memory-ratio、threads-per-instance 控制并发案件数与内存占比,防止多人开案叠加资源。eager-load 在启动时后台预热,也支持懒加载。[本节为通用性能说明,不直接分析具体代码片段]
server.port=8980,server.servlet.context-path=/js/a。/js/a。章节来源
spring.datasource.dynamic.primary=master,master.url、username、password、driver-class-name。章节来源
spring.data.redis.host、spring.data.redis.port;Sa-Token 依赖 Redis 且无降级实现。章节来源
zsjz.embedding.enabled:决定是否使用本地 bge-m3。zsjz.embedding.model-path:本地模型目录。zsjz.ai-clean.vector.enabled:向量索引总开关。zsjz.ai-clean.vector.auto-rebuild-on-startup:启动自举开关。zsjz.ai-clean.vector.enabled 是否为 true。章节来源
SearchIndexInitializer 捕获异常并记录日志;Elasticsearch 配置在 dev/pro 的 spring.elasticsearch.uris。uris、连接超时、套接字超时。章节来源
CaseDataSourceReaper 定时扫描,依据归属 token 与业务探针决定是否回收。章节来源
AppLoadEndEventListener.initDir()。PathConst 指向的路径是否被安全软件或容器卷挂载限制。章节来源
ai-server 以 com.zsjz.ai.App 为唯一应用入口,使用 Spring Boot 3.5.16 与 Java 25,围绕 PostgreSQL、Redis、Elasticsearch、pgvector、DuckDB 与 AgentScope 构建。启动阶段的关键横切能力包括:系统目录与 Agent 工作区初始化、全局缓存与 RocksDB 初始化、全文检索索引幂等创建、智能清洗批次恢复、模板向量索引自举,以及数据源兜底回收。配置通过 application.yaml 统一管理业务配置,并通过 ${ENV_NAME:默认值} 形式支持环境变量覆盖;数据库、Redis、Elasticsearch、存储路径、Sa-Token 与 Neo4j 等环境差异放在 application-dev.yaml 与 application-pro.yaml 中。常见启动问题主要集中在端口、数据库连通性、Redis 可用性、向量库开关与模型路径、以及案件数据源生命周期管理上。