001/* 002 * Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.agentsflex.core.model.chat; 017 018import com.agentsflex.core.util.Maps; 019import com.agentsflex.core.util.StringUtil; 020 021import java.util.List; 022import java.util.Map; 023 024/** 025 * 聊天选项配置类,用于控制大语言模型(LLM)的生成行为。 026 * 支持 Builder 模式,便于链式调用。 027 * 注意:不同模型厂商对参数的支持和默认值可能不同。 028 */ 029public class ChatOptions { 030 031 032 /** 033 * 指定使用的大模型名称。 034 * 例如:"gpt-4", "qwen-max", "claude-3-sonnet" 等。 035 * 如果未设置,将使用客户端默认模型。 036 */ 037 private String model; 038 039 /** 040 * 随机种子(Seed),用于控制生成结果的可重复性。 041 * 当 seed 相同时,相同输入将产生相同输出(前提是其他参数也一致)。 042 * 注意:并非所有模型都支持 seed 参数。 043 */ 044 private String seed; 045 046 /** 047 * 温度(Temperature)控制输出的随机性。 048 * <ul> 049 * <li>值越低(如 0.1~0.3):输出更确定、稳定、可重复,适合事实性任务(如 RAG、结构化输出)</li> 050 * <li>值越高(如 0.7~1.0):输出更多样、有创意,但可能不稳定或偏离事实</li> 051 * </ul> 052 * 推荐值: 053 * <ul> 054 * <li>文档处理、路由、工具调用:0.1 ~ 0.3</li> 055 * <li>问答、摘要:0.2 ~ 0.5</li> 056 * <li>创意写作:0.7 ~ 1.0</li> 057 * </ul> 058 * 默认值:0.5f 059 */ 060 private Float temperature = 0.5f; 061 062 /** 063 * Top-p(也称 nucleus sampling)控制生成时考虑的概率质量。 064 * 模型从累积概率不超过 p 的最小词集中采样。 065 * - 值为 1.0 表示考虑所有词(等同于无 top-p 限制) 066 * - 值为 0.9 表示只考虑累积概率达 90% 的词 067 * 注意:temperature 和 top_p 不应同时调整,通常只用其一。 068 */ 069 private Float topP; 070 071 /** 072 * Top-k 控制生成时考虑的最高概率词的数量。 073 * 模型仅从 top-k 个最可能的词中采样。 074 * - 值为 50 表示只考虑概率最高的 50 个词 075 * - 值越小,输出越确定;值越大,输出越多样 076 * 注意:与 top_p 类似,通常不与 temperature 同时使用。 077 */ 078 private Integer topK; 079 080 /** 081 * 生成内容的最大 token 数量(不包括输入 prompt)。 082 * 用于限制响应长度,防止生成过长内容。 083 * 注意:不同模型有不同上限,超过将被截断或报错。 084 */ 085 private Integer maxTokens; 086 087 /** 088 * 停止序列(Stop Sequences),当生成内容包含这些字符串时立即停止。 089 * 例如:设置为 ["\n", "。"] 可在句末或换行时停止。 090 * 适用于需要精确控制输出长度的场景。 091 */ 092 private List<String> stop; 093 094 /** 095 * 是否启用“思考模式”(Thinking Mode)。 096 * 适用于支持该特性的模型(如 Qwen3),开启后模型会显式输出推理过程。 097 * 默认为 null(由模型决定)。 098 */ 099 private Boolean thinkingEnabled; 100 101 /** 102 * 是否返回 Usage 信息, 仅在 stream 模式下有效。 103 * 适用于支持该特性的模型(如 Qwen3),开启后模型会返回 Usage 信息。 104 * 默认为 true。 105 */ 106 private Boolean includeUsage; 107 108 /** 109 * 额外的模型参数,用于传递模型特有或未明确暴露的配置。 110 * 例如:{"response_format": "json", "presence_penalty": 0.5} 111 * 使用 addExtraBody() 方法可方便地添加单个参数。 112 */ 113 private Map<String, Object> extraBody; 114 115 116 protected Boolean retryEnabled; // 默认开启错误重试 117 protected Integer retryCount; 118 protected Integer retryInitialDelayMs; 119 120 121 private Map<String, Object> responseFormat; 122 123 /** 124 * 是否为流式请求。 125 * 这个不允许用户设置,由 Framework 自动设置(用户设置也可能被修改)。 126 * 用户调用 chat 或者 chatStream 方法时,会自动设置这个字段。 127 */ 128 private boolean streaming; 129 130 // ===== 构造函数 ===== 131 public ChatOptions() { 132 } 133 134 private ChatOptions(Builder builder) { 135 this.model = builder.model; 136 this.seed = builder.seed; 137 this.temperature = builder.temperature; 138 this.topP = builder.topP; 139 this.topK = builder.topK; 140 this.maxTokens = builder.maxTokens; 141 this.stop = builder.stop; 142 this.thinkingEnabled = builder.thinkingEnabled; 143 this.includeUsage = builder.includeUsage; 144 this.extraBody = builder.extraBody; 145 this.retryEnabled = builder.retryEnabled; 146 this.retryCount = builder.retryCount; 147 this.retryInitialDelayMs = builder.retryInitialDelayMs; 148 this.responseFormat = builder.responseFormat; 149 } 150 151 // ===== Getter / Setter ===== 152 153 public String getModel() { 154 return model; 155 } 156 157 public String getModelOrDefault(String defaultModel) { 158 return StringUtil.hasText(model) ? model : defaultModel; 159 } 160 161 public void setModel(String model) { 162 this.model = model; 163 } 164 165 public String getSeed() { 166 return seed; 167 } 168 169 public void setSeed(String seed) { 170 this.seed = seed; 171 } 172 173 public Float getTemperature() { 174 return temperature; 175 } 176 177 public void setTemperature(Float temperature) { 178 if (temperature != null && temperature < 0) { 179 throw new IllegalArgumentException("temperature must be greater than 0"); 180 } 181 this.temperature = temperature; 182 } 183 184 public Float getTopP() { 185 return topP; 186 } 187 188 public void setTopP(Float topP) { 189 if (topP != null && (topP < 0 || topP > 1)) { 190 throw new IllegalArgumentException("topP must be between 0 and 1"); 191 } 192 this.topP = topP; 193 } 194 195 public Integer getTopK() { 196 return topK; 197 } 198 199 public void setTopK(Integer topK) { 200 if (topK != null && topK < 0) { 201 throw new IllegalArgumentException("topK must be greater than 0"); 202 } 203 this.topK = topK; 204 } 205 206 public Integer getMaxTokens() { 207 return maxTokens; 208 } 209 210 public void setMaxTokens(Integer maxTokens) { 211 if (maxTokens != null && maxTokens < 0) { 212 throw new IllegalArgumentException("maxTokens must be greater than 0"); 213 } 214 this.maxTokens = maxTokens; 215 } 216 217 public List<String> getStop() { 218 return stop; 219 } 220 221 public void setStop(List<String> stop) { 222 this.stop = stop; 223 } 224 225 public Boolean getThinkingEnabled() { 226 return thinkingEnabled; 227 } 228 229 public Boolean getThinkingEnabledOrDefault(Boolean defaultValue) { 230 return thinkingEnabled != null ? thinkingEnabled : defaultValue; 231 } 232 233 public void setThinkingEnabled(Boolean thinkingEnabled) { 234 this.thinkingEnabled = thinkingEnabled; 235 } 236 237 public Boolean getIncludeUsage() { 238 return includeUsage; 239 } 240 241 public Boolean getIncludeUsageOrDefault(Boolean defaultValue) { 242 return includeUsage != null ? includeUsage : defaultValue; 243 } 244 245 public void setIncludeUsage(Boolean includeUsage) { 246 this.includeUsage = includeUsage; 247 } 248 249 250 public Map<String, Object> getExtraBody() { 251 return extraBody; 252 } 253 254 public void setExtraBody(Map<String, Object> extraBody) { 255 this.extraBody = extraBody; 256 } 257 258 /** 259 * 添加一个额外参数到 extra 映射中。 260 * 261 * @param key 参数名 262 * @param value 参数值 263 */ 264 public void addExtraBody(String key, Object value) { 265 if (extraBody == null) { 266 extraBody = Maps.of(key, value); 267 } else { 268 extraBody.put(key, value); 269 } 270 } 271 272 public Boolean getRetryEnabled() { 273 return retryEnabled; 274 } 275 276 public boolean getRetryEnabledOrDefault(boolean defaultValue) { 277 return retryEnabled != null ? retryEnabled : defaultValue; 278 } 279 280 public void setRetryEnabled(Boolean retryEnabled) { 281 this.retryEnabled = retryEnabled; 282 } 283 284 public Integer getRetryCount() { 285 return retryCount; 286 } 287 288 public int getRetryCountOrDefault(int defaultValue) { 289 return retryCount != null ? retryCount : defaultValue; 290 } 291 292 293 public void setRetryCount(Integer retryCount) { 294 this.retryCount = retryCount; 295 } 296 297 public Integer getRetryInitialDelayMs() { 298 return retryInitialDelayMs; 299 } 300 301 public int getRetryInitialDelayMsOrDefault(int defaultValue) { 302 return retryInitialDelayMs != null ? retryInitialDelayMs : defaultValue; 303 } 304 305 public void setRetryInitialDelayMs(Integer retryInitialDelayMs) { 306 this.retryInitialDelayMs = retryInitialDelayMs; 307 } 308 309 public Map<String, Object> getResponseFormat() { 310 return responseFormat; 311 } 312 313 public void setResponseFormat(Map<String, Object> responseFormat) { 314 this.responseFormat = responseFormat; 315 } 316 317 public boolean isStreaming() { 318 return streaming; 319 } 320 321 public void setStreaming(boolean streaming) { 322 this.streaming = streaming; 323 } 324 325 326 /** 327 * 创建 ChatOptions 的 Builder 实例。 328 * 329 * @return 新的 Builder 对象 330 */ 331 public static Builder builder() { 332 return new Builder(); 333 } 334 335 /** 336 * ChatOptions 的构建器类,支持链式调用。 337 */ 338 public static final class Builder { 339 340 private String model; 341 private String seed; 342 private Float temperature = 0.5f; 343 private Float topP; 344 private Integer topK; 345 private Integer maxTokens; 346 private List<String> stop; 347 private Boolean thinkingEnabled; 348 private Boolean includeUsage; 349 private Map<String, Object> extraBody; 350 private Boolean retryEnabled; 351 private int retryCount = 3; 352 private int retryInitialDelayMs = 1000; 353 public Map<String, Object> responseFormat; 354 355 public Builder model(String model) { 356 this.model = model; 357 return this; 358 } 359 360 public Builder seed(String seed) { 361 this.seed = seed; 362 return this; 363 } 364 365 public Builder temperature(Float temperature) { 366 this.temperature = temperature; 367 return this; 368 } 369 370 public Builder topP(Float topP) { 371 this.topP = topP; 372 return this; 373 } 374 375 public Builder topK(Integer topK) { 376 this.topK = topK; 377 return this; 378 } 379 380 public Builder maxTokens(Integer maxTokens) { 381 this.maxTokens = maxTokens; 382 return this; 383 } 384 385 public Builder stop(List<String> stop) { 386 this.stop = stop; 387 return this; 388 } 389 390 public Builder thinkingEnabled(Boolean thinkingEnabled) { 391 this.thinkingEnabled = thinkingEnabled; 392 return this; 393 } 394 395 public Builder includeUsage(Boolean includeUsage) { 396 this.includeUsage = includeUsage; 397 return this; 398 } 399 400 public Builder extraBody(Map<String, Object> extra) { 401 this.extraBody = extra; 402 return this; 403 } 404 405 public Builder addExtraBody(String key, Object value) { 406 if (this.extraBody == null) { 407 this.extraBody = Maps.of(key, value); 408 } else { 409 this.extraBody.put(key, value); 410 } 411 return this; 412 } 413 414 public Builder retryEnabled(Boolean retryEnabled) { 415 this.retryEnabled = retryEnabled; 416 return this; 417 } 418 419 public Builder retryCount(int retryCount) { 420 this.retryCount = retryCount; 421 return this; 422 } 423 424 public Builder retryInitialDelayMs(int retryInitialDelayMs) { 425 this.retryInitialDelayMs = retryInitialDelayMs; 426 return this; 427 } 428 429 public Builder responseFormat(Map<String, Object> responseFormat) { 430 this.responseFormat = responseFormat; 431 return this; 432 } 433 434 public Builder responseFormatToJsonObject() { 435 this.responseFormat = Maps.of("type", "json_object"); 436 return this; 437 } 438 439 public Builder responseFormatToJsonSchema(Map<String, Object> json_schema) { 440 this.responseFormat = Maps.of("type", "json_schema").set("json_schema", json_schema); 441 return this; 442 } 443 444 /** 445 * 构建并返回 ChatOptions 实例。 446 * 447 * @return 配置完成的 ChatOptions 对象 448 */ 449 public ChatOptions build() { 450 return new ChatOptions(this); 451 } 452 } 453}