从Spring Boot后端到Vue3前端,再融合FastAPI智能体服务,打造一套生产级的AI驱动开发辅助系统。本文深入剖析异构技术栈的集成策略、异步任务编排、流式响应与容器化部署全链路。
在AI原生应用时代,单一技术栈已无法兼顾企业级稳定性与AI生态敏捷性。Java生态拥有成熟的Spring Cloud、高并发线程模型和丰富的中间件,而Python则是AI/LLM工具链(PyTorch、Transformers、LangChain)的第一语言。
我们的目标:构建一个智能代码生成助手,用户通过Web界面描述需求(如“生成一个Spring Boot CRUD接口”),后端Java服务负责权限、日志、任务调度,再将复杂推理任务异步派发给Python智能体服务,最终返回可运行的代码片段。
架构全景图:
Vue3 + Vite 前端
↓
Spring Boot Gateway (路由+鉴权)
↓
┌───┴───┐
↓ ↓
Java业务微服务 Python智能体集群
(用户/项目/模板) (FastAPI + LangChain)
↓ ↓
Redis缓存 PostgreSQL + Milvus向量库
↓ ↓
Docker Compose / K8s使用Spring Initializr生成,关键依赖:
<dependencies>
<!-- WebFlux响应式,支持流式SSE -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign 用于调用Python服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Reactor Core 异步编排 -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<!-- Jackson 处理JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>定义AgentClient接口,指向Python服务的FastAPI端点:
@FeignClient(name = "python-agent", url = "${agent.service.url:http://localhost:8000}")
public interface AgentClient {
@PostMapping("/api/v1/generate")
Mono<GenerationResponse> generateCode(@RequestBody GenerationRequest request);
// 流式接口 - 返回Flux<CodeChunk>
@PostMapping(value = "/api/v1/generate/stream",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
Flux<String> streamGenerate(@RequestBody GenerationRequest request);
}复杂任务需要组合多个步骤:参数校验→缓存查询→调用Python→结果解析→持久化。使用Reactor的Mono链式编排:
@Service
@Slf4j
public class CodeGenerationService {
private final AgentClient agentClient;
private final RedisTemplate<String, String> redisTemplate;
private final ObjectMapper objectMapper;
public Mono<GenerationResponse> generateWithFallback(GenerationRequest request) {
String cacheKey = "gen:" + request.getRequirementHash();
return redisTemplate.opsForValue()
.get(cacheKey)
.flatMap(cached -> {
try {
return Mono.just(objectMapper.readValue(cached, GenerationResponse.class));
} catch (Exception e) {
return Mono.empty();
}
})
.switchIfEmpty(Mono.defer(() ->
agentClient.generateCode(request)
.timeout(Duration.ofSeconds(30))
.doOnNext(resp -> {
// 异步缓存,设置TTL=1小时
redisTemplate.opsForValue()
.set(cacheKey, toJson(resp), Duration.ofHours(1));
})
.onErrorResume(throwable -> {
log.error("Agent调用失败,降级返回模板", throwable);
return Mono.just(fallbackResponse(request));
})
))
.doOnSuccess(resp -> log.info("生成完成,耗时{}ms", resp.getDuration()));
}
}前端需要实时接收生成的代码块,采用Server-Sent Events (SSE):
@RestController
@RequestMapping("/api/code")
@Slf4j
public class CodeController {
@Autowired
private CodeGenerationService generationService;
@PostMapping(value = "/generate/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamGenerate(@RequestBody GenerationRequest request) {
return generationService.generateStream(request)
.map(chunk -> ServerSentEvent.<String>builder()
.data(chunk)
.event("code-chunk")
.build())
.concatWith(Flux.just(
ServerSentEvent.<String>builder()
.event("done")
.data("[DONE]")
.build()
))
.doOnCancel(() -> log.warn("客户端中断流"));
}
}from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from typing import Optional, AsyncGenerator
import json
import asyncio
from langchain.prompts import ChatPromptTemplate
from langchain_community.llms import Ollama
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
app = FastAPI(title="CodeGen Agent", version="1.0")
# 加载模型(支持流式)
llm = Ollama(
model="qwen2.5:7b",
base_url="http://localhost:11434",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
temperature=0.3,
num_predict=2048
)
class GenerationRequest(BaseModel):
requirement: str = Field(..., min_length=5, max_length=2000)
language: str = "java"
framework: Optional[str] = "spring-boot"
include_explanation: bool = True
class GenerationResponse(BaseModel):
code: str
explanation: str
language: str
duration_ms: float
# ---------- 非流式生成 ----------
@app.post("/api/v1/generate", response_model=GenerationResponse)
async def generate_code(request: GenerationRequest):
import time
start = time.time()
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一名资深全栈工程师,只输出干净的代码和简要注释。"),
("human", "请用{language}和{framework}实现以下需求:\n{requirement}\n"
"要求:代码完整,包含异常处理,若需要解释,用//或#注释。")
])
chain = prompt_template | llm
try:
response = await chain.ainvoke({
"language": request.language,
"framework": request.framework,
"requirement": request.requirement
})
# 简单解析代码和解释(实际可用正则分离)
code = extract_code(response)
explanation = extract_explanation(response) if request.include_explanation else ""
return GenerationResponse(
code=code,
explanation=explanation,
language=request.language,
duration_ms=(time.time() - start) * 1000
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ---------- 流式生成(SSE) ----------
@app.post("/api/v1/generate/stream")
async def generate_code_stream(request: GenerationRequest):
async def event_generator() -> AsyncGenerator[str, None]:
prompt = f"请用{request.language}和{request.framework}实现:{request.requirement}"
# 使用LangChain的流式回调
chunks = []
async for chunk in llm.astream(prompt):
# 每个chunk发送为SSE格式
yield f"data: {json.dumps({'chunk': chunk, 'done': False})}\n\n"
# 结束标记
yield f"data: {json.dumps({'chunk': '', 'done': True})}\n\n"
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
}
)
# 辅助函数
def extract_code(text: str) -> str:
import re
# 匹配```code```块
match = re.search(r"```(\w+)?\n(.*?)\n```", text, re.DOTALL)
return match.group(2) if match else text智能体不止是LLM,我们集成代码向量检索,当用户要求“生成Spring Security配置”时,先检索项目已有的最佳实践代码库(Milvus),再注入Prompt作为Few-shot示例。
from langchain.vectorstores import Milvus
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vector_store = Milvus(
embedding_function=embeddings,
collection_name="code_embeddings",
connection_args={"host": "milvus", "port": "19530"}
)
retriever = vector_store.as_retriever(search_kwargs={"k": 2})
# 在生成端点中增强
async def generate_with_rag(request):
docs = retriever.invoke(request.requirement)
context = "\n".join([doc.page_content for doc in docs])
enhanced_prompt = f"参考以下类似代码:\n{context}\n\n用户需求:{request.requirement}"
# 继续调用llm...使用EventSource或fetch + ReadableStream。我们采用后者以支持POST请求:
// api/codeApi.ts
export async function streamGenerate(
requirement: string,
onChunk: (chunk: string) => void,
onDone: () => void
): Promise<void> {
const response = await fetch('/api/code/generate/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ requirement, language: 'java' })
});
const reader = response.body?.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
while (true) {
const { done, value } = await reader!.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 解析SSE格式
const lines = buffer.split('\n\n');
for (let i = 0; i < lines.length - 1; i++) {
const event = lines[i];
if (event.startsWith('data: ')) {
const data = JSON.parse(event.slice(6));
if (data.done) {
onDone();
} else {
onChunk(data.chunk);
}
}
}
buffer = lines[lines.length - 1];
}
}
// 组件中使用
const codeContent = ref('');
streamGenerate('生成一个用户登录接口', (chunk) => {
codeContent.value += chunk;
}, () => {
console.log('生成完毕');
});uvicorn workers,配合asyncio。@Retryable(value = {Exception.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public Mono<GenerationResponse> generateWithRetry(...) { ... }当Python服务不可用或超时,返回预置的模板代码(如通用CRUD模板),确保前端有响应。
使用Sentinel或Spring Cloud Circuit Breaker,对智能体端点设置QPS=50,避免打爆Ollama显存。
docker-compose.yml(精简版):
version: '3.8'
services:
java-gateway:
build: ./backend
ports:
- "8080:8080"
environment:
- AGENT_SERVICE_URL=http://python-agent:8000
- REDIS_HOST=redis
depends_on:
- redis
- python-agent
python-agent:
build: ./agent
ports:
- "8000:8000"
environment:
- OLLAMA_HOST=ollama:11434
- MILVUS_HOST=milvus
volumes:
- ./models:/root/.ollama
deploy:
resources:
reservations:
devices:
- capabilities: [gpu] # 如果有GPU
redis:
image: redis:7-alpine
ports:
- "6379:6379"
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama-data:/root/.ollama
command: serve
volumes:
ollama-data:Java Dockerfile 采用分层构建:
FROM eclipse-temurin:21-jre-alpine
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]Python Dockerfile 使用uvicorn多worker:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]/actuator/prometheus,监控Java服务调用Python的耗时、成功率、缓存命中率。@Timed(value = "agent.call.duration", percentiles = {0.5, 0.95, 0.99})
public Mono<GenerationResponse> callAgent(...) { ... }本文完整展示了一套Java Spring Boot + Vue3 + Python FastAPI的全栈智能体系统,涵盖了:
这套架构已在某企业内部用于自动化代码脚手架生成,将新模块开发时间从2天缩短至30分钟。未来可演进方向:
技术没有银弹,但双栈融合让我们既享受Java的工业级严谨,又拥抱Python的AI敏捷。希望这篇实践对您构建智能开发工具有所启发。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。