实训8:AI模型调用性能优化
本次任务的目标是:优化平台中AI相关功能的调用性能与稳定性。AI接口和普通接口不太一样--它要等外部大模型生成内容,耗时长,还容易受网络,输出格式等因素影响。本文记录我针对AI调用做的几项优化。
一.问题分析
AI调用主要有这么几个问题:
1.每次调用都重新创建模型客户端,有额外开销
2.文档向量化这类任务耗时长,如果同步处理,会让用户一直等待,甚至请求超时。
3.长文本回答如果等模型全部生成完再返回,用户会觉得页面一直没有反应。
4.模型输出的JSON格式不稳定,解析失败会让功能直接报错
二.优化措施
1.复用模型客户端(缓存)
不同功能都要调大模型,如果每次都新建客户端,开销很大。我用一个Registry统一管理,按Provider缓存ChatClient和Embedding模型,第一次创建后,后续直接从缓存取。
private final Map<String, ChatClient> clientCache = new ConcurrentHashMap<>();
// 缓存里有就直接拿,没有才创建
public ChatClient getChatClient(String providerId) {
return clientCache.computeIfAbsent(providerId, this::createChatClient);
}
2.向量化异步化
文档向量化要分块,再逐批调用embedding接口,比较慢。如果放在上传请求里同步做,用户要一直等。所以我把它做成异步:上传时只保存文件和元数据,立即返回;向量化任务投递到Redis Stream,由后台消费者处理,并通过状态机(PENDING->PROCESSING->COMPLETED)反馈进度,失败自动重试。
// 上传后只投递任务,请求立即返回;向量化在后台进行
vectorizeStreamProducer.sendVectorizeTask(kbId, content);
3.流式输出+虚拟线程
知识库问答如果等模型把答案全部生成完再返回,前端会长时间没反应。所以采用流式输出(SSE),模型边生成边返回,前端一个字一个字地显示。
AI调用和SSE都属于I/O等待型任务,我开启了Java21的虚拟线程来支撑这类长连接的高并发·。
// 流式返回(stream 而不是 call)
Flux<String> responseFlux = chatClient.prompt().user(userPrompt).stream().content();
配置:spring.threads.virtuals.enabled:true
4.结构化输出重试与本地修复
有些功能需要模型返回JSON给程序解析,但模型偶尔输出不规范,导致解析失败。我做了两层保障:解析失败时,把错误原因回灌进提示词,让模型重新生成;对于未转义引号这类小错,现在本地修复再解析,能少调用一次模型
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
String content = chatClient.prompt().system(sys).user(user).call().content();
return convertWithRepair(content, outputConverter); // 解析;失败先本地修复再解析
} catch (Exception e) {
lastError = e; // 重试时带上错误原因,引导模型改正
}
}
三.总结
| 优化点z | 做法 | 效果 |
| 客户端复用 | 按Provider缓存ChatClient/Embedding | 减少重复创建开销 |
| 向量化异步 | Redis Stream后台处理+状态机+重试 | 上传请求即时返回 |
| 流式输出 | SSE+Java21虚拟线程 | 回答及时可见,支撑高并发 |
| 结构化输出 | 失败重试+本地修复JSON | 减少解析失败与重复调用 |
这几项优化都是围绕AI调用展开,目标是降低用户等待,提高调用的稳定性。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)