☰
openJiuwen Java 的 InferenceAffinityModel:面向 vLLM 算力亲和与 KV Cache 复用的统一模型调用入口
2026/10/14 16:40:06 网站建设 项目流程
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

InferenceAffinityModel是 openJiuwen agent-core-java(com.openjiuwen.core.foundation.llm包)中专用于InferenceAffinity(vLLM)推理服务的统一会话调用入口。它的核心职责是把sessionId与缓存共享开关(enableCacheSharing)安全、一致地透传给底层InferenceAffinityModelClient,从而让多轮对话的 KV Cache 能在服务端按会话复用,并支持主动释放已缓存的上下文。读完本文,你将掌握:该类的字段与构造约束、invoke/stream/release三个方法族的行为与参数语义、session 与缓存参数的归一化规则,以及它与KVCacheManager、模型客户端工厂在源码层面的协作机制,并可直接照抄文中的配置与调用示例接入真实项目。

类定位:统一会话入口,而不是又一个 HTTP 客户端

InferenceAffinityModel位于 core/src/main/java/com/openjiuwen/core/foundation/llm/InferenceAffinityModel.java,类声明如下:

public class InferenceAffinityModel implements KVCacheManager.ReleaseCapableModel

从源码注释可以确认,该类对应 Python 版openjiuwen/core/foundation/llm/inference_affinity_model.py中的同名实现,是 Java 侧的对齐移植。它本身不直接实现 HTTP 协议,而是扮演两个角色:

  • 统一入口:把messages、tools、采样参数以及sessionId、enableCacheSharing打包后透传给实际的模型客户端;
  • KV Cache 释放能力的载体:实现KVCacheManager.ReleaseCapableModel接口,使上下文引擎能够在窗口变化时回调release(...)释放旧缓存。

API 文档中对该类的定位描述是"统一 InferenceAffinity 会话调用入口,向InferenceAffinityModelClient透传 session 与缓存共享参数",这与源码实现完全一致。要理解它,建议先区分三张"脸":

组件文件职责
InferenceAffinityModelInferenceAffinityModel.java面向业务代码的统一入口,做参数归一化与校验
InferenceAffinityModelClientmodelclients/InferenceAffinityModelClient.java真正发 HTTP 请求的客户端,实现 cache sharing 与 release
InferenceAffinityModelClientFactorymodelclients/InferenceAffinityModelClientFactory.java按 provider 名称创建客户端实例的工厂

核心字段与构造方法

三个字段

文档列出的私有字段与源码一一对应:

字段类型说明
modelConfigModelRequestConfig本次请求的模型配置(模型名、采样参数默认值等)
modelClientConfigModelClientConfig连接层配置(provider、apiBase、apiKey、超时、重试等)
clientInferenceAffinityModelClient(内部以InferenceAffinityClient接口持有)当前 provider 对应的客户端实例

注意文档表格中字段类型写为InferenceAffinityModelClient,源码中实际以窄接口InferenceAffinityModel.InferenceAffinityClient持有(见下文"反射适配"小节),但语义不变:它就是承载真实调用逻辑的客户端对象。

构造方法

public InferenceAffinityModel(ModelClientConfig modelClientConfig, ModelRequestConfig modelConfig)

构造时会把两份配置分别保存,并通过createDefaultClient(modelConfig, modelClientConfig)创建默认客户端。源码中对校验逻辑有明确约束(构造器会先做非空校验):

  • modelClientConfig == null时抛出MODEL_SERVICE_CONFIG_ERROR,错误信息为model client config is none;
  • 客户端创建失败时抛出MODEL_SERVICE_CONFIG_ERROR,错误信息为InferenceAffinityModelClient translation is not available或failed to create InferenceAffinityModelClient: ...。

也就是说,连接配置缺失或客户端不可用时,构造阶段就会直接失败,而不是等到首次调用才报错。测试 InferenceAffinityModelTest.java 中constructorRejectsMissingClientConfigLikePython用例验证了这一行为:传入null的ModelClientConfig会抛出包含model client config is none的BaseError。

两层配置从哪来

两个配置类分别解决"怎么连"与"怎么调":

  • ModelClientConfig(schema/ModelClientConfig.java)描述连接行为,常用字段包括clientProvider(provider 名,支持ProviderType枚举或字符串)、apiBase、apiKey、timeout(默认60.0秒,必须大于 0)、maxRetries(默认3)、verifySsl(默认true)、sslCert、headers/customHeaders、clientId(默认自动生成 UUID)。
  • ModelRequestConfig(schema/ModelRequestConfig.java)描述单次请求参数,包括modelName(默认空字符串,接入时务必显式设置)、temperature(默认0.95)、topP(默认0.1)、maxTokens、stop、user、seed、extraFields。

关于 provider 名称:ProviderType.INFERENCE_AFFINITY("InferenceAffinity", "InferenceAffinity")是内置 provider,工厂注册同时覆盖InferenceAffinity与inference_affinity两个名称(见 modelclients/DefaultModelClientFactories.java),在 接入大模型.md 中也被列入开箱即用的内置 provider 列表。

方法详解

配置访问器

public ModelRequestConfig getModelConfig() public ModelClientConfig getModelClientConfig()

返回构造时保存的两份配置对象,便于上层读取模型名、采样参数与连接信息。

invoke:发起同步模型调用

API 文档给出的签名(文档为表述简化的throws Exception风格)在实际源码中是响应式风格:

public CompletionStage<AssistantMessage> invoke(Object messages, List<?> tools, Float temperature, Float topP, Integer maxTokens, String stop, String model, BaseOutputParser outputParser, String sessionId, boolean enableCacheSharing, Map<String, Object> kwargs)

参数语义如下:

参数说明
messages模型输入,支持String、List<BaseMessage>、List<Map<String, Object>>等形态
tools工具定义列表(List<ToolInfo>或List<Map<String, Object>>)
temperature/topP/maxTokens/stop/model单次调用覆盖默认配置的采样参数;model为空时回退到ModelRequestConfig.modelName
outputParser输出解析器,解析结果写入返回的AssistantMessage.parserContent
sessionId会话标识,用于 KV Cache 复用,透传到底层请求
enableCacheSharing缓存共享开关,开启后请求会携带cache_sharing与cache_salt
kwargs额外请求参数,会与 session 参数合并后一并透传

该方法内部直接委托client.invoke(...),并在透传前调用normalizeKwargs把 session 参数并入 kwargs:

private static Map<String, Object> normalizeKwargs(String sessionId, boolean enableCacheSharing, Map<String, Object> kwargs) { Map<String, Object> options = kwargs == null ? new LinkedHashMap<>() : new LinkedHashMap<>(kwargs); if (sessionId != null) { options.put("session_id", sessionId); } options.put("enable_cache_sharing", enableCacheSharing); return options; }

也就是说,即使调用方不传 kwargs,session_id与enable_cache_sharing也一定会被写入透传参数——这正是文档所说"透传 session 与缓存共享参数"的落地实现。测试invokeDelegatesAllOptionsAndCacheKwargsToClient验证了这一点:调用方传入的kwargs(如tracer_record_data)与自动注入的session_id、enable_cache_sharing会同时出现在透传给客户端的参数中。

stream:发起流式模型调用

public Iterator<AssistantMessageChunk> stream(Object messages, List<?> tools, Float temperature, Float topP, Integer maxTokens, String stop, String model, BaseOutputParser outputParser, String sessionId, boolean enableCacheSharing, Map<String, Object> kwargs)

与invoke参数完全一致,返回同步Iterator<AssistantMessageChunk>,调用方逐块next()消费即可。每个 chunk 可包含content(文本增量)、reasoningContent、toolCalls、usageMetadata、finishReason、parserContent字段;流式场景下finishReason中间过程通常为"null",结束时才变为stop/tool_calls。底层同样经过normalizeKwargs归一化。

此外invoke与stream各提供一个仅传messages的便捷重载,其余参数全部置null/false,适合快速单轮调用。

release:释放会话与已缓存上下文

public CompletionStage<Boolean> release(String sessionId, List<?> messages, Integer messagesReleasedIndex, List<?> tools, Integer toolsReleasedIndex, String model)
  • messagesReleasedIndex为空时按0处理(releasedIndex = messagesReleasedIndex == null ? 0 : messagesReleasedIndex);
  • 真正释放逻辑委托给客户端的release(...),请求会发往服务端的/release_kv_cache端点;
  • 返回CompletionStage<Boolean>,HTTP 200 返回true,其余状态返回false。

release同时是KVCacheManager.ReleaseCapableModel接口的实现(该接口签名为release(String sessionId, List<BaseMessage> messages, Integer messagesReleasedIndex, List<ToolInfo> tools, Integer toolsReleasedIndex)),因此它可以被上下文引擎在窗口变化时回调,实现"差异释放"。

静态 KV Cache 辅助方法

public static boolean supportsKvCacheRelease() // 恒为 true public static Map<String, Object> buildKvCacheInvokeKwargs(Object session, boolean enableKvCacheRelease)

buildKvCacheInvokeKwargs会从 session 对象解析会话 ID:优先反射调用getSessionId(),找不到再尝试 Python 风格的get_session_id(),两者都不存在则跳过session_id。开启缓存释放时还会写入enable_cache_sharing=true。这是给KVCacheManager等上层组件拼装调用参数用的工具方法。

底层原理:反射适配与客户端 HTTP 协议

为什么要反射?

InferenceAffinityModel通过CLIENT_CLASS_NAME = "com.openjiuwen.core.foundation.llm.modelclients.InferenceAffinityModelClient"在运行时用Class.forName(...)加载客户端,并包装成内部类ReflectiveInferenceAffinityClient。这样做的意义在于:InferenceAffinityModel不硬依赖具体客户端类,客户端实现缺失时只在构造阶段抛出可读的错误信息,而不是在类加载阶段就失败。反射适配层按参数个数查找invoke(10 参)、stream(10 参)、release(6 参)方法,并把返回结果规整为CompletionStage。

InferenceAffinityClient窄接口定义了三个透传方法(invoke/stream/release),测试中的RecordingClient正是通过实现该接口来记录调用参数,这从侧面说明业务代码与真实 HTTP 客户端之间只隔着这层窄接口。

InferenceAffinityModelClient 做了什么

InferenceAffinityModelClient.java 继承BaseModelClient,在静态块中调用registerClientClass(...)完成自注册,CLIENT_NAME等于ProviderType.INFERENCE_AFFINITY.getValue()。它的核心行为包括:

  1. 请求构造与工具调用净化(buildAndSanitizeParams):调用buildRequestParams组装 OpenAI 兼容请求体;随后对assistant角色的tool_calls做净化(只保留id、type="function"、index以及function.name/function.arguments字符串,丢弃多余字段,避免服务端解析失败)。
  2. 缓存共享参数:当enableCacheSharing && sessionId非空时,在请求体顶层写入:
params.put("cache_sharing", true); params.put("cache_salt", sessionId);
  1. 非流式调用:从 kwargs 中剥离tracer_record_data、session_id、enable_cache_sharing三个控制字段后,POST 到<apiBase>/v1/chat/completions;按ModelClientConfig.maxRetries进行指数退避重试(每次失败后等待2^attempt秒),收到非 200 响应则抛出IOException。解析响应时支持choices[0].message的content、reasoning_content、tool_calls,以及usage.prompt_tokens_details.cached_tokens(计入UsageMetadata.cacheTokens),finishReason在无工具调用时为stop,有工具调用时为tool_calls。
  2. 流式调用:POST 同样的端点,逐行解析data:前缀的 SSE 块,[DONE]结束;若配置了outputParser,则走streamWithParser边累积边解析。
  3. 缓存释放:构造releaseParams(model、cache_salt=sessionId、cache_sharing=true、messages、messages_released_index,以及可选的tools/tools_released_index),POST 到/release_kv_cache,200 即视为成功。
  4. 图像 / 语音 / 视频生成接口(generateImage/generateSpeech/generateVideo)在该客户端中返回null,说明此客户端专注对话与缓存管理,不承担多模态生成。

测试 InferenceAffinityModelClientTest.java 用录制式客户端验证了cache_sharing/cache_salt的注入、tool_calls 净化结果,以及prompt_tokens_details.cached_tokens的解析。

与 KVCacheManager 的协作:差异释放机制

InferenceAffinityModel的价值不止于"能带 session 调用",更在于它可以被 KVCacheManager.java 驱动做按需释放。协作流程(对应 API 文档 KVCacheManager.md):

  1. KVCacheManager记录上一次发给推理端的ContextWindow;首次调用只缓存窗口,不触发释放。
  2. 后续窗口变化时,checkReleaseNeeded/firstChangedDecision分别比较消息列表与工具列表的首个差异索引,得到ReleaseDecision(shouldRelease, messagesReleasedIndex, toolsReleasedIndex)。
  3. 若判定需要释放,KVCacheManager检查模型是否可释放:model instanceof ReleaseCapableModel或存在release(String, List, Integer, List, Integer)方法。InferenceAffinityModel实现了该接口,于是被回调release(sessionId, lastContextWindow.getMessages(), messagesReleasedIndex, tools, toolsReleasedIndex)。
  4. 释放失败不阻塞主流程,只记录 warning 日志。

一句话总结协作关系:KVCacheManager负责"何时释放"的差异检测,InferenceAffinityModel负责"怎么释放"的协议调用。

接入示例:配置、调用与释放

1. 构造模型

import com.openjiuwen.core.foundation.llm.InferenceAffinityModel; import com.openjiuwen.core.foundation.llm.schema.ModelClientConfig; import com.openjiuwen.core.foundation.llm.schema.ModelRequestConfig; ModelClientConfig clientConfig = ModelClientConfig.builder() .clientProvider("InferenceAffinity") // 内置 provider,也可写 inference_affinity .apiKey("sk-xxx") .apiBase("http://vllm-gateway:8000") // 服务根地址,不要拼 /chat/completions .timeout(60.0) .maxRetries(3) .verifySsl(true) .build(); ModelRequestConfig requestConfig = ModelRequestConfig.builder() .modelName("your-vllm-model") .temperature(0.6) .topP(0.8) .maxTokens(256) .build(); InferenceAffinityModel model = new InferenceAffinityModel(clientConfig, requestConfig);

2. 携带 session 的非流式调用

import com.openjiuwen.core.foundation.llm.schema.AssistantMessage; import com.openjiuwen.core.foundation.llm.schema.SystemMessage; import com.openjiuwen.core.foundation.llm.schema.UserMessage; import java.util.List; import java.util.Map; List<Object> messages = List.of( new SystemMessage("你是一个简洁的助手。"), new UserMessage("你好,请记住会话 ID 对应的上下文。") ); AssistantMessage response = model.invoke( messages, // messages null, // tools null, // temperature(不覆盖默认) null, // topP null, // maxTokens null, // stop null, // model(回退 requestConfig.modelName) null, // outputParser "session-1", // sessionId:KV Cache 复用的关键 true, // enableCacheSharing:开启缓存共享 Map.of("custom", "value") // kwargs:额外参数 ).toCompletableFuture().join(); System.out.println(response.getContentAsString());

开启enableCacheSharing=true后,请求体会携带cache_sharing: true与cache_salt: "session-1",服务端据此把该会话的 KV Cache 组织为可复用的缓存条目。

3. 流式调用

import com.openjiuwen.core.foundation.llm.schema.AssistantMessageChunk; import java.util.Iterator; Iterator<AssistantMessageChunk> chunks = model.stream( messages, null, null, null, null, null, null, null, "session-1", true, null); StringBuilder fullText = new StringBuilder(); while (chunks.hasNext()) { AssistantMessageChunk chunk = chunks.next(); String delta = chunk.getContentAsString(); if (delta != null && !delta.isEmpty()) { System.out.print(delta); fullText.append(delta); } }

4. 主动释放缓存

Boolean released = model.release( "session-1", // sessionId messages, // 会话消息 1, // messagesReleasedIndex:释放起点 null, // tools null, // toolsReleasedIndex null // model:为空时回退 requestConfig.modelName ).toCompletableFuture().join();

release会向/release_kv_cache发送包含cache_salt、messages_released_index的请求,让服务端释放该会话已缓存但不再需要的 KV Cache 前缀,为后续轮次腾出显存。测试releaseImplementsKvCacheManagerPort验证了 sessionId、messages、释放索引等参数的正确透传。

使用边界与注意事项

  • provider 名称兼容:ModelClientConfig.clientProvider支持大小写兼容匹配,但建议按内置名称原样填写InferenceAffinity或inference_affinity;写错会在构造阶段报 provider 不可用(MODEL_PROVIDER_INVALID)。
  • 客户端能力边界:InferenceAffinityModelClient的多模态生成方法返回null,本模型入口聚焦对话与 KV Cache 管理,不要用它承接图像 / 语音 / 视频生成。
  • 与 Ascend-affinity 互斥:仓库文档 Ascend算力亲和.md 明确指出,Ascend-affinity 与 InferenceAffinity 的 release 模式互斥:affinity 客户端的supportsKvCacheRelease()恒为 false,二者不可同时启用。需要设备亲和调度时应走 affinity 链路(extensions.kv_cache.mode=affinity+enableKvCacheAffinity),需要 vLLM 会话级缓存复用与释放时走本文的 InferenceAffinity 链路。
  • 日志观测:请求发出与响应接收均有Loggers.LLM日志(如InferenceAffinity API response received、Before release KV cache, release request params ready),排障时可先 grep 这些关键字确认请求是否真正发出、参数是否正确。
  • 签名以源码为准:文档说明"所有签名均以当前 Java 源码为准",上文签名均取自 InferenceAffinityModel.java 与 InferenceAffinityModelClient.java;invoke/stream/release实际返回CompletionStage,调用时注意通过.toCompletableFuture().join()或异步编排消费结果。

源码阅读地图

顺序文件建议关注点
1InferenceAffinityModel.java构造校验、normalizeKwargs、反射适配、ReleaseCapableModel实现
2InferenceAffinityModelClient.javacache_sharing/cache_salt注入、tool_calls 净化、/release_kv_cache协议
3InferenceAffinityModelClientFactory.java 与 DefaultModelClientFactories.javaprovider 工厂注册机制
4KVCacheManager.java窗口差异检测与释放回调
5InferenceAffinityModelTest.java 与 InferenceAffinityModelClientTest.java参数透传与协议构造的行为参照

相关文档可继续阅读:接入大模型.md(两层配置与 provider 列表)、KVCacheManager.md(差异释放语义)、Ascend算力亲和.md(affinity 链路与互斥边界)。

  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询