第2341篇:Java AI项目的脚手架设计——让新AI项目5分钟内启动
2026/4/30大约 5 分钟
第2341篇:Java AI项目的脚手架设计——让新AI项目5分钟内启动
适读人群:需要频繁创建新AI项目的团队技术负责人,关注AI工程标准化的架构师 | 阅读时长:约16分钟 | 核心价值:设计企业级AI项目脚手架,消除重复劳动,统一工程标准
每次开一个新的AI项目,我都要花半天时间做同样的事:配置Spring AI依赖、写日志拦截Advisor、配置健康检查、写错误处理、配置多环境……
这些工作没有技术含量,但不做又会出问题。做了一次,下次继续重复,第三次、第四次……直到某一天你意识到:这些东西应该沉淀成脚手架。
什么是好的AI项目脚手架
不是简单的代码模板(一堆要复制粘贴的文件),而是:
- 可以用命令行一键生成项目(类似Spring Initializr)
- 包含所有工程最佳实践(日志、监控、错误处理、安全)
- 零配置即可运行Demo(clone下来mvn run就能看到效果)
- 预留扩展点(不是死板的,可以按需裁剪)
脚手架项目结构
ai-project-template/
├── src/
│ ├── main/
│ │ ├── java/com/example/ai/
│ │ │ ├── AiApplication.java # 启动类
│ │ │ ├── config/
│ │ │ │ ├── AiClientConfig.java # ChatClient配置(含Advisor链)
│ │ │ │ ├── VectorStoreConfig.java # 向量存储配置
│ │ │ │ └── SecurityConfig.java # 接口安全配置
│ │ │ ├── advisor/
│ │ │ │ ├── LoggingAdvisor.java # 日志追踪
│ │ │ │ ├── CostTrackingAdvisor.java # Token成本追踪
│ │ │ │ └── SafetyGuardAdvisor.java # 内容安全检查
│ │ │ ├── controller/
│ │ │ │ ├── ChatController.java # 对话接口(含流式)
│ │ │ │ └── HealthController.java # 健康检查接口
│ │ │ ├── service/
│ │ │ │ ├── ChatService.java # 对话服务
│ │ │ │ └── RagService.java # RAG服务(可选)
│ │ │ ├── exception/
│ │ │ │ ├── AiException.java # 异常基类
│ │ │ │ └── GlobalExceptionHandler.java # 全局异常处理
│ │ │ └── health/
│ │ │ ├── LlmHealthIndicator.java # LLM健康检查
│ │ │ └── VectorStoreHealthIndicator.java
│ │ └── resources/
│ │ ├── application.yml # 基础配置
│ │ ├── application-dev.yml # 开发环境
│ │ ├── application-prod.yml # 生产环境
│ │ ├── logback-spring.xml # 日志配置(JSON格式)
│ │ └── prompts/ # Prompt模板目录
│ │ ├── system-default.txt
│ │ └── rag-answer.txt
│ └── test/
│ ├── java/com/example/ai/
│ │ ├── ChatServiceTest.java # 单元测试
│ │ └── ChatControllerIntegrationTest.java # 集成测试
│ └── resources/
│ └── application-test.yml # 测试配置(Mock LLM)
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml # 包含PostgreSQL+PGVector
├── scripts/
│ └── start.sh # 快速启动脚本
└── pom.xml核心配置文件
<!-- pom.xml:预配置所有需要的依赖 -->
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Spring AI:OpenAI(默认,可替换) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- Spring AI:向量存储(PGVector,可替换) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>
<!-- 工具库 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<!-- 开发工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project># application.yml:开箱即用的配置
spring:
threads:
virtual:
enabled: true # 启用虚拟线程
ai:
openai:
api-key: ${OPENAI_API_KEY:sk-placeholder}
base-url: ${AI_BASE_URL:https://api.openai.com}
chat:
options:
model: ${AI_MODEL:gpt-4o-mini}
temperature: ${AI_TEMPERATURE:0.7}
max-tokens: ${AI_MAX_TOKENS:2048}
vectorstore:
pgvector:
initialize-schema: ${VECTOR_STORE_INIT:true}
dimensions: 1536
datasource:
url: ${DB_URL:jdbc:postgresql://localhost:5432/ai_service}
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:postgres}
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
health:
readinessState:
enabled: true
livenessState:
enabled: true
logging:
level:
com.example.ai: DEBUG
org.springframework.ai: INFO
# 自定义AI配置
ai-service:
max-concurrent-calls: ${MAX_CONCURRENT:20}
user-max-concurrent: ${USER_MAX_CONCURRENT:3}
rate-limit-per-second: ${RATE_LIMIT:10}
cache-ttl-minutes: ${CACHE_TTL:60}关键的ChatClient配置类
@Configuration
@RequiredArgsConstructor
@Slf4j
public class AiClientConfig {
@Value("${ai-service.max-concurrent-calls:20}")
private int maxConcurrentCalls;
@Bean
public Semaphore llmConcurrencySemaphore() {
return new Semaphore(maxConcurrentCalls, true);
}
@Bean
public LoggingAdvisor loggingAdvisor() {
return new LoggingAdvisor();
}
@Bean
public SafetyGuardAdvisor safetyGuardAdvisor() {
return new SafetyGuardAdvisor();
}
/**
* 默认的ChatClient(包含所有推荐的Advisor)
* 项目中所有Service都通过Builder构建自己的ChatClient
*/
@Bean
@ConditionalOnMissingBean(name = "configuredChatClientBuilder")
public ChatClient.Builder configuredChatClientBuilder(
ChatClient.Builder builder,
LoggingAdvisor loggingAdvisor,
SafetyGuardAdvisor safetyGuardAdvisor) {
return builder.defaultAdvisors(
safetyGuardAdvisor, // 安全优先
loggingAdvisor // 日志记录
);
}
}docker-compose.yml:一键启动本地开发环境
# docker/docker-compose.yml
version: '3.8'
services:
postgres:
image: pgvector/pgvector:pg16
environment:
POSTGRES_DB: ai_service
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
volumes:
postgres_data:#!/bin/bash
# scripts/start.sh:5分钟内启动完整开发环境
echo "启动本地开发环境..."
# 检查Java版本
java_version=$(java -version 2>&1 | head -1 | awk -F '"' '{print $2}' | cut -d'.' -f1)
if [ "$java_version" -lt "21" ]; then
echo "错误:需要Java 21+,当前版本:$java_version"
exit 1
fi
# 启动依赖服务
docker-compose -f docker/docker-compose.yml up -d
# 等待PostgreSQL就绪
echo "等待数据库就绪..."
until docker-compose -f docker/docker-compose.yml exec -T postgres pg_isready -U postgres; do
sleep 1
done
echo "数据库已就绪!"
# 检查环境变量
if [ -z "$OPENAI_API_KEY" ]; then
echo "警告:未设置OPENAI_API_KEY,将使用Mock模式"
export SPRING_PROFILES_ACTIVE=dev,mock
else
export SPRING_PROFILES_ACTIVE=dev
fi
# 启动应用
echo "启动AI服务..."
./mvnw spring-boot:run -Dspring-boot.run.profiles=$SPRING_PROFILES_ACTIVE
echo "服务已启动:http://localhost:8080"
echo "健康检查:http://localhost:8080/actuator/health"
echo "API文档:http://localhost:8080/swagger-ui.html"快速验证:项目是否正常运行
启动后,运行这个验证脚本:
#!/bin/bash
# scripts/verify.sh
BASE_URL="http://localhost:8080"
echo "1. 检查健康状态..."
health=$(curl -s "$BASE_URL/actuator/health" | jq -r '.status')
if [ "$health" = "UP" ]; then
echo " ✓ 服务健康"
else
echo " ✗ 服务不健康:$health"
fi
echo "2. 测试基本对话..."
response=$(curl -s -X POST "$BASE_URL/api/v1/chat" \
-H "Content-Type: application/json" \
-d '{"message": "你好,测试一下"}')
if echo "$response" | jq -e '.reply' > /dev/null 2>&1; then
echo " ✓ 对话功能正常"
else
echo " ✗ 对话功能异常:$response"
fi
echo "3. 测试流式输出..."
curl -s -N "$BASE_URL/api/v1/chat/stream?message=测试" \
--max-time 10 | head -5
echo " ✓ 流式输出正常"
echo "验证完成!"好的脚手架能让一个新加入团队的工程师,在一天内就能理解整个AI项目的结构,并贡献第一行有价值的代码。
