主题
watsonx.ai
本集成构建于 IBM watsonx.ai Java SDK 之上。下文每个模型都包装它的某个服务。当你需要了解某个非 LangChain4j 专有行为的细节——令牌缓存、重试、HTTP 客户端调优——SDK 文档即是参考。
Maven 依赖
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-watsonx</artifactId>
<version>1.22.0-beta32</version>
</dependency>身份认证
Watsonx.ai 支持通过 Authenticator 接口进行身份验证。
这使你可以根据部署方式使用不同的认证机制:
- IBMCloudAuthenticator —— 使用 API 密钥向 IBM Cloud 认证。这是最简单的方式,在你提供
apiKey(...)builder 方法时使用。 - CP4DAuthenticator —— 向 Cloud Pak for Data 部署认证。
- 自定义认证器 —— 可使用
Authenticator接口的任意实现。
WatsonxChatModel、WatsonxStreamingChatModel 及其他服务 builder 既接受通过 .apiKey(...) 的快捷方式,也接受通过 .authenticator(...) 的完整 Authenticator 实例。
令牌缓存与续期会被透明地处理。令牌在首次请求时获取、被缓存、并在过期前刷新,因此你从不管理它的生命周期。把同一个 Authenticator 实例传给多个模型,能让它们共享一个缓存令牌。认证器及其参数的完整列表见 SDK 认证指南。
Example
java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxChatModel;
import com.ibm.watsonx.ai.core.auth.cp4d.CP4DAuthenticator;
import com.ibm.watsonx.ai.core.auth.cp4d.AuthMode;
import com.ibm.watsonx.ai.CloudRegion;
WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key") // Simple IBM Cloud authentication
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.build();
WatsonxChatModel.builder()
.baseUrl("https://my-instance-url")
.authenticator( // For Cloud Pak for Data deployments
CP4DAuthenticator.builder()
.baseUrl("https://my-instance-url")
.username("username")
.apiKey("api-key")
.authMode(AuthMode.LEGACY)
.build()
)
.projectId("my-project-id")
.modelName("ibm/granite-4-h-small")
.build();自定义 HttpClient 与 SSL 配置
使用自定义 HttpClient
所有服务与认证器都通过 builder 模式支持自定义的 HttpClient 实例。这在 Cloud Pak for Data 环境中特别有用——你可能需要配置自定义 TLS/SSL 设置、代理配置或其他 HTTP 客户端属性。
java
HttpClient httpClient = HttpClient.newBuilder()
.sslContext(createCustomSSLContext())
.executor(ExecutorProvider.ioExecutor())
.build();
EmbeddingModel embeddingModel = WatsonxEmbeddingModel.builder()
.baseUrl("https://my-instance-url")
.modelName("ibm/granite-embedding-278m-multilingual")
.projectId("project-id")
.httpClient(httpClient) // Custom HttpClient
.authenticator(
CP4DAuthenticator.builder()
.baseUrl("https://my-instance-url")
.username("username")
.apiKey("api-key")
.httpClient(httpClient) // Custom HttpClient
.build()
)
.build();注: 在 Cloud Pak for Data 中使用自定义
HttpClient时,请确保同时在服务 builder 和认证器 builder 上设置它,以保证所有请求的 HTTP 行为一致。
🔗 SDK 关于 HTTP 客户端的指南,包括如何从一个私有信任库构建
SSLContext。
禁用 SSL 验证
如果你只需禁用 SSL 证书验证,可以使用 verifySsl(false) 选项,而不必提供自定义 HttpClient:
java
EmbeddingModel embeddingModel = WatsonxEmbeddingModel.builder()
.baseUrl("https://my-instance-url")
.modelName("ibm/granite-embedding-278m-multilingual")
.projectId("project-id")
.verifySsl(false) // Disable SSL verification
.authenticator(
CP4DAuthenticator.builder()
.baseUrl("https://my-instance-url")
.username("username")
.apiKey("api-key")
.verifySsl(false) // Disable SSL verification
.build()
)
.build();如何创建 IBM Cloud API 密钥
你可以在 https://cloud.ibm.com/iam/apikeys 点击 Create + 创建 API 密钥。
如何找到你的 Project ID
- 访问 https://dataplatform.cloud.ibm.com/projects/?context=wx
- 打开你的项目
- 进入 Manage 标签
- 从 Details 部分复制 Project ID
WatsonxChatModel
WatsonxChatModel 类让你能创建一个完全封装在 LangChain4j 内的 ChatModel 接口实例。要创建一个实例,你必须指定以下必填参数:
baseUrl(...)—— IBM Cloud 端点 URL(以String、URI或CloudRegion形式)apiKey(...)—— IBM Cloud IAM API 密钥projectId(...)—— IBM Cloud Project ID(或使用spaceId(...))modelName(...)—— 用于推理的基础模型 ID
你可以用
.apiKey(...)或通过.authenticator(...)提供一个完整Authenticator实例来认证。
要调用你已按需部署的模型,请改用
WatsonxDeploymentChatModel。
Example
java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxChatModel;
import com.ibm.watsonx.ai.CloudRegion;
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.temperature(0.7)
.maxOutputTokens(0)
.build();
String answer = chatModel.chat("Hello from watsonx.ai");
System.out.println(answer);🔗 查看可用模型
WatsonxStreamingChatModel
WatsonxStreamingChatModel 在 LangChain4j 内为 IBM watsonx.ai 提供流式支持。当你想在词元生成之际就处理它们时它很有用,非常适合聊天 UI 或长文本生成这类实时应用。
流式使用与非流式 WatsonxChatModel 相同的配置结构与参数。主要区别在于响应通过一个处理器接口增量交付。
要调用你已按需部署的模型,请改用
WatsonxDeploymentStreamingChatModel。
Example
java
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.StreamingChatResponseHandler;
import dev.langchain4j.model.chat.ChatResponse;
import dev.langchain4j.model.watsonx.WatsonxStreamingChatModel;
import com.ibm.watsonx.ai.CloudRegion;
StreamingChatModel model = WatsonxStreamingChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.maxOutputTokens(0)
.build();
model.chat("What is the capital of Italy?", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.println("Partial: " + partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("Complete: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});🔗 查看可用模型
已部署模型(按需部署)
IBM watsonx.ai 允许你在专用硬件上按需部署基础模型,供你的组织独占使用。这些已部署模型以其 deploymentId 寻址,并由一个不同于基础模型目录的 watsonx.ai 端点服务,因此 LangChain4j 通过它们各自的一对类 WatsonxDeploymentChatModel 与 WatsonxDeploymentStreamingChatModel 暴露它们。
要创建一个实例,你必须指定:
baseUrl(...)—— IBM Cloud 端点 URL(以String、URI或CloudRegion形式)apiKey(...)—— IBM Cloud IAM API 密钥deploymentId(...)—— 按需部署模型的 Deployment ID
一个部署已经指向某个项目或空间内的特定模型,因此这些 builder 既不暴露 modelName(...),也不暴露 projectId(...)/spaceId(...)。其他每个生成参数(temperature、maxOutputTokens、thinking、工具、responseFormat……)的工作方式与在 WatsonxChatModel 上完全一致。
注:
deploymentId是一个连接级的设置,在构建模型时固定——它选择部署端点,因此不能通过WatsonxChatRequestParameters逐次请求覆盖。
WatsonxDeploymentChatModel
java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxDeploymentChatModel;
import com.ibm.watsonx.ai.CloudRegion;
ChatModel chatModel = WatsonxDeploymentChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.deploymentId("your-deployment-id")
.temperature(0.7)
.maxOutputTokens(0)
.build();
String answer = chatModel.chat("Hello from watsonx.ai");
System.out.println(answer);WatsonxDeploymentStreamingChatModel
java
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.StreamingChatResponseHandler;
import dev.langchain4j.model.chat.ChatResponse;
import dev.langchain4j.model.watsonx.WatsonxDeploymentStreamingChatModel;
import com.ibm.watsonx.ai.CloudRegion;
StreamingChatModel model = WatsonxDeploymentStreamingChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.deploymentId("your-deployment-id")
.maxOutputTokens(0)
.build();
model.chat("What is the capital of Italy?", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.println("Partial: " + partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("Complete: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});模型网关(Model Gateway)
IBM watsonx.ai 的 Model Gateway 暴露一个兼容 OpenAI 的聊天端点,能在单个 watsonx.ai 入口之后,将请求路由到由多家提供者(例如 OpenAI、Anthropic,或你注册的第三方提供者)托管的模型。LangChain4j 通过 WatsonxGatewayChatModel 与 WatsonxGatewayStreamingChatModel 与之集成。
注: 该网关须由管理员在使用前配置——你传入的每个
modelName都必须是网关中已注册的 id。
WatsonxGatewayChatModel
要创建一个实例,指定:
baseUrl(...)—— IBM Cloud 端点 URL(以String、URI或CloudRegion形式)apiKey(...)—— IBM Cloud IAM API 密钥(或通过.authenticator(...)提供完整Authenticator)modelName(...)—— 网关中注册的、OpenAI 风格的模型 id
java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxGatewayChatModel;
import com.ibm.watsonx.ai.CloudRegion;
ChatModel chatModel = WatsonxGatewayChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.modelName("gpt-4o")
.temperature(0.7)
.build();
String answer = chatModel.chat("Hello from the watsonx.ai Model Gateway");
System.out.println(answer);WatsonxGatewayStreamingChatModel
WatsonxGatewayStreamingChatModel 为网关提供流式支持。它使用与 WatsonxGatewayChatModel 相同的配置。响应通过一个处理器增量交付。
java
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.StreamingChatResponseHandler;
import dev.langchain4j.model.chat.ChatResponse;
import dev.langchain4j.model.watsonx.WatsonxGatewayStreamingChatModel;
import com.ibm.watsonx.ai.CloudRegion;
StreamingChatModel model = WatsonxGatewayStreamingChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.modelName("gpt-4o")
.build();
model.chat("What is the capital of Italy?", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.println("Partial: " + partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("Complete: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});仅网关支持的参数
每个 watsonx.ai 聊天服务都有自己的 ChatRequestParameters 实现,只暴露该服务接受的参数,别无其他:
| 类 | 被谁使用 |
|---|---|
WatsonxChatRequestParameters | WatsonxChatModel、WatsonxStreamingChatModel、WatsonxDeploymentChatModel、WatsonxDeploymentStreamingChatModel |
WatsonxGatewayChatRequestParameters | WatsonxGatewayChatModel、WatsonxGatewayStreamingChatModel |
这两个类是 ChatRequestParameters 的相互独立实现。每一个都精确声明其服务支持什么,因此谁也不知道对方的参数。把一个服务的参数传给另一个——无论是通过 builder 上的 defaultRequestParameters(...),还是通过单个 ChatRequest 的参数——因此只贡献 DefaultChatRequestParameters 所涵盖的部分(modelName、temperature、topP、maxOutputTokens……),而它们携带的每个 watsonx.ai 专有参数都会被忽略。请使用与你所调用模型相匹配的那个类。
除通用聊天参数外,WatsonxGatewayChatRequestParameters 还暴露网关专有参数:
| 参数 | 描述 |
|---|---|
serviceTier(...) | 服务层级,AUTO、DEFAULT、FLEX 或 PRIORITY 之一。 |
reasoningEffort(...) | 推理模型的推理精力,LOW、MEDIUM 或 HIGH 之一。 |
router(...) / cache(...) | 路由器配置,含一个提示词 Cache。 |
modalities(...) | 输出模态(例如 ["text"])。 |
store(...) | 提供者是否应持久化请求/响应。 |
parallelToolCalls(...) | 启用/禁用并行工具调用。 |
user(...) | 转发给提供者的终端用户标识。 |
metadata(...) | 转发给提供者的自由格式元数据 map。 |
logitBias(...)、logprobs(...)、topLogprobs(...)、seed(...) | 兼容 OpenAI 的采样控制。 |
java
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.watsonx.WatsonxGatewayChatRequestParameters;
import com.ibm.watsonx.ai.gateway.chat.ModelGatewayParameters.ReasoningEffort;
import com.ibm.watsonx.ai.gateway.chat.ModelGatewayParameters.ServiceTier;
ChatRequest request = ChatRequest.builder()
.messages(UserMessage.from("Solve this step by step."))
.parameters(
WatsonxGatewayChatRequestParameters.builder()
.serviceTier(ServiceTier.FLEX)
.reasoningEffort(ReasoningEffort.HIGH)
.build()
).build();
String answer = chatModel.chat(request).aiMessage().text();响应元数据
每个 watsonx.ai 聊天响应都携带一个 WatsonxChatResponseMetadata。它的三个字段只由 Model Gateway 填充,对其他服务保持 null:
java
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.watsonx.WatsonxChatResponseMetadata;
ChatResponse response = chatModel.chat(request);
var metadata = (WatsonxChatResponseMetadata) response.metadata();
metadata.serviceTier(); // service tier that served the request (gateway only)
metadata.systemFingerprint(); // provider system fingerprint (gateway only)
metadata.cached(); // whether the response was served from cache (gateway only)内联审核
WatsonxChatModel 与 WatsonxStreamingChatModel 能在答案生成的同时筛查聊天请求的输入与输出,而无需额外调用任何审核服务。筛查通过 moderations(...) builder 方法启用。
java
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxChatModel;
import com.ibm.watsonx.ai.CloudRegion;
import com.ibm.watsonx.ai.chat.ChatModeration;
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.moderations(
ChatModeration.builder()
.pii(pii -> pii.input(true).output(true))
.hap(hap -> hap.output(0.8f))
.graniteGuardian(guardian -> guardian.input(0.85f))
.build()
)
.build();有三个检测器可用。pii 与 hap 可在输入、输出或两者上启用,而 graniteGuardian 只筛查输入。
| 检测器 | 用途 | 通过何者启用 |
|---|---|---|
pii | 检测个人数据,例如电话号码、邮箱与信用卡。 | .pii(pii -> pii.input(true).output(true)) |
hap | 检测高于某置信度阈值的仇恨与脏话。 | .hap(hap -> hap.input(0.8f).output(0.9f)) |
graniteGuardian | 通用的有害内容分类器。 | .graniteGuardian(guardian -> guardian.input(0.85f)) |
inputRanges(...) 会把对输入的筛查收窄到给定范围,其中起点含、终点不含。
java
import com.ibm.watsonx.ai.chat.ChatModeration.InputRanges;
ChatModeration.builder()
.pii(pii -> pii.input(true))
.inputRanges(List.of(InputRanges.of(0, 50), InputRanges.of(100, 150)))
.build();读取匹配项
匹配项会在响应的 WatsonxChatResponseMetadata 上上报。
java
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.watsonx.WatsonxChatResponseMetadata;
ChatResponse response = chatModel.chat(request);
var metadata = (WatsonxChatResponseMetadata) response.metadata();
metadata.moderations(); // matches keyed by detector name, "pii", "hap" or "granite_guardian"
metadata.detections(); // Granite Guardian detections keyed by position, "input" or "output"当审核被禁用、或没有任何检测器匹配时,两个 map 都为 null。每个匹配都会说明它来自输入还是输出、检测器赋给的分数、被识别出的实体类型,以及越界词在文本中的位置。用 WatsonxStreamingChatModel 时,每个块的匹配会被合并,并在传给 onCompleteResponse(...) 的响应元数据上上报。
| 字段 | 类型 | 描述 |
|---|---|---|
score() | float | 匹配的置信度。 |
input() | boolean | 匹配来自输入时为 true,来自输出时为 false。 |
position() | Position | 匹配在文本内的起始(含)与结束(不含)偏移。 |
entity() | String | 被识别实体的类型,例如 PhoneNumber。 |
word() | String | 匹配到的文本。 |
该服务上报输出的匹配却从不改写答案,因此对文本的脱敏交由调用方完成,调用方可利用每个匹配的偏移。每个检测器也接受 mask(true),请求服务移除被检测实体的值,而聊天服务的答案仍原样返回。
java
import com.ibm.watsonx.ai.chat.TextChatResponse.ModerationResult;
static String mask(String text, ModerationResult result) {
var position = result.position();
return text.substring(0, position.start())
+ "*".repeat(position.end() - position.start())
+ text.substring(position.end());
}输入被检测器拦截
匹配某个检测器的输入会阻断生成。LangChain4j 会把它报告为一个 ContentFilteredException,其 cause 是 SDK 抛出的 ModerationException,因此每个触发拦截的检测器的匹配仍可访问。
java
import com.ibm.watsonx.ai.chat.exception.ModerationException;
import dev.langchain4j.exception.ContentFilteredException;
try {
chatModel.chat(request);
} catch (ContentFilteredException e) {
var cause = (ModerationException) e.getCause();
System.out.println(cause.moderations());
}在 WatsonxStreamingChatModel 上,同一个异常会被投递到 onError(...)。
单个请求的审核
WatsonxChatRequestParameters 携带同样的设置,优先于在 builder 上配置的那个。
java
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.watsonx.WatsonxChatRequestParameters;
ChatRequest request = ChatRequest.builder()
.messages(UserMessage.from("Contact me at john@example.com."))
.parameters(
WatsonxChatRequestParameters.builder()
.moderations(ChatModeration.builder().pii(pii -> pii.output(true)).build())
.build()
).build();注: 内联审核只在基础模型上可用。
WatsonxDeploymentChatModel与WatsonxDeploymentStreamingChatModel会以UnsupportedFeatureException拒绝它,而 Model Gateway 不支持它。要在聊天流之外筛查一段文本,请使用 WatsonxModerationModel。
🔗 SDK 内容审核,了解每个检测器选项的完整列表。
工具集成
所有 watsonx.ai 聊天模型——WatsonxChatModel、WatsonxStreamingChatModel、WatsonxDeploymentChatModel、WatsonxDeploymentStreamingChatModel、WatsonxGatewayChatModel 与 WatsonxGatewayStreamingChatModel——都支持 LangChain4j 工具,让模型能调用用 @Tool 注解的 Java 方法。
这里是一个使用同步模型(WatsonxChatModel)的示例,但同样的方式适用于流式以及部署/网关变体。
java
static class Tools {
@Tool
LocalDate currentDate() {
return LocalDate.now();
}
@Tool
LocalTime currentTime() {
return LocalTime.now();
}
}
interface AiService {
String chat(String userMessage);
}
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("mistralai/mistral-small-3-1-24b-instruct-2503")
.maxOutputTokens(0)
.build();
AiService aiService = AiServices.builder(AiService.class)
.chatModel(chatModel)
.tools(new Tools())
.build();
String answer = aiService.chat("What is the date today?");
System.out.println(answer);注: 请确保你所选的模型支持工具使用。
结构化输出
所有 watsonx.ai 聊天模型都能把响应约束到一个 JSON Schema。通用的 LangChain4j 文档在这里,而本节描述 watsonx.ai 专有的行为。
strictJsonSchema(...) builder 方法控制 schema 如何发送给服务,默认为 true。在严格模式下,模型被要求遵循该 schema,每个属性都被标记为 required,additionalProperties 被设为 false,而未被列入 required 的属性会被置为可空。
java
import static dev.langchain4j.model.chat.Capability.RESPONSE_FORMAT_JSON_SCHEMA;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.watsonx.WatsonxChatModel;
import com.ibm.watsonx.ai.CloudRegion;
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA)
.strictJsonSchema(true) // default value
.build();使用
strictJsonSchema(false)会把 schema 作为提示而非约束发送。模型仍会尝试产生一个遵循该 schema 的响应,但当响应偏离它时请求不会失败,required 列表按声明发送,且additionalProperties被略去。当可选字段必须保持可选时,就该用这个模式。
只有当模型通过 AI 服务使用、JSON Schema 由 AI 服务方法的返回类型生成时,才需要
supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA)。
JSON Schema 的根元素必须是一个 JsonObjectSchema 或一个 JsonRawSchema。任何其他根元素都会让请求以 IllegalArgumentException 失败。
启用思考 / 推理输出
一些基础模型能把内部推理(也被称为思考)步骤作为其响应的一部分包含进来。取决于模型,这种推理可能与最终响应的文本内嵌在一起,也可能在 watsonx.ai 的一个专有的字段中单独返回。
要正确地启用并捕获这一行为,你必须根据模型的输出格式配置 thinking(...) builder 方法。这确保 LangChain4j 能从模型输出中自动抽取推理与响应内容。
主要有两种配置模式:
ExtractionTags→ 用于把推理与响应在同一文本块中返回的模型(例如 ibm/granite-3-3-8b-instruct)。ThinkingEffort→ 用于已自动分离推理与响应的模型(例如 openai/gpt-oss-120b)。
thinking(...)builder 方法在WatsonxChatModel、WatsonxStreamingChatModel、WatsonxDeploymentChatModel与WatsonxDeploymentStreamingChatModel上可用。Model Gateway 不接受它,因此在网关模型上请改用reasoningEffort(...)。
一起返回推理与响应的模型
当模型在同一文本字符串中输出推理与响应时,使用 ExtractionTags。这些标签定义了用于把推理与最终响应分离的类 XML 标记。
示例标签:
- 推理标签:
<think>—— 包含模型的内部推理。 - 响应标签:
<response>—— 包含面向用户的答案。
行为
- 若两个标签都指定,就直接用它们抽取推理与响应片段。
- 若只指定推理标签,该标签之外的一切都视为响应。
ibm/granite-3-3-8b-instruct 的示例
java
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-3-3-8b-instruct")
.maxOutputTokens(0)
.thinking(ExtractionTags.of("think", "response"))
.build();
ChatResponse chatResponse = chatModel.chat(
UserMessage.userMessage("Why is the sky blue?")
);
AiMessage aiMessage = chatResponse.aiMessage();
System.out.println(aiMessage.thinking());
System.out.println(aiMessage.text());分开返回推理与响应的模型。
对于已经把推理与响应作为独立字段返回的模型,使用 ThinkingEffort 来控制模型在生成时投入多少推理。或者,用一个布尔标志启用它。
openai/gpt-oss-120b 的示例
java
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.DALLAS)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("openai/gpt-oss-120b")
.thinking(ThinkingEffort.HIGH)
.build();or
java
ChatModel chatModel = WatsonxChatModel.builder()
.baseUrl(CloudRegion.DALLAS)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("openai/gpt-oss-120b")
.thinking(true)
.build();流式示例
java
StreamingChatModel model = WatsonxStreamingChatModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-3-3-8b-instruct")
.thinking(ExtractionTags.of("think", "response"))
.build();
List<ChatMessage> messages = List.of(
UserMessage.userMessage("Why is the sky blue?")
);
ChatRequest chatRequest = ChatRequest.builder()
.messages(messages)
.build();
model.chat(chatRequest, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
...
}
@Override
public void onPartialThinking(PartialThinking partialThinking) {
...
}
});注:
- 请确保所选模型支持推理输出。
- 对把推理与响应嵌在同一文本字符串中的模型,使用
ExtractionTags。- 对已自动分离推理与响应的模型,使用
ThinkingEffort或thinking(true)。
WatsonxModelCatalog
WatsonxModelCatalog 提供了一种以编程方式发现并列出 IBM watsonx.ai 上所有可用基础模型的途径。它实现了 LangChain4j 的 ModelCatalog 接口,让你能检索每个模型的详细信息。
Example
java
import dev.langchain4j.model.catalog.ModelCatalog;
import dev.langchain4j.model.catalog.ModelDescription;
import dev.langchain4j.model.watsonx.WatsonxModelCatalog;
import com.ibm.watsonx.ai.CloudRegion;
ModelCatalog modelCatalog = WatsonxModelCatalog.builder()
.baseUrl(CloudRegion.FRANKFURT)
.build();
var models = modelCatalog.listModels();WatsonxGatewayModelCatalog
WatsonxGatewayModelCatalog 是 WatsonxModelCatalog 对应的 Model Gateway 版本。它不列出 watsonx.ai 托管的基础模型,而是列出网关中配置的、跨其注册的所有提供者聚合而来的模型。它也实现了 LangChain4j 的 ModelCatalog 接口。
每个返回的 ModelDescription 的 name(),就是要传给 WatsonxGatewayChatModel.modelName(...) 与 WatsonxGatewayStreamingChatModel.modelName(...) 的标识符。当网关管理员定义了别名时,它是模型的别名,否则是提供者侧的模型 id。
Example
java
import dev.langchain4j.model.catalog.ModelCatalog;
import dev.langchain4j.model.catalog.ModelDescription;
import dev.langchain4j.model.watsonx.WatsonxGatewayModelCatalog;
import com.ibm.watsonx.ai.CloudRegion;
ModelCatalog modelCatalog = WatsonxGatewayModelCatalog.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.build();
for (ModelDescription model : modelCatalog.listModels()) {
System.out.println(model.name() + " (" + model.owner() + ")");
}
// → gpt-4o (openai)
// → claude-3-5-sonnet (anthropic)网关模型如何映射
ModelDescription | 网关字段 | 备注 |
|---|---|---|
name() | alias,无别名时为 id | 与网关聊天模型一起使用的 id |
displayName() | 与 name() 相同 | 网关没有单独的名称标签 |
description() | description | 用户定义,除非管理员设置了否则为 null |
owner() | owned_by | 提供者,例如 openai |
createdAt() | created | 网关配置的 Unix 时间戳,而非模型发布时间 |
type() | - | 始终为 ModelType.CHAT,因为网关不暴露模型能力 |
maxInputTokens() | metadata.context_window | 当管理员未配置 metadata 时为 null |
maxOutputTokens() | - | 始终为 null,网关不返回它 |
WatsonxTokenCountEstimator
WatsonxTokenCountEstimator 通过调用 watsonx.ai 的分词端点实现了 LangChain4j 的 TokenCountEstimator 接口,因此计数来自模型自身的分词器,而非本地近似。正因如此,modelName(...) 是必填的。
Example
java
import dev.langchain4j.model.TokenCountEstimator;
import dev.langchain4j.model.watsonx.WatsonxTokenCountEstimator;
import com.ibm.watsonx.ai.CloudRegion;
TokenCountEstimator tokenCountEstimator = WatsonxTokenCountEstimator.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.modelName("ibm/granite-4-h-small")
.build();
int tokenCount = tokenCountEstimator.estimateTokenCountInText("Hello from watsonx.ai");注: 每次估算都是一次远程调用。
estimateTokenCountInMessage(...)也会统计一个AiMessage的思考文本与工具执行请求。不支持图像、音频、PDF 与视频内容。
🔗 SDK 分词服务
WatsonxModerationModel
WatsonxModerationModel 使用 IBM watsonx.ai 提供了 ModerationModel 接口的 LangChain4j 实现。它让你能通过检测器自动检测并标记文本中敏感、不安全或违反策略的内容。
可以用一个或多个检测器来识别不同类型的内容,例如:
- Pii – 检测个人身份信息(例如邮箱、电话号码)
- Hap – 检测仇恨、辱骂或脏话
- GraniteGuardian – 检测有风险或有害的语言
Example
java
ModerationModel model = WatsonxModerationModel.builder()
.baseUrl(CloudRegion.FRANKFURT)
.apiKey("your-api-key")
.projectId("your-project-id")
.detectors(Hap.ofDefaults(), GraniteGuardian.ofDefaults())
.build();
Response<Moderation> response = model.moderate("...");元数据
每次审核响应都包含一个 metadata map,提供关于检测的额外上下文。
| 键 | 描述 |
|---|---|
detection | 检测器赋给的被检测标签或类别 |
detection_type | 触发标记的检测器类型 |
start | 被检测片段的起始字符索引 |
end | 被检测片段的结束字符索引 |
score | 检测的置信度分数 |
这些元数据值可通过 Response.metadata() 获取:
java
Map<String, Object> metadata = response.metadata();
System.out.println("Detection type: " + metadata.get("detection_type"));
System.out.println("Score: " + metadata.get("score"));🔗 SDK 检测服务,了解检测器及其选项的完整列表。
通过环境变量配置
底层 SDK 的内部 HTTP 行为可以通过环境变量自定义,无需任何代码改动。这些设置是可选的,未显式定义变量时会使用合理的默认值。
重试配置
在发生瞬时故障或认证令牌过期时,HTTP 请求会被自动重试。重试行为可用以下环境变量自定义:
| 环境变量 | 描述 | 默认值 |
|---|---|---|
WATSONX_RETRY_TOKEN_EXPIRED_MAX_RETRIES | 认证令牌过期(HTTP 401 / 403)时的最大重试次数 | 1 |
WATSONX_RETRY_STATUS_CODES_MAX_RETRIES | 瞬时 HTTP 状态码(429、503、504、520)的最大重试次数 | 10 |
WATSONX_RETRY_STATUS_CODES_BACKOFF_ENABLED | 为瞬时重试启用指数退避 | true |
WATSONX_RETRY_STATUS_CODES_INITIAL_INTERVAL_MS | 初始重试间隔(毫秒,作为指数退避的基数) | 20 |
HTTP IO 执行器配置
流式响应与 HTTP 响应处理由一个内部 IO 执行器处理。默认在 Java 21+ 上使用虚拟线程,在 Java 17–20 上使用一个缓存线程池。
该行为可通过以下环境变量自定义:
| 环境变量 | 描述 | 默认值 |
|---|---|---|
WATSONX_IO_EXECUTOR_THREADS | 把 IO 执行器上限设为固定大小、此线程数的线程池 | 未设置 |
错误处理
SDK 抛出的 watsonx.ai 错误会被翻译为标准的 LangChain4j 异常,因此你可以像对待任何其他提供者那样处理它们。该映射由 watsonx.ai 返回的错误码驱动:
| watsonx.ai 错误码 | LangChain4j 异常 |
|---|---|
authentication_token_expired、authorization_rejected | AuthenticationException |
invalid_input_argument、invalid_request_entity、json_type_error、json_validation_error | InvalidRequestException |
model_not_supported | ModelNotFoundException |
token_quota_reached | RateLimitException |
| 任何其他错误码 | LangChain4jException |
当响应不含错误体时,异常改由 HTTP 状态码选取。一个超出其 timeout(...) 的请求会被报告为 TimeoutException。一个拒绝作答的模型,以及一个被内联审核拦截的输入,会被报告为 ContentFilteredException。
java
try {
String answer = chatModel.chat("Hello from watsonx.ai");
} catch (RateLimitException e) {
// token quota reached
} catch (InvalidRequestException e) {
// a request parameter was rejected by watsonx.ai
}🔗 SDK 异常层级,了解底层的
WatsonxException及其statusCode()、errorCode()与traceId(),可通过getCause()获取。
Quarkus
更多细节见这里。