AI 能力
后端基于 Spring AI 接入 OpenAI 兼容协议的大模型,为代码生成、动态表单、审批流提供 AI 辅助。默认关闭,未配置密钥不影响应用启动。
功能一览
| 功能 | 接口 | 说明 |
|---|---|---|
| 代码生成配置推断 | POST /api/v1/codegen/{tableName}/ai-config | 读取表结构,回填业务名与字段的中文描述、表单/查询类型、显示位置,未推断出的字段沿用默认值 |
| 表单规则生成 | POST /api/v1/forms/ai-generate | 需求描述转 form-create 规则数组,产出经结构校验后才返回 |
| 审批摘要 | POST /api/v1/workflow/tasks/{taskId}/ai-summary | 汇总表单数据与审批记录生成摘要,需 workflow.enabled=true |
AI 未开启时调用返回业务异常「AI 功能未开启,请将 spring.ai.model.chat 置为 openai 并配置 spring.ai.openai.api-key」,不影响应用启动与其余接口。
开启方式
spring.ai.model.chat 在 application-{profile}.yml 中默认为 none,改为 openai 并补齐模型配置:
spring:
ai:
model:
chat: openai
openai:
base-url: https://api.deepseek.com # OpenAI 兼容服务地址,以服务商文档为准
api-key: ${AI_API_KEY:}
chat:
options:
model: deepseek-chatapplication.yml 已将 embedding、image、moderation、audio 置为 none,仅对话模型参与自动装配,因此不配密钥也能正常启动。
核心组件
| 组件 | 位置 | 职责 |
|---|---|---|
AiChatService | support/ai/AiChatService.java | 模型调用统一入口:chat 返回纯文本,chatEntity 映射为实体,chatJsonList 映射为 JSON 数组 |
PromptLoader | support/ai/PromptLoader.java | 启动时载入 resources/prompts 下的提示词并缓存 |
chatEntity 由框架按实体结构生成 JSON Schema 并校验,结构不符时自动重试,无需手工解析 JSON;chatJsonList 的元素结构开放,产出后需业务侧校验。
提示词配置
提示词一个功能一个文件,放在 src/main/resources/prompts 下:
prompts/
├── codegen/system.md # 表结构 → 生成配置
├── form/system.md # 需求 → form-create 规则
└── workflow/summary.md # 审批摘要PromptLoader 启动时全部载入,文件缺失或读取失败直接启动失败。文本中的占位符由 render 替换为变量值,替换后仍残留占位符则抛异常,避免残缺提示词发给模型:
// 提示词中的 {{formTypes}} 在此被替换为实际枚举清单
String systemPrompt = promptLoader.render("codegen/system.md",
Map.of("formTypes", "INPUT(输入框)|SELECT(下拉框)"));不含变量的提示词直接取原文:promptLoader.get("form/system.md")。
codegen 提示词中的表单类型与查询类型清单由 FormTypeEnum、QueryTypeEnum 运行时拼装注入,新增枚举值无需改动提示词。
新增 AI 模块
1. 编写提示词
新建 src/main/resources/prompts/order/summary.md:
你是订单助手,根据订单数据生成一句话摘要。
要求:
- 50 字以内
- 包含订单号、金额、状态2. 编写 Service
@Service
@RequiredArgsConstructor
public class OrderAiServiceImpl implements OrderAiService {
/** 系统提示词名称,对应 resources/prompts 下的相对路径 */
private static final String SYSTEM_PROMPT_PATH = "order/summary.md";
private final AiChatService aiChatService;
private final PromptLoader promptLoader;
@Override
public String summarize(String orderNo) {
return aiChatService.chat(promptLoader.get(SYSTEM_PROMPT_PATH), "订单号:" + orderNo);
}
}3. 编写 Controller
@Tag(name = "订单")
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderAiController {
private final OrderAiService orderAiService;
@Operation(summary = "AI 生成订单摘要")
@PostMapping("/{orderNo}/ai-summary")
public Result<String> summarize(@PathVariable String orderNo) {
return Result.success(orderAiService.summarize(orderNo));
}
}需要结构化结果时改用 chatEntity,传入返回的实体类型:
return aiChatService.chatEntity(systemPrompt, userPrompt, OrderSummaryVO.class);注意事项
- 提示词随代码版本发布,修改后需重新构建部署,不支持热更新
- 模型输出不可靠,涉及落库或执行的结果需业务侧校验,参考
FormRuleValidator对表单规则的处理
