
如果你尝试过在 Java 应用中集成大语言模型LLM大概率写过不少样板代码HTTP 客户端、JSON 解析、流式处理、会话管理……Solon 4.0 的ChatModel用一套简洁的 Builder API 把这些都封装好了。本文将通过真实的代码示例带你一步步用ChatModel构建 AI 功能——从简单的单次调用到带记忆的流式聊天机器人。1. 什么是 ChatModelChatModel是 Solon AI 生态中的统一 LLM 客户端。你不再需要为不同的模型提供商写不同的 HTTP 调用而是通过一套统一的 API 完成同步调用— 一次请求完整返回流式调用— 基于 Project Reactor 的响应式流FluxChatResponse工具/函数调用— 让 LLM 调用你的 Java 方法聊天会话— 自动维护对话记忆多模态消息— 文本、图片、音频方言适配— 支持 OpenAI、Ollama、Anthropic、Gemini、DashScope 等多种服务商最核心的是它使用了方言模式Dialect Pattern——你只需要指向任意兼容的 LLM 端点它会自动适配协议。2. 环境配置在pom.xml中添加依赖Solon 不需要父 POM独立工作dependency groupIdorg.noear/groupId artifactIdsolon-ai/artifactId version${solon.version}/version /dependency这会引入所有内置的方言适配器OpenAI、Ollama、Gemini、Anthropic、DashScope。3. 配置方式3.1 通过 YAML 配置推荐solon.ai.chat: demo: apiUrl: http://127.0.0.1:11434/api/chat # 完整 URL非 baseUrl standard: ollama # 接口规范方言标识 model: llama3.2 # 模型名称 headers: x-demo: demo1然后通过Bean注入一个可以直接使用的ChatModelimport org.noear.solon.ai.chat.ChatConfig; import org.noear.solon.ai.chat.ChatModel; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; import org.noear.solon.annotation.Inject; Configuration public class AiConfig { Bean public ChatModel chatModel(Inject(${solon.ai.chat.demo}) ChatModel model) { return model; } }3.2 编程式 BuilderBean public ChatModel chatModel() { return ChatModel.of(http://127.0.0.1:11434/api/chat) .standard(ollama) // 或 .provider(ollama) .model(llama3.2) .timeout(Duration.ofSeconds(60)) .build(); }3.3 支持的模型提供商standard或provider字段选择方言方言标识apiUrl 示例模型openai默认https://api.openai.com/v1/chat/completionsGPT、DeepSeek、Qwen、GLM、Kimi 等ollamahttp://127.0.0.1:11434/api/chat本地 Ollama 模型anthropichttps://api.anthropic.com/v1/messagesClaudegeminihttps://generativelanguage.googleapis.com/...Geminidashscope阿里云 DashScope 端点QwenDashScope 原生4. 同步调用最简单的方式最基本的用法——发送提示词获取完整响应import org.noear.solon.ai.chat.ChatModel; import org.noear.solon.ai.chat.ChatResponse; import org.noear.solon.annotation.Inject; import org.noear.solon.annotation.Component; Component public class ChatService { Inject ChatModel chatModel; public String ask(String question) throws IOException { ChatResponse resp chatModel.prompt(question).call(); return resp.getMessage().getContent(); } }仅三行业务代码搞定。5. 流式调用实时响应对于聊天机器人和助手类应用流式响应是刚需。ChatModel返回 Reactor 的FluxChatResponseimport reactor.core.publisher.Flux; public FluxString askStream(String question) throws IOException { return chatModel.prompt(question) .stream() .filter(resp - resp.hasContent()) // 跳过空块 .map(resp - resp.getContent()); }如果你使用 Solon Web Reactive可以直接把Flux返回给 SSE 端点import org.noear.solon.web.sse.SseEvent; import org.noear.solon.annotation.Mapping; import reactor.core.publisher.Flux; Mapping(/chat/stream) public FluxSseEvent chatStream(String prompt) throws IOException { return chatModel.prompt(prompt) .stream() .filter(resp - resp.hasContent()) .map(resp - new SseEvent().data(resp.getContent())); }流式协议根据提供商不同使用标准 SSE 或x-ndjson。6. 对话记忆ChatSessionLLM 本身是无状态的每次请求都需要传入历史上下文。ChatSession自动帮你完成这件事。6.1 基本用法import org.noear.solon.ai.chat.ChatSession; import org.noear.solon.ai.chat.session.InMemoryChatSession; ChatSession session InMemoryChatSession.builder() .sessionId(user-123) .maxMessages(10) // 保留最近 10 轮 .build(); // 第一轮 ChatResponse resp1 chatModel.prompt(你好) .session(session) .call(); // 第二轮——模型记得刚才的对话 ChatResponse resp2 chatModel.prompt(我刚才说了什么) .session(session) .call();6.2 Web 应用中的用户级会话在实际的 Web 应用中每个用户需要一个独立的会话import org.noear.solon.annotation.Controller; import org.noear.solon.web.sse.SseEvent; import reactor.core.publisher.Flux; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Controller public class ChatController { Inject ChatModel chatModel; final MapString, ChatSession sessionMap new ConcurrentHashMap(); Mapping(/chat) public FluxSseEvent chat(String sessionId, String prompt) throws IOException { ChatSession session sessionMap.computeIfAbsent(sessionId, k - InMemoryChatSession.builder().sessionId(k).build()); return chatModel.prompt(prompt) .session(session) .options(o - o.systemPrompt(你是一个友好、乐于助人的助手。)) .stream() .filter(ChatResponse::hasContent) .map(resp - new SseEvent().data(resp.getContent())); } }6.3 内置会话实现实现类存储方式适用场景InMemoryChatSession本地 Map开发、单节点FileChatSession文件系统CLI 工具、桌面应用RedisChatSessionRedis生产环境、分布式部署