配置对话模型并处理流式输出
This content is not available in your language yet.
如果说 完成第一次 Feat AI 调用 解决的是“模型有没有真的返回内容”,那么这一页要解决的是下一层问题:
- 我该怎么配模型
- 什么时候用
chat(...) - 什么时候用
chatStream(...) - 调整
system、temperature、noThink这类参数到底意味着什么
先从最小配置继续往前走
Section titled “先从最小配置继续往前走”一个最常见的下一步写法,是在模型上显式补系统提示词和输出行为:
import tech.smartboot.feat.ai.FeatAI;import tech.smartboot.feat.ai.chat.ChatModel;import tech.smartboot.feat.ai.chat.ChatModelVendor;
public class OllamaDemo { public static void main(String[] args) { ChatModel chatModel = FeatAI.chatModel(opts -> { opts.model(ChatModelVendor.Ollama.Qwen3_06B) .system("你是一个擅长生成藏头诗的诗人。") .noThink(true) .debug(false); });
chatModel.chat("写一句和 Feat 有关的短句", rsp -> { System.out.println(rsp.getContent()); }); }}这段代码里最值得关心的是三项配置:
system(...):定义角色或输出风格noThink(true):在支持的模型上关闭“思考过程”输出debug(...):本地排查请求时打开,平时不必默认开启
chat(...) 和 chatStream(...) 到底怎么选
Section titled “chat(...) 和 chatStream(...) 到底怎么选”这两个方法不是谁更高级,而是两种不同的交付方式。
用 chat(...),如果你要的是完整结果
Section titled “用 chat(...),如果你要的是完整结果”chatModel.chat("你好,请自我介绍一下。", rsp -> { System.out.println("content: " + rsp.getContent()); System.out.println("usage: " + rsp.getUsage());});适合:
- 后端任务处理
- 一次性生成结果
- 你需要在最后统一读取
usage
用 chatStream(...),如果你要边生成边消费
Section titled “用 chatStream(...),如果你要边生成边消费”chatModel.chatStream("根据以下关键词生成一首藏头诗:情,人,节,快,乐", content -> { System.out.print(content);});适合:
- 终端逐步输出
- 前端打字机效果
- 较长内容的实时展示
如果你需要在流式结束后再做收尾动作,可以用完整回调版本:
chatModel.chatStream("介绍一下 Feat", new StreamResponseCallback() { @Override public void onStreamResponse(String content) { System.out.print(content); }
@Override public void onCompletion(ResponseMessage responseMessage) { System.out.println("\n完成,总令牌数:" + responseMessage.getUsage().getTotalTokens()); }});多轮对话怎么理解
Section titled “多轮对话怎么理解”多轮对话不是一个额外模式,本质上只是“你把历史消息也一起发给模型”。
最简单的做法是维护一组消息:
import tech.smartboot.feat.ai.chat.entity.Message;import tech.smartboot.feat.ai.chat.entity.MessageRole;
List<Message> messages = new ArrayList<>();messages.add(new Message(MessageRole.SYSTEM, "你是一个专业的 Java 工程师"));messages.add(new Message(MessageRole.USER, "什么是 Feat?"));
chatModel.chat(messages, rsp -> { System.out.println(rsp.getContent());});如果你准备做真正的聊天产品,这一步很重要。
如果你当前只是做单次问答,先别急着把对话状态管理复杂化。
这些配置项什么时候才值得改
Section titled “这些配置项什么时候才值得改”system(...)
Section titled “system(...)”最值得最早用。
它往往比你反复修改用户提示词更有效,因为它定义的是整体角色和输出边界。
temperature(...)
Section titled “temperature(...)”当你明确要控制“稳定性 vs 创造性”时再动它。
- 较低:回答更收敛
- 较高:回答更发散
如果你还不知道自己想要什么风格,先保持默认值通常更稳。
maxTokens(...)
Section titled “maxTokens(...)”当你需要控制回复长度时再用。
比如做固定格式输出、成本控制或者避免模型生成过长内容。
noThink(...)
Section titled “noThink(...)”只有当你已经确认某些模型会输出不希望暴露的“思考过程”时,再显式使用它。
一个更接近真实使用的例子
Section titled “一个更接近真实使用的例子”仓库里的 ChatDemo 展示了一个很典型的多轮问题:
import tech.smartboot.feat.ai.FeatAI;import tech.smartboot.feat.ai.chat.ChatModel;import tech.smartboot.feat.ai.chat.ChatModelVendor;
public class ChatDemo { public static void main(String[] args) { ChatModel chatModel = FeatAI.chatModel(opts -> opts.model(ChatModelVendor.GiteeAI.Qwen2_5_72B_Instruct) );
chatModel.chat("你好,请自我介绍一下。", rsp -> { System.out.println("rsp: " + rsp.getContent()); System.out.println("usage: " + rsp.getUsage()); chatModel.chat("我对你说的上一句话是什么?", rsp2 -> { System.out.println("rsp2: " + rsp2.getContent()); }); }); }}这个例子至少能帮助你理解两件事:
chat(...)可以直接拿到响应对象,不只是拿到文本- 你完全可以在一个回调里继续发下一轮请求
第一次调用能成功,为什么一加配置就不对了
Section titled “第一次调用能成功,为什么一加配置就不对了”优先检查:
- 模型本身是否支持你设置的能力
system(...)是否把角色限制得过死- 你是不是同时改了太多参数,导致很难判断是哪一项生效
chatStream(...) 没有等到完整句子就一直打印碎片
Section titled “chatStream(...) 没有等到完整句子就一直打印碎片”这是正常的。
流式输出本来就是按片段到达,不保证是完整语义单元。
noThink(true) 为什么似乎没效果
Section titled “noThink(true) 为什么似乎没效果”因为这取决于模型和厂商支持情况。
它不是所有后端都一定有效的通用开关。
- 如果你准备开始让模型调用工具:去 使用 Agent
- 如果你准备做检索增强:去 Embedding 与向量
- 如果你还没完成最小调用:先回到 完成第一次 Feat AI 调用