通义千问 Chat 模型高级用法:Function Calling / Structured Output / 多轮对话
本文深入讲解通义千问 Chat 模型的核心高级能力:Function Calling、Structured Output、多轮对话管理,以及流式处理、限流重试、生产级最佳实践和常见踩坑指南。
一、通义千问参数详解
1.1 完整参数列表
| 参数 | 类型 | 范围 | 默认值 | 说明 |
|---|---|---|---|---|
model |
String | - | qwen-plus | 模型名称:qwen-turbo / qwen-plus / qwen-max / qwen-long / qwen-math-plus / qwen-coder-plus |
temperature |
Double | 0.0-2.0 | 0.7 | 越高越随机,0.0 返回确定输出 |
top-p |
Double | 0.0-1.0 | 0.8 | nucleus 采样,1.0 全选 |
top-k |
Int | 0-100 | - | top-k 采样,0 关闭 |
seed |
Int | - | - | 复现性种子,同 seed 输出一致 |
max-tokens |
Int | - | 1500 | 输出最大 Token 数 |
presence-penalty |
Double | -2.0-2.0 | 0 | 话题新颖度,正值鼓励新话题 |
frequency-penalty |
Double | -2.0-2.0 | 0 | 字频惩罚,减少重复 |
stop |
List | - | - | 停止字符串列表 |
enable-search |
Boolean | - | false | 开启联网搜索 |
incremental-output |
Boolean | - | true | 流式增量输出 |
tools |
List | - | - | 工具定义(JSON Schema) |
tool-choice |
String | - | auto | auto / none / {name} |
parallel-tool-calls |
Boolean | - | true | 是否并行调用多工具 |
result-format |
String | - | text | text / json_object / json_schema |
response-format |
Object | - | - | JSON Schema(result-format=json_schema 时有效) |
1.2 参数调优指南
核心原则:确定性任务用低温,创意任务用高温。
java
// 场景1:代码生成(低温,确定性优先)
DashScopeChatOptions.builder()
.withModel("qwen-coder-plus")
.withTemperature(0.1) // 极低温度,输出稳定
.withTopP(0.1) // 候选集极小,聚焦最可能答案
.withMaxTokens(2000)
.withSeed(42) // 可复现
.build();
// 场景2:创意写作(高温,探索多样)
DashScopeChatOptions.builder()
.withModel("qwen-plus")
.withTemperature(0.9) // 高温度,创意丰富
.withTopP(0.9)
.withMaxTokens(4000)
.withPresencePenalty(0.5) // 鼓励新话题
.build();
// 场景3:客服问答(启用搜索,精准事实)
DashScopeChatOptions.builder()
.withModel("qwen-plus")
.withTemperature(0.3) // 中等偏低,减少幻觉
.withEnableSearch(true) // 联网事实
.withIncrementalOutput(true)
.build();
参数组合速查:
| 任务类型 | temperature | top_p | top_k | 说明 |
|---|---|---|---|---|
| 代码生成 | 0.0-0.2 | 0.1-0.3 | 10-20 | 确定性优先 |
| 数据分析 | 0.1-0.3 | 0.3-0.5 | 20-50 | 准确优先 |
| 通用对话 | 0.5-0.7 | 0.7-0.9 | 50-80 | 平衡 |
| 创意写作 | 0.8-1.2 | 0.8-1.0 | 80-100 | 多样性优先 |
| 头脑风暴 | 1.0-1.5 | 0.9-1.0 | 100 | 最大随机 |
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
1.3 两层参数配置
Spring AI Alibaba 支持 全局默认 + 单次调用动态覆盖 两层配置:
yaml
# 全局默认配置
spring:
ai:
dashscope:
chat:
options:
model: qwen-plus
temperature: 0.7
max-tokens: 1500
java
// 单次调用覆盖
String response = chatClient.prompt()
.user(userMessage)
.options(DashScopeChatOptions.builder()
.withModel("qwen-max") // 覆盖模型
.withTemperature(0.3) // 覆盖温度
.withMaxTokens(3000) // 覆盖最大 Token
.withEnableSearch(true) // 本次开启搜索
.build())
.call()
.content();
二、Function Calling
2.1 三种函数注册方式
Spring AI Alibaba 支持三种 Tool 注册方式:
| 方式 | 适用场景 | 推荐度 |
|---|---|---|
| @Bean + Function 接口 | 标准注册,与 Spring 容器解耦 | ⭐⭐⭐⭐⭐ |
| @Tool 注解 | 1.1 版推荐的简化写法 | ⭐⭐⭐⭐ |
| ToolCallback 编程式 | 动态注册、复杂场景 | ⭐⭐⭐ |
java
/**
* 方式一:@Bean + Function 接口(最标准)
*/
@Configuration
public class ToolsConfiguration {
@Bean
@Description("获取当前的日期和时间")
public Function<CurrentTimeRequest, CurrentTimeResponse> getCurrentTime() {
return request -> {
String now = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
return new CurrentTimeResponse(now, "Asia/Shanghai");
};
}
@Bean
@Description("查询指定城市的实时天气信息")
public Function<WeatherRequest, WeatherResponse> getWeather() {
return request -> {
// 调用天气 API
return new WeatherResponse(request.city(), "25°C", "晴");
};
}
}
/**
* 方式二:@Tool 注解(简化写法)
*/
@Service
public class TongyiToolService {
@Tool(description = "查询订单状态,返回订单状态、金额、物流信息")
public OrderResult queryOrder(@ToolParam(description = "订单号") String orderId) {
return orderService.query(orderId);
}
@Tool(description = "创建退换货工单")
public RefundResult createRefund(
@ToolParam(description = "订单号") String orderId,
@ToolParam(description = "退换货原因") String reason) {
return refundService.create(orderId, reason);
}
}
/**
* 方式三:ChatClient 中注册
*/
@RestController
public class ChatController {
@PostMapping("/chat")
public String chat(@RequestBody ChatRequest request) {
return chatClient.prompt()
.user(request.message())
.tools("getCurrentTime", "getWeather") // 注册工具
.call()
.content();
}
}
2.2 Tool 参数校验(JSR-303)
使用 JSR-303 注解对工具参数进行校验:
java
import jakarta.validation.constraints.*;
public class WeatherRequest {
@NotBlank(message = "城市名称不能为空")
@Size(min = 2, max = 20, message = "城市名称长度应在2-20之间")
private String city;
@Pattern(regexp = "^[A-Z]{2}$", message = "国家代码必须为2位大写字母")
private String countryCode;
// getters/setters
}
@Configuration
public class ValidatedToolsConfig {
@Bean
@Description("查询指定城市的天气")
public Function<WeatherRequest, WeatherResponse> getWeather() {
return request -> {
// 参数已在进入方法前被 Spring Validation 校验
return weatherService.query(request.getCity());
};
}
}
2.3 工具执行超时控制
java
@Configuration
public class TimeoutToolsConfig {
@Bean
@Description("查询数据库(可能较慢)")
public Function<QueryRequest, QueryResponse> slowDatabaseQuery() {
return request -> {
// 使用 CompletableFuture + 超时
CompletableFuture<QueryResponse> future = CompletableFuture.supplyAsync(() ->
databaseService.complexQuery(request)
);
try {
return future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new RuntimeException("查询超时,请稍后重试");
}
};
}
}
2.4 并行工具调用
Qwen 支持并行调用多个工具(parallel_tool_calls: true 时):
java
@Service
public class ParallelToolService {
private final ChatClient chatClient;
/**
* 用户:"帮我查下订单 MT2025001 状态,同时查下该商品的退换货政策"
* → Qwen 返回两个并行 tool_calls
* → Spring AI 并行执行两个 @Tool 方法
*/
public String chatWithParallelTools(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.functions("queryOrder", "queryRefundPolicy")
.options(DashScopeChatOptions.builder()
.withParallelToolCalls(true) // 允许并行调用
.build())
.call()
.content();
}
}
2.5 ToolChoice 强制调用
java
/**
* 强制模型调用指定工具(不询问直接执行)
*/
public String forceToolCall(String orderId) {
return chatClient.prompt()
.system("请调用指定工具处理用户请求")
.user("处理订单 " + orderId)
.functions("queryOrder")
.options(DashScopeChatOptions.builder()
.withToolChoice("queryOrder") // 强制调用 queryOrder
.build())
.call()
.content();
}
2.6 Tool 描述 Prompt 工程
工具描述的质量直接影响模型选择工具的准确性:
java
// ❌ 差:描述模糊
@Tool(description = "查询数据")
public String queryData(String id) { ... }
// ✅ 好:描述具体,包含触发条件
@Tool(description = "查询订单状态。当用户询问'订单到哪了'、'订单状态'、'物流信息'时调用此工具。")
public OrderResult queryOrder(@ToolParam(description = "订单号,格式如 MT2025001") String orderId) { ... }
// ✅ 最佳:包含参数示例
@Tool(description = "查询天气信息。用户询问'天气'、'温度'、'会不会下雨'时调用。")
public WeatherResponse getWeather(
@ToolParam(description = "城市名称,如'北京'、'上海'") String city,
@ToolParam(description = "日期,格式 yyyy-MM-dd,默认今天") String date
) { ... }
2.7 Function Calling 完整调用链
Java Function ( @Bean / @Tool )
│
▼
Spring AI 转换 tool 定义 (name + description + JSON Schema)
│
▼
TongyiChatModel 发送 messages + tools 参数
│
▼
Qwen 模型 → 返回 tool_calls (function name + args)
│
▼
TongyiChatModel 识别 tool_calls → 调用 Java Method (反射)
│
▼
Java 返回值转 JSON 拼接进 messages
│
▼
模型二次推理 → 最终输出自然语言
三、结构化输出(Structured Output)
3.1 三种结构化模式
| Mode | 实现方式 | 适用场景 |
|---|---|---|
| BeanOutputConverter | 定义 POJO,自动 JSON Schema 推断 | Java 内部直接消费,类型安全 |
| outputType(Class) | ReactAgent 直接传入 Java 类 | Agent 场景,自动生成 Schema |
| outputSchema(String) | 手写 JSON Schema 或 BeanOutputConverter 生成 | 精确字段校验 |
3.2 Bean 方式(ChatClient)
java
@Data
public class ProductReviewAnalysis {
private String productName;
private Double sentimentScore; // 0.0-1.0
private List<String> positive; // 优点
private List<String> negative; // 缺点
private String summary;
private boolean recommend;
}
@Service
public class StructuredOutputService {
private final ChatClient chatClient;
/**
* Bean 方式:Qwen 直接返回符合 Java POJO 结构的 JSON
* 内部自动完成:(1) prompt 附 JSON Schema (2) 模型返回 JSON (3) Jackson 反序列
*/
public ProductReviewAnalysis analyzeReview(String productName, String reviewText) {
var outputConverter = new BeanOutputConverter<>(ProductReviewAnalysis.class);
return chatClient.prompt()
.system("分析用户评论,结构化返回评分、优缺点、总结。"
+ outputConverter.getFormat()) // 自动注入 JSON Schema
.user("商品: %s\n评论: %s".formatted(productName, reviewText))
.call()
.entity(ProductReviewAnalysis.class); // 自动反序列
}
}
3.3 Agent 场景的结构化输出
在 Spring AI Alibaba 的 ReactAgent 中,通过 outputSchema 和 outputType 处理结构化输出:
java
/**
* 方式一:outputType(Class) - 推荐,类型安全
*/
public static class ContactInfo {
private String name;
private String email;
private String phone;
// getters/setters
}
ReactAgent agent = ReactAgent.builder()
.name("contact_extractor")
.model(chatModel)
.outputType(ContactInfo.class) // 传入 Java 类,自动转 JSON Schema
.instruction("从用户消息中提取联系人信息")
.build();
ContactInfo result = agent.call("我的名字是张三,邮箱 zhangsan@example.com,电话 13800138000");
/**
* 方式二:outputSchema(String) - 手动指定 Schema
*/
BeanOutputConverter<ContactInfo> converter = new BeanOutputConverter<>(ContactInfo.class);
String schema = converter.getFormat(); // 从 Java 类自动生成
ReactAgent agent = ReactAgent.builder()
.name("contact_extractor")
.model(chatModel)
.outputSchema(schema) // 使用生成的 Schema
.instruction("从用户消息中提取联系人信息")
.build();
3.4 JsonSchema 精细控制(原生模式)
java
@Service
public class JsonSchemaService {
private final ChatClient chatClient;
public String queryWithJsonSchema(String naturalLanguageQuery) {
String schema = """
{
"type": "object",
"properties": {
"query_type": { "type": "string", "enum": ["order","refund","logistics","other"] },
"keywords": { "type": "array", "items": { "type": "string" } },
"needs_human": { "type": "boolean" }
},
"required": ["query_type","keywords","needs_human"],
"additionalProperties": false
}
""";
return chatClient.prompt()
.system("请把用户口语化请求解析为结构化 query。")
.user(naturalLanguageQuery)
.options(DashScopeChatOptions.builder()
.withResultFormat(DashScopeApi.JsonSchema.builder()
.schema(JSON.parseObject(schema))
.name("QueryAnalysis")
.strict(true) // 严格模式,不合规输出报错
.build())
.build())
.call()
.content();
}
}
四、多轮对话管理
4.1 ChatMemory 架构
┌─────────────────────────────────────────────────────────────────┐
│ ChatMemory 架构 │
│ │
│ ChatClient │
│ │ │
│ ▼ │
│ MessageChatMemoryAdvisor (拦截器) │
│ ├── Before Request: 从 Memory 读取历史 → 注入 Prompt│
│ ├── Call LLM │
│ └── After Response: 将用户提问 + 模型回复写入 Memory│
│ │
│ ChatMemoryRepository (存储层抽象) │
│ ├── InMemoryChatMemoryRepository (内存,开发用) │
│ ├── RedisChatMemoryRepository (Redis,生产用) │
│ └── JdbcChatMemoryRepository (数据库) │
└─────────────────────────────────────────────────────────────────┘
4.2 内存版多轮对话
java
@Configuration
public class MemoryChatConfig {
@Bean
public MessageChatMemoryAdvisor memoryAdvisor() {
return new MessageChatMemoryAdvisor(
MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(20) // ⚠️ 关键:保留最近20条消息
.build()
);
}
}
@Service
public class MultiTurnService {
private final ChatClient chatClient;
private final MessageChatMemoryAdvisor memoryAdvisor;
public String multiTurn(String sessionId, String userMessage) {
return chatClient.prompt()
.user(userMessage)
.advisors(memoryAdvisor, a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
.call()
.content();
}
}
4.3 Redis 持久化(生产级)
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis</artifactId>
</dependency>
java
@Configuration
public class RedisMemoryConfig {
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository(
RedisTemplate<String, Object> redisTemplate) {
return new RedisChatMemoryRepository(redisTemplate);
}
@Bean
public MessageChatMemoryAdvisor redisMemoryAdvisor(
RedisChatMemoryRepository repository) {
return new MessageChatMemoryAdvisor(
MessageWindowChatMemory.builder()
.chatMemoryRepository(repository) // Redis 持久化
.maxMessages(20) // 每会话保留20条消息
.build()
);
}
}
生产级 Redis 配置要点:
maxMessages建议 10-50 条,避免 Token 浪费和注意力分散- 严格保证
conversationId唯一性,防止会话越权 - Redis 持久化支持多实例部署,服务重启不丢失
4.4 Agent 场景的状态持久化
java
/**
* RedisSaver:Agent 状态持久化到 Redis
*/
@Configuration
public class AgentRedisConfig {
@Bean
public ReactAgent persistentAgent(ChatModel chatModel, RedissonClient redisson) {
RedisSaver redisSaver = new RedisSaver(redisson);
return ReactAgent.builder()
.name("persistent_agent")
.model(chatModel)
.saver(redisSaver) // Redis 持久化
.instruction("你是一个持久化记忆的助手")
.build();
}
}
@Service
public class PersistentAgentService {
private final ReactAgent agent;
/**
* 通过 threadId 管理会话
*/
public String chat(String threadId, String message) {
return agent.call(message, threadId); // threadId 隔离不同会话
}
}
4.5 手动管理消息历史(灵活场景)
java
@Service
public class CustomMultiTurnService {
private final ChatModel chatModel;
private final Map<String, List<Message>> sessionStore = new ConcurrentHashMap<>();
/**
* 手动管理消息列表,实现"回滚某轮"、"编辑历史"等高级功能
*/
public String chatInSession(String sessionId, String userMessage) {
List<Message> messages = sessionStore.computeIfAbsent(sessionId, k -> new ArrayList<>());
// 添加用户消息
messages.add(new UserMessage(userMessage));
// 发送完整消息列表
String response = chatModel.call(new Prompt(messages,
DashScopeChatOptions.builder().withModel("qwen-plus").build()
)).getResult().getOutput().getContent();
// 添加 assistant 回复
messages.add(new AssistantMessage(response));
// 限制消息数量,防止超长
if (messages.size() > 30) {
// 保留系统消息 + 最近20条
messages.subList(0, messages.size() - 20).clear();
}
return response;
}
/**
* 回滚历史(删除上一轮 user + assistant)
*/
public void rollbackLast(String sessionId) {
List<Message> messages = sessionStore.get(sessionId);
if (messages != null && messages.size() >= 2) {
messages.removeLast(); // 删 assistant reply
messages.removeLast(); // 删 user message
}
}
}
五、流式处理(Streaming)
5.1 基础流式输出
java
@RestController
@RequestMapping("/api/stream")
public class StreamingController {
private final ChatClient chatClient;
/**
* Flux 流式(默认增量输出)
*/
@GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String message) {
return chatClient.prompt(message)
.stream()
.content();
}
/**
* Server-Sent Events 包装
*/
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> sseStream(@RequestParam String message) {
return chatClient.prompt(message)
.stream()
.content()
.map(content -> ServerSentEvent.<String>builder()
.data(content)
.build());
}
}
5.2 流式中观测工具调用
java
/**
* 流式过程中透出工具调用中间态(UI 显示"正在查询订单...")
*/
@GetMapping(value = "/stream-with-tools", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamWithToolObservation(@RequestParam String message) {
return chatClient.prompt(message)
.functions("queryOrder", "createRefund")
.stream()
.chatResponse()
.flatMap(response -> {
// 检测是否有工具调用
if (response.getResult() != null &&
response.getResult().getOutput() != null &&
response.getResult().getOutput().getToolCalls() != null) {
return Flux.just("🔧 正在调用工具: " +
response.getResult().getOutput().getToolCalls().get(0).getName() + "...");
}
// 返回文本内容
String content = response.getResult() != null ?
response.getResult().getOutput().getContent() : "";
if (content != null && !content.isBlank()) {
return Flux.just(content);
}
return Flux.empty();
});
}
5.3 流式 Function Calling 的坑与解决方案
通义千问的流式响应可能将工具调用的参数(arguments)拆分成多个片段返回,Spring AI 需要正确处理这种分片:
java
/**
* 自定义流式工具调用处理器
*/
@Component
public class StreamingToolCallHandler {
private final Map<String, StringBuilder> toolArgBuffers = new ConcurrentHashMap<>();
public Flux<String> handleStreamingToolCalls(Flux<ChatResponse> stream) {
return stream
.flatMap(response -> {
if (response.getResult() == null) return Flux.empty();
var output = response.getResult().getOutput();
if (output == null) return Flux.empty();
// 处理工具调用片段
if (output.getToolCalls() != null && !output.getToolCalls().isEmpty()) {
for (var toolCall : output.getToolCalls()) {
String id = toolCall.getId();
String arguments = toolCall.getArguments();
if (arguments != null) {
// 累积参数片段
toolArgBuffers.computeIfAbsent(id, k -> new StringBuilder())
.append(arguments);
}
}
return Flux.just("🔧 正在处理工具调用...");
}
// 输出文本内容
String content = output.getContent();
if (content != null && !content.isBlank()) {
return Flux.just(content);
}
return Flux.empty();
});
}
}
六、限流与重试
6.1 DashScope 限流说明
| 模型 | 免费版 QPS | 付费版 QPS | 说明 |
|---|---|---|---|
| qwen-turbo | 1 | 50+ | 入门模型 |
| qwen-plus | 1 | 50+ | 通用推荐 |
| qwen-max | 需付费 | 50+ | 最强性能 |
| qwen-long | 需付费 | 20+ | 超长上下文 |
6.2 Resilience4j 限流 + 重试
java
@Service
public class RateLimitedChatService {
private final ChatClient chatClient;
private final CacheService cacheService;
@RateLimiter(name = "dashscope", fallbackMethod = "fallbackToCache")
@Retry(name = "dashscope")
public String chatWithProtection(String message) {
return chatClient.prompt(message).call().content();
}
public String fallbackToCache(String message, Throwable t) {
log.warn("Qwen 限流兜底,查询缓存", t);
return cacheService.getCachedAnswer(message);
}
}
yaml
# application.yml
resilience4j:
ratelimiter:
instances:
dashscope:
limitForPeriod: 10 # 每周期 10 次
limitRefreshPeriod: 1s
timeoutDuration: 500ms
retry:
instances:
dashscope:
maxAttempts: 3
waitDuration: 1s
retryExceptions:
- com.alibaba.dashscope.exception.StatusErrorException # 429 限流重试
6.3 熔断器配置
java
@Configuration
public class ResilienceConfig {
@Bean
public CircuitBreaker circuitBreaker() {
return CircuitBreaker.of("dashscope", CircuitBreakerConfig.custom()
.slidingWindowSize(100)
.failureRateThreshold(50) // 失败率 > 50% 触发熔断
.waitDurationInOpenState(Duration.ofSeconds(30))
.permittedNumberOfCallsInHalfOpenState(10)
.build());
}
}
七、多轮 + Function Calling 组合
多轮叠加 Function Calling 时的注意事项:
java
@Service
public class MultiTurnFunctionService {
private final ChatClient chatClient;
private final MessageChatMemoryAdvisor memoryAdvisor;
/**
* 第1轮:用户"帮我查一下 MT2025001" → 模型调用 tool → 返回订单信息
* 第2轮:用户"那金额是多少" → 模型记住上一轮订单号,继续用 tool 查
*/
public String chatMultiTurnWithTools(String sessionId, String userMessage) {
return chatClient.prompt()
.user(userMessage)
.functions("queryOrder", "createRefund")
.advisors(memoryAdvisor, a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
.call()
.content();
}
}
注意事项:
- Function Calling 的 tool_calls 和 tool_results 也会被 ChatMemory 记录
- 多次 tool 调用的上下文会自动累积,无需手动管理
- 建议将
maxMessages设大一些(如 30-50),因为工具调用会占用多条消息 - 如果工具返回大量数据,考虑只保留关键字段,避免上下文超长
八、常见问题与踩坑指南
8.1 Function Calling 相关
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 模型不调用工具 | 工具描述不够清晰 | 优化 @Tool 的 description,包含触发条件 |
| 工具参数解析失败 | 参数类型不匹配 | 使用 @ToolParam 明确类型和描述 |
| 并行调用未生效 | parallel-tool-calls 未开启 |
设置 withParallelToolCalls(true) |
| 流式工具调用报错 | 参数被拆分成多个片段 | 使用自定义累积处理器 |
8.2 结构化输出相关
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 输出包含额外文本 | 未启用 strict 模式 | 设置 strict(true) |
| 字段缺失 | required 未正确设置 | 在 Schema 中明确 required 字段 |
| 枚举值不匹配 | 模型输出不在枚举范围内 | 在 Prompt 中强调枚举值列表 |
| POJO 反序列化失败 | JSON 字段名不匹配 | 使用 @JsonProperty 映射 |
8.3 多轮对话相关
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 会话串数据 | conversationId 不唯一 |
使用 UUID 或 JWT 中的唯一标识 |
| 上下文过长 | maxMessages 设置过大 |
设置 10-50 条,超出后滚动丢弃 |
| 重启后记忆丢失 | 使用内存存储 | 切换到 Redis 持久化 |
| 多实例会话不同步 | 内存存储无法共享 | 使用 Redis 共享存储 |
8.4 流式输出相关
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 流式输出报错 | 版本兼容性问题 | 升级到最新版本 |
| 前端接收乱码 | Content-Type 不正确 | 使用 text/event-stream |
| 连接超时 | 响应时间过长 | 增加超时配置,或使用异步处理 |
九、最佳实践清单
9.1 参数调优
- ✅ 确定性任务(代码/数学)用
temperature=0.0-0.2 - ✅ 创意任务用
temperature=0.8-1.2 - ✅ 事实问答开启
enable-search=true - ✅ 使用
seed保证可复现性
9.2 Function Calling
- ✅ 工具描述包含触发条件和参数示例
- ✅ 使用 JSR-303 校验工具参数
- ✅ 设置合理的工具执行超时
- ✅ 并行调用时注意线程安全
9.3 结构化输出
- ✅ 使用
BeanOutputConverter从 Java 类自动生成 Schema - ✅ Agent 场景使用
outputType(Class)类型安全 - ✅ 严格模式确保输出格式精确
9.4 多轮对话
- ✅ 生产环境使用 Redis 持久化
- ✅
maxMessages设置 10-50 条 - ✅
conversationId使用唯一标识 - ✅ 多实例部署使用 Redis 共享存储
9.5 限流与容错
- ✅ 接入 Resilience4j 限流和重试
- ✅ 配置熔断器防止雪崩
- ✅ 提供降级方案(缓存/默认回复)
9.6 可观测性
- ✅ 记录每次调用的 Token 用量
- ✅ 监控限流触发次数和错误率
- ✅ 使用 MDC 传递 traceId 便于链路追踪
十、总结
本章深入通义千问 Chat 模型四大高级能力及生产级最佳实践:
| 模块 | 核心内容 | 关键点 |
|---|---|---|
| 参数调优 | temperature/top_p/top_k 等参数详解 | 确定性任务低温,创意任务高温 |
| Function Calling | @Bean/@Tool 注册、并行调用、ToolChoice 强制、参数校验 | 工具描述质量决定调用准确性 |
| Structured Output | Bean/Agent/JsonSchema 三档模式 | outputType 类型安全 |
| 多轮对话 | ChatMemory + Advisor 架构 | Redis 持久化生产必备 |
| 流式处理 | Flux/SSE 流式输出 | 注意工具调用参数分片问题 |
| 限流容错 | Resilience4j 限流/重试/熔断 | 降级方案保障可用性 |
参考资源: