目录 · 第 02 章4 卷 12 章

第 02 章 · 卷一 先搞懂整个项目

系统总体架构与技术选型

六层架构、每条依赖的边界,以及 Spring AI 与 Spring AI Alibaba 各管什么。

约 11 分钟9 节
已知事实

平台已有设备接入、航线规划、任务下发、遥测回传和告警处置能力。技术栈确定为 Java 21、Spring Boot 3、Spring AI、Spring AI Alibaba、PostgreSQL、Redis、RocketMQ、Milvus、MinIO、Vue 3、Cesium 和 SSE。

目标设计

将现有业务能力封装为可调用的业务接口,新增 Copilot 编排服务,按六层划分职责,并建立同步调用、异步处理和数据存储边界。

验证结果

暂无,本文只做方案设计。 上述测试项是验收计划,不代表系统已经通过测试或达到任何性能指标。

1要解决的问题

本章要解决三个技术问题:系统怎么分层、各技术怎么分工、一次用户请求怎么经过 AI 能力和现有业务系统。

核心结论:采用六层架构,以 Spring Boot 承载 Copilot 业务,以 Spring AI 统一模型调用,以 Spring AI Alibaba 提供可选的模型与 Agent 集成能力,以现有业务服务负责真实业务操作。

这里有三个设计原则。

  • 模型负责理解和编排,业务服务负责执行。 大模型可以判断用户意图、选择工具、组织参数,但不能直接修改数据库或绕过业务服务下发飞行任务。
  • 同步链路只做决策所需的关键工作。 用户发起规划后,需要及时得到设备、空域和航线校验结果。耗时较长的任务执行、报告生成等工作放到异步链路。
  • 不同类型的数据交给合适的存储。 业务事实进 PostgreSQL,短期状态进 Redis,异步事件进 RocketMQ,语义检索进 Milvus,文件和影像进 MinIO。

本章事实边界:

类型内容
已知事实平台已有设备接入、航线规划、任务下发、遥测回传和告警处置能力。技术栈确定为 Java 21、Spring Boot 3、Spring AI、Spring AI Alibaba、PostgreSQL、Redis、RocketMQ、Milvus、MinIO、Vue 3、Cesium 和 SSE。
目标设计将现有业务能力封装为可调用的业务接口,新增 Copilot 编排服务,按六层划分职责,并建立同步调用、异步处理和数据存储边界。
验证结果暂无,本文只做方案设计。

2在整个系统中的位置

Copilot 不是替换原有无人机平台,而是在现有业务能力上增加一个 AI 应用层。

用户仍然通过三维地图选择区域,但后端不再只接收固定表单。Copilot 还要理解自然语言,将用户意图转换成结构化参数,再调用已有业务能力。

建议的核心服务如下:

服务主要职责是否由 AI 决策
Copilot 服务会话管理、意图理解、工具编排、执行计划、人工确认是,负责理解和编排
设备服务查询无人机、载荷、在线状态和可用性否,返回业务事实
空域服务查询禁飞区、限飞区、授权和飞行约束否,执行规则校验
航线服务生成航线、检查航线参数和飞行约束否,按规则计算
任务服务创建任务、管理任务状态、下发任务否,负责受控执行
遥测与告警服务接收飞行状态、视觉告警和异常事件否,负责事实采集与事件处理
报告服务汇总任务、航迹、告警和影像,生成报告可使用 AI 辅助总结,但报告数据必须来自真实记录

这里最重要的边界是:Copilot 可以提出执行计划,但不能把模型生成的内容直接当成已批准的飞行指令。

例如,模型输出了一个航线规划工具调用,只代表它提出了调用请求。后端还要校验用户权限、区域参数、空域限制和航线结果。创建任务与实际起飞之间还要保留明确的确认和授权环节。

这样设计有两个好处:AI 能力可以独立升级,原有设备和任务系统也不需要因为接入大模型而重写。

3总体设计

整个系统划分为六层。层与层之间通过明确的接口交互,不允许上层绕过业务规则直接操作底层数据。

六层的职责需要明确区分。

层放什么不做什么
用户接入层Vue 3 页面、Cesium 三维地图、SSE 客户端、任务确认界面不保存业务权威状态,不在浏览器里执行飞行决策
业务应用层Spring Boot Controller、Service、权限校验、任务草稿、业务工具适配器不让大模型直接操作数据库,不把模型输出视为可信指令
AI 能力层Prompt、工具定义、模型调用编排、RAG 检索、上下文管理、结构化输出校验不负责设备协议接入,不代替任务服务执行业务规则
模型服务层对话模型、Embedding 模型、视觉模型及其 API不保存平台的任务权威状态,不决定用户是否有权起飞
数据层关系数据、缓存、消息、向量和文件不承载业务编排,不把缓存当作永久事实来源
基础设施层部署、网络、服务发现、日志、指标、链路追踪、密钥管理不实现 Copilot 业务逻辑

这六层是逻辑分层,不代表必须部署成六个独立服务。

例如,AI 能力层和业务应用层在初期可以部署在同一个 Copilot 应用中,但代码包、接口和职责仍然要分开。等到调用量、团队分工或稳定性要求发生变化,再决定是否拆分部署。

模型服务层也不等于 Spring AI。 模型服务层是实际提供推理能力的模型及其服务端点;Spring AI 是 Java 应用调用这些模型的抽象和集成框架。

系统总体架构图 · 六层,以及同步与异步两条流 —— 图中的服务划分、接口与表名都是目标设计,不代表已经实现。

4完整执行链路

系统的请求路径要分成两条:用户请求的同步链路,以及任务执行过程中的异步链路。

4.1 同步链路:生成可确认的执行计划

用户在地图圈选区域,并输入巡检目标后,执行顺序如下。

  1. Vue 3 将自然语言、区域几何数据、会话标识和必要的地图上下文提交给 Copilot。
  2. CopilotController 校验请求格式与登录身份,交给 CopilotApplicationService。
  3. 应用服务加载会话上下文,调用模型理解用户意图,生成结构化的任务目标。
  4. AI 编排器通过工具接口查询设备可用性、空域限制,并请求航线服务生成候选航线。
  5. 业务服务执行权限、参数、空域和航线校验,返回真实业务结果。
  6. Copilot 将结果整理成任务草稿,保存草稿及其版本信息。
  7. 后端通过普通 HTTP 响应或 SSE 返回规划进度与最终草稿,前端展示设备、航线、风险提示和待确认事项。

这条链路必须在返回“可确认的计划”之前完成关键校验。不能只因为模型已经返回了文字,就告诉用户规划成功。

同步不代表每一步都必须由同一个线程完成。它的含义是:在当前请求的响应语义上,必须拿到结果才能进入下一步。

如果规划涉及耗时较长的视觉分析或复杂计算,可以改成异步规划任务,通过 SSE 推送状态;此时前端展示的是“规划中”,而不是一个尚未校验的成功结果。

4.2 人工确认与异步执行

用户点击确认后,后端重新检查草稿版本、用户权限、空域有效性和执行条件,避免使用过期的规划结果。

确认成功后:

  1. 任务服务创建正式任务记录。
  2. 在一致性设计允许的情况下,将任务状态变更和待发布事件写入同一 PostgreSQL 事务。
  3. 通过 Outbox 发布流程将事件发送到 RocketMQ。
  4. 执行消费者处理任务下发请求,调用现有任务服务或设备控制链路。
  5. 设备回传遥测,告警服务产生事件,消费者根据事件更新任务状态。
  6. 任务结束后发布完成事件,报告服务汇总数据并生成报告。
  7. Copilot 通过 SSE 向前端推送任务状态、告警和报告生成进度。

这里有一个容易被忽略的细节:数据库事务与 RocketMQ 发布不是天然的同一个原子操作。不能简单地在事务提交前后随意发送消息,否则可能出现数据库已经创建任务、消息却没有发出去的情况。Outbox、可靠重试和幂等消费是目标设计的一部分。

4.3 同步与异步的分界

操作处理方式原因
身份、权限、请求格式校验同步不通过就不能继续
查询设备和空域约束规划阶段同步需要真实结果才能形成可信计划
航线生成和校验默认同步;耗时过长时异步化需要校验后的航线才能让用户确认
保存任务草稿同步必须确认草稿已经持久化
确认任务、创建正式任务同步提交业务事务必须明确告知用户是否受理成功
实际任务下发异步处理设备执行有延迟,且可能失败或重试
遥测和告警事件处理异步消费需要解耦设备事件生产和后续处理
批量影像分析、报告生成异步处理耗时不可控,不适合占用用户请求线程

SSE 只是服务端向浏览器推送事件的通道,不是消息队列,也不负责保证事件永久保存。客户端断线后,应能通过任务查询接口恢复当前状态;必要时还要根据事件 ID 补取遗漏信息。

5技术选型与原理

5.1 Spring Boot、Spring AI 和 Spring AI Alibaba 的职责边界

这三个技术不是同一层面的替代品。

Spring Boot:应用运行与业务服务框架。

它负责依赖注入、配置管理、Web 接口、事务、参数校验和应用生命周期。CopilotController、CopilotApplicationService、业务工具适配器和数据访问层都可以基于 Spring Boot 开发。

Spring AI:模型交互抽象与 AI 应用能力。

它提供 Java 应用调用聊天模型、Embedding 模型、向量存储和工具调用等能力的统一接口。应用可以使用 ChatClient 组织模型请求,利用工具定义将业务方法暴露给模型调用,并结合向量检索构建 RAG。

Spring AI 不会自动替你实现无人机业务规则,也不意味着只配置一个模型就拥有完整的 Agent。

Spring AI Alibaba:面向阿里系 AI 技术生态的扩展。

它可以提供与阿里云模型服务、相关 Agent 能力及生态组件的集成。具体使用哪些能力,要根据所选版本和部署环境决定。它不是 Spring Boot 的替代品,也不是 Spring AI 的必需依赖。

目标设计中,优先通过 Spring AI 的通用接口组织模型调用;只有确实需要 Spring AI Alibaba 提供的特定集成或 Agent 能力时,才引入对应模块,避免把业务代码绑定到某个模型供应商。

5.2 数据组件怎么选

组件存储内容为什么选择不适合承担什么
PostgreSQL用户、会话元数据、任务草稿、任务记录、执行状态、审计记录支持事务、关系约束和复杂查询,适合业务权威数据大规模原始影像存储
Redis会话短期状态、热点查询缓存、短期幂等键、限流状态读写快,适合有过期时间的临时数据唯一的任务状态来源
RocketMQ任务执行事件、遥测处理事件、告警事件、报告生成事件支持异步解耦、消费重试和消息积压治理实时业务查询的权威数据库
Milvus文档切片、操作手册、历史案例的向量及检索元数据面向向量近邻检索,适合语义召回任务状态与事务数据
MinIO图片、视频、飞行报告附件、原始文件和大体积影像对象存储适合大文件和文件生命周期管理需要复杂事务查询的结构化业务记录

这里的分工不是说其他组件完全做不到,而是避免让一种存储承担它不擅长的职责。

例如,Redis 可以保存任务状态的缓存,但任务状态的最终依据仍应是 PostgreSQL 中的权威记录。Milvus 可以召回相关操作手册,却不能凭向量相似度判断某架无人机是否在线。MinIO 保存影像文件,PostgreSQL 保存文件的对象键、任务关联、采集时间和分析状态。

5.3 至少三组备选方案的取舍

向量检索:Milvus、pgvector、Elasticsearch

方案优点代价选择建议
Milvus专门面向向量检索,适合独立扩展检索能力增加一套服务及运维成本目标设计中采用,适合将知识检索作为独立能力建设
PostgreSQL + pgvector业务数据和向量数据可以放在同一数据库,部署更简单向量检索与业务数据库共享资源,扩展方式受整体数据库架构影响数据量和检索压力较小时值得优先评估
Elasticsearch适合关键词、过滤条件和全文检索,也能支持向量检索如果系统只需要向量召回,可能引入不必要的复杂度已有搜索集群、需要混合检索时考虑

不能只凭“Milvus 是向量数据库”就认定它一定更快。最终选择应根据数据规模、过滤条件、召回质量、延迟和运维成本进行验证。

消息队列:RocketMQ、Kafka、RabbitMQ

方案优点代价选择建议
RocketMQ适合业务事件、延迟消息及可靠消费等场景需要维护 Broker、消费组和消息治理机制本项目的目标设计选择
Kafka适合高吞吐事件流、日志管道和流式处理生态对业务消息的重试、延迟和消费治理需要结合架构额外设计事件流规模大、流处理需求强时考虑
RabbitMQ路由能力灵活,常规任务队列容易理解大规模事件流和长期事件处理的设计思路与 Kafka 不同任务队列规模适中、团队已有相关经验时考虑

选择 RocketMQ 并不代表它在所有指标上优于其他队列。最终要验证消息可靠性、消费延迟、失败重试、积压恢复和运维能力。

模型接入:直接调用 SDK、Spring AI、Spring AI Alibaba

方案优点代价选择建议
直接调用模型 SDK接口直接,能快速使用供应商特有功能业务代码容易绑定具体厂商,切换模型时修改范围较大仅在特殊能力无法通过通用接口表达时使用
Spring AI统一聊天模型、工具调用和向量检索等应用接口特定供应商的高级能力可能仍需要扩展作为 Copilot 的主要模型交互抽象
Spring AI Alibaba可以利用阿里系模型服务和相关 Agent 集成能力具体 API 和能力受模块版本影响,增加生态依赖有明确的阿里系集成需求时引入

这三者并不是互斥关系。更合理的目标结构是以 Spring AI 作为主要抽象层,根据实际模型供应商接入对应实现;需要 Spring AI Alibaba 的特定能力时,再在适配层中使用它。

5.4 同步接口与异步事件的边界

同步接口用于“必须立即得到业务结果”的操作,例如查询设备可用性、调用航线规划、校验空域和保存草稿。

异步事件用于“业务已经受理,但后续工作可以继续执行”的操作,例如任务下发、报告生成和批量影像分析。

建议统一以下接口风格:

  • POST /api/copilot/sessions/{sessionId}/plans:创建规划请求。
  • GET /api/copilot/plans/{planId}:查询规划状态与结果。
  • POST /api/copilot/plans/{planId}/confirm:确认任务草稿。
  • GET /api/copilot/tasks/{taskId}:查询正式任务状态。
  • GET /api/copilot/tasks/{taskId}/events:通过 SSE 接收任务事件。

以上为目标设计接口,不代表已有接口已实现。

异步消息可使用 TaskConfirmed、TaskDispatchRequested、TelemetryReceived、AlertRaised 和 ReportGenerationRequested 等事件类型。消费者必须考虑重复投递、乱序、超时和重试,不能假设每条消息只会被处理一次。

5.5 服务边界与部署方式

目标设计建议:先模块化单体,再按负载和组织边界拆服务。

如果目前只有一个开发团队,Copilot 可以先作为独立的 Spring Boot 应用,内部按模块划分会话、AI 编排、工具适配、任务确认和报告协调。已有的设备、空域、航线和任务服务继续独立运行,不需要为了 AI 接入而合并。

如果现有业务服务本身已经是独立微服务,Copilot 通过 HTTP 或内部 RPC 调用其公开接口。跨服务的长耗时操作通过 RocketMQ 传递事件。

部署边界建议如下:

  • Vue 3 + Cesium:静态前端资源,通过网关访问后端。
  • Copilot:独立 Spring Boot 应用,负责 AI 编排和用户交互。
  • 现有业务服务:继续负责设备、空域、航线和任务等权威操作。
  • 模型服务:按供应商和部署环境访问,密钥只保存在服务端。
  • 数据组件:PostgreSQL、Redis、RocketMQ、Milvus、MinIO 分别部署或托管。

不要为了体现“微服务架构”而把每个 AI 功能拆成一个服务。服务拆分应由独立扩缩容、故障隔离、团队协作和资源需求驱动。

6关键数据结构与代码

6.1 Spring AI 配置片段

下面展示一个基于 Spring Boot 3、Spring AI 的目标配置。具体依赖版本、模型提供方和属性名称需要以实际采用的 Spring AI BOM 及模型 Starter 为准。

application.yml 示例:

text
spring:
  ai:
    model:
      chat: <model-provider>
    openai:
      api-key: ${LLM_API_KEY}
      chat:
        options:
          model: ${LLM_MODEL}
          temperature: 0.1

这里的 <model-provider> 是说明性占位符,不应直接复制到配置文件。实际工程中要使用所选 Spring AI 版本支持的模型配置,并确认 spring.ai.model.chat 是否适用于该版本及所安装的 Starter。

环境变量由部署环境注入,不能把真实 API Key 写入 Git 仓库。temperature 用于控制生成的随机性,不是业务安全开关。即使将其设得很低,也不能保证模型输出一定正确。

在 Java 代码中,可以通过 ChatClient 统一调用模型:

java
@Configuration
public class CopilotAiConfig {

    @Bean
    ChatClient copilotChatClient(ChatClient.Builder builder) {
        return builder
                .defaultSystem("""
                    你是无人机平台的任务规划助手。
                    只能根据工具返回的业务事实生成规划建议。
                    不得声称任务已经执行,除非业务服务确认。
                    涉及飞行执行时,必须经过后端校验与人工确认。
                    """)
                .build();
    }
}

调用模型的应用服务可以保持较薄:

java
@Service
public class CopilotPlanningService {

    private final ChatClient chatClient;

    public CopilotPlanningService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String analyzeIntent(String userRequest) {
        return chatClient.prompt()
                .user(userRequest)
                .call()
                .content();
    }
}

这只是展示 Spring AI 的调用边界,不是完整的生产实现。真实规划接口应使用结构化输出 DTO,校验区域坐标、任务类型、时间范围和必要参数,再调用设备、空域和航线工具。不能将 content() 返回的自然语言直接作为任务执行参数。

如果接入 Spring AI Alibaba,应在确认具体模块和版本后配置对应的模型或 Agent 集成,并通过独立适配层暴露给应用服务。不要在每个业务类里直接依赖供应商 SDK。

6.2 数据表与状态字段

目标设计中的核心业务表可以是:

表名关键字段用途
copilot_sessionid、user_id、status、created_at会话元数据
copilot_planid、session_id、version、plan_json、status、expires_at保存规划草稿与版本
drone_taskid、plan_id、device_id、status、created_at正式任务及执行状态
copilot_audit_logid、user_id、action、resource_id、created_at记录关键操作
outbox_eventid、aggregate_id、event_type、payload、status可靠发布业务事件

建议规划状态至少覆盖 PLANNING、READY_FOR_CONFIRMATION、CONFIRMED、FAILED 和 EXPIRED。正式任务使用独立状态机,例如 CREATED、DISPATCHING、IN_PROGRESS、COMPLETED 和 FAILED。

规划状态和任务状态不能混在一个字段里。规划成功不代表任务已经创建,任务创建成功也不代表设备已经起飞。

6.3 业务工具接口

Copilot 不直接访问设备数据库,而是通过工具适配器调用业务服务。

java
public interface DroneCapabilityService {

    DeviceAvailability queryAvailability(
            String regionId,
            Instant plannedStart,
            Instant plannedEnd);

    AirspaceCheckResult checkAirspace(
            RegionGeometry region,
            Instant plannedStart,
            Instant plannedEnd);

    RoutePlanResult planRoute(
            RoutePlanRequest request);
}

这些接口是目标设计中的 Java 契约,实际 DTO 需要根据已有业务服务定义。

调用顺序应当是:查询设备可用性、校验空域、生成航线、校验规划结果、保存草稿。实际顺序可以根据航线算法的输入要求调整,但空域和权限规则不能由模型自行决定。

对于任务确认,应由独立的应用服务负责校验草稿版本、重新验证关键约束、创建正式任务,并保证重复提交不会创建重复任务。

7异常、安全与边界条件

系统架构不仅要考虑正常调用,还要考虑模型、业务服务和消息系统各自失败时如何处理。

风险处理方式
模型超时或不可用设置超时和有限重试;返回明确的规划失败状态,不自动执行
模型生成错误参数使用 DTO、枚举、数值范围和业务规则校验;不合法就拒绝调用工具
设备服务或空域服务超时将规划标记为待重试或失败;不能将“未查询到”当成“没有限制”
用户重复点击确认使用幂等键、草稿版本校验和数据库约束,防止重复建任务
RocketMQ 重复投递消费端根据事件 ID 或业务幂等键去重
任务下发失败保存失败原因,按规则重试或进入人工处置,不把消息发送成功当作设备执行成功
SSE 断开提供任务查询和状态恢复接口,不依赖连接本身保存状态
向量检索召回错误内容检查来源、版本和权限;检索结果只作为参考,不覆盖实时业务事实

无人机执行涉及真实设备和飞行安全,因此必须明确以下边界。

第一,模型不能绕过后端授权。即使模型判断用户“应该有权限”,也必须由业务服务检查权限。

第二,空域规则和设备状态以权威业务系统返回的数据为准。知识库中的操作手册不能替代实时空域查询。

第三,模型输出不能直接触发起飞。任务草稿必须经过参数校验和人工确认;如果执行条件在确认后发生变化,还要重新检查。

第四,日志要记录关键决策输入、工具调用结果、任务状态变更和确认人,但应控制敏感信息与大体积文件的写入。API Key、完整凭据和不必要的个人信息不能进入普通日志。

第五,RAG 文档和工具返回内容都应视为待验证的数据。文档中即使出现要求模型忽略系统规则的文字,也不能改变工具权限和后端安全策略。

8测试与验证

本章涉及的架构目前属于目标设计,因此测试重点是验证边界是否正确,而不是虚构性能结果。

建议按以下层次开展验证。

第一层:组件与契约测试。

  • 验证 ChatClient 能否通过配置调用目标模型。
  • 验证模型输出缺少必填字段、区域坐标越界、时间范围非法时,系统是否拒绝进入业务规划。
  • 验证设备、空域和航线工具适配器的请求及响应契约。
  • 验证数据库状态字段、版本字段和必要约束。

第二层:集成测试。

  • 模拟设备服务超时,确认系统不会将规划标记为可确认。
  • 模拟空域校验失败,确认不能生成可执行任务。
  • 模拟模型返回错误工具参数,确认后端校验能够拦截。
  • 模拟 RocketMQ 重复投递,确认不会重复创建任务。
  • 模拟 SSE 断线,确认客户端能够通过任务查询接口恢复状态。

第三层:端到端测试。

从用户圈选区域开始,依次验证意图理解、设备查询、空域校验、航线生成、草稿保存、人工确认、任务受理、执行事件和报告生成。

测试环境应使用模拟设备或受控测试环境,不能用未经授权的真实飞行来验证普通业务逻辑。

需要记录的验证指标包括模型调用延迟、规划成功率、工具调用失败率、消息积压量、任务状态一致性、重复任务数量和 SSE 状态恢复结果。具体阈值应在测试环境建立基线后确定。

验证结果:暂无,本文只做方案设计。 上述测试项是验收计划,不代表系统已经通过测试或达到任何性能指标。

9面试表达与追问

问答一:为什么要分六层?直接用 Spring Boot 调模型不行吗?

参考回答:

Spring Boot 当然可以直接调用模型,但问题不在于能不能调用,而在于职责是否清楚。

我把系统分成用户接入、业务应用、AI 能力、模型服务、数据和基础设施六层。AI 负责理解用户意图、组织工具调用,业务应用负责权限和规则,模型服务负责推理,数据层负责持久化和检索。

这样模型供应商可以更换,AI 编排也可以独立调整,原来的设备和任务服务不用跟着重写。六层是逻辑分层,不要求一开始部署成六个独立服务。

问答二:为什么同时用 Spring Boot、Spring AI 和 Spring AI Alibaba?

参考回答:

它们解决的问题不一样。

Spring Boot 负责 Java 应用的运行、接口、依赖注入和事务。Spring AI 提供统一的模型交互接口,以及工具调用、向量检索等 AI 应用能力。Spring AI Alibaba 则用于需要阿里系模型或相关生态能力的场景。

我不会把三者混成一个技术栈名称。项目里以 Spring AI 作为主要模型交互抽象,只有在明确需要特定扩展时才引入 Spring AI Alibaba,避免业务逻辑绑定到某个供应商。

问答三:为什么不用 Redis 保存所有状态,也不让模型直接创建任务?

参考回答:

Redis 适合缓存和短期状态,但不适合作为正式任务的唯一权威来源。任务创建、状态变更和审计记录需要可靠的持久化,因此核心业务状态放在 PostgreSQL。

模型也不能直接创建并执行飞行任务。模型给出的是建议或工具调用请求,后端还要检查权限、设备、空域和航线约束。只有用户确认、业务校验通过后,任务服务才创建正式任务,再通过异步消息驱动后续执行。这样才能把 AI 的灵活性和真实设备执行的安全边界分开。