- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
本文围绕 agent-core-java 仓库中的 InferenceAffinityModelClient 展开:这是一个面向 InferenceAffinity 风格(vLLM)推理服务的模型客户端,在标准 OpenAI 兼容/v1/chat/completions调用之上,额外提供cache_sharing(基于session_id的会话级 KV Cache 共享)与release(主动释放已缓存上下文)两项能力。读完本文,你能掌握该客户端的配置方式、请求参数组装链路、/release_kv_cache释放协议,以及它与InferenceAffinityModel、KVCacheManager之间从源码到测试的完整调用关系。
客户端定位:InferenceAffinity 风格的会话级缓存客户端
InferenceAffinityModelClient继承自 BaseModelClient,位于com.openjiuwen.core.foundation.llm.modelclients包中,其源码 Javadoc 明确说明它“mirrors Python'sInferenceAffinityModelClient”(参见 InferenceAffinityModelClient.java)。与仓库中其他 OpenAI 兼容客户端相比,它的差异化能力集中在三点:
- 会话级 KV Cache 共享:在请求体中附带
cache_sharing: true与cache_salt: <sessionId>,让服务端按会话维度复用 KV Cache; - 缓存主动释放:通过私有端点
POST /release_kv_cache释放指定会话、指定窗口之后的缓存; - 能力自声明:覆写
supportsKvCacheRelease()返回true(源码 L303-L306),使上层的 KV Cache 管理组件知道可以对该客户端执行释放操作。
从源码结构看,它只实现了两类请求路径,均写入两个类级常量(L53-L54):
| 常量 | 路径 | 用途 |
|---|---|---|
CHAT_COMPLETIONS_PATH | /v1/chat/completions | 同步invoke与流式stream的补全端点 |
RELEASE_KV_CACHE_PATH | /release_kv_cache | 释放会话已缓存上下文的私有端点 |
图像、语音、视频生成(generateImage/generateSpeech/generateVideo)在该客户端中均直接返回null(L206-L242),即该 provider 面向的是纯文本对话补全场景,使用时无需考虑多模态生成路径。
构造方法、注册机制与两层配置
两个构造入口
客户端提供两个构造入口(L63-L74):
public InferenceAffinityModelClient(ModelRequestConfig modelConfig, ModelClientConfig modelClientConfig) public InferenceAffinityModelClient(Map<String, Object> kwargs) // 从 kwargs 取 model_config / model_client_config主构造器先调用super(modelConfig, modelClientConfig)完成基类校验,再通过createHttpClient(modelClientConfig)基于配置构建 JDKHttpClient(L523-L527)。第二个kwargs构造器是供ClientRegistry反射实例化使用的入口,它从model_config与model_client_config两个键中取出配置对象后委托给主构造器。
此外,客户端在静态块中调用registerClientClass(InferenceAffinityModelClient.class)完成自注册(L59-L61),同时存在一个显式工厂 InferenceAffinityModelClientFactory,其create(...)方法等价于直接new InferenceAffinityModelClient(modelConfig, clientConfig)(见 工厂源码)。
provider 路由:InferenceAffinity是内置 provider
ProviderType枚举中定义了INFERENCE_AFFINITY("InferenceAffinity", "InferenceAffinity")(ProviderType.java L24)。在 ModelClients 的内置映射表中,InferenceAffinity被直接绑定到InferenceAffinityModelClient类名(L62)。因此通过ModelClients.createModelClient(clientConfig, modelConfig)创建客户端时,只要client_provider填写InferenceAffinity,就会走内置反射路径实例化该类,无需手动registerFactory。这一行为与 接入大模型 文档中列出的内置 provider 名单一致。
连接层配置:ModelClientConfig 关键参数
构造客户端所需的连接配置来自 ModelClientConfig,常用字段及其在本客户端中的实际作用如下:
| 字段 | 默认值 | 在本客户端中的作用 |
|---|---|---|
clientProvider | 无 | 必须为InferenceAffinity(大小写兼容匹配,建议按内置名称原样填写) |
apiKey | 无 | 必填。基类validateConfig()会校验api_key非空,否则抛出MODEL_SERVICE_CONFIG_ERROR(BaseModelClient.java L146-L160) |
apiBase | 无 | 必填。推理服务根地址,客户端会自动去掉末尾/后拼接路径(trimTrailingSlash,L650-L659) |
timeout | 60.0 | 请求超时(秒),必须大于 0;单次调用可通过invoke的timeout参数覆盖 |
maxRetries | 3 | 同步invoke的重试次数上限(语义见下文) |
verifySsl | true | 是否校验证书,自签证书环境可临时关闭 |
httpVersion | 无 | 指定HttpClient版本,如ModelHttpVersion.HTTP_1_1 |
一个最小可用配置示例(形态与测试用例 InferenceAffinityModelClientTest L232-L242 一致):
ModelRequestConfig requestConfig = ModelRequestConfig.builder() .modelName("qwen2-72b") .build(); ModelClientConfig clientConfig = ModelClientConfig.builder() .clientProvider(ProviderType.INFERENCE_AFFINITY) // 即 "InferenceAffinity" .apiKey("sk-test") .apiBase("http://localhost:8000") .verifySsl(false) .httpVersion(ModelHttpVersion.HTTP_1_1) .build(); InferenceAffinityModelClient client = new InferenceAffinityModelClient(requestConfig, clientConfig);同步 invoke:kwargs 剥离、cache_sharing 注入与响应解析
请求参数组装链路
invoke(messages, tools, temperature, topP, model, maxTokens, stop, outputParser, timeout, kwargs)的执行步骤如下(L115-L158):
- kwargs 副本与内部键剥离:复制
kwargs后移除tracer_record_data、session_id、enable_cache_sharing三个内部键,确保它们不会混入最终 HTTP 请求体——测试用例invokePostsChatCompletionAndParsesUsageToolsAndParserContent明确断言了lastPayload不包含这三个键(测试 L138)。 - 参数组装:
buildAndSanitizeParams(...)先调用基类buildRequestParams组装标准 OpenAI 兼容参数(model、messages、temperature、top_p、max_tokens、stream: false等),随后执行两件事(L81-L112):sanitizeToolCalls(messages):清洗历史 assistant 消息中的tool_calls,只保留id、type: "function"、index与function.name/arguments字段;- 当
enableCacheSharing == true且sessionId非空时,注入cache_sharing: true与cache_salt: sessionId。
- 发送与重试:
makeAsyncRequest(params, timeout)按maxRetries循环发送,每次失败后按2^attempt秒退避(L308-L329)。注意其语义与 OpenAI 客户端略有差异:这里maxRetries直接作为最大尝试总次数使用(for attempt < maxRetries),配置为 0 时一次都不发。 - 响应解析:
parseResponse从choices[0].message中解析content、reasoning_content、tool_calls与usage,并对 content 执行outputParser.parse(...)得到parserContent(L331-L353)。
响应字段映射
解析结果封装为AssistantMessage,字段映射关系(均有测试断言佐证,见 测试 L139-L150):
| 响应字段 | 映射目标 | 说明 |
|---|---|---|
choices[0].message.content | content | 正文内容,缺失时取空串 |
choices[0].message.reasoning_content | reasoningContent | 推理链内容(如存在) |
choices[0].message.tool_calls | toolCalls | 解析为ToolCall列表;非空时finishReason置为tool_calls,否则为stop |
usage.prompt_tokens / completion_tokens / total_tokens | usageMetadata | 输入 / 输出 / 总 token |
usage.prompt_tokens_details.cached_tokens | usageMetadata.cacheTokens | 仅同步invoke路径会读取(includeCacheTokens=true),用于观察 KV Cache 命中 |
cacheTokens正是 cache_sharing 是否生效的直接观测点:测试中prompt_tokens_details.cached_tokens: 5被断言映射到usageMetadata.getCacheTokens()(测试 L143)。
错误处理
调用链中所有异常最终收敛为StatusCode.MODEL_CALL_FAILED的BaseError,错误信息分别为InferenceAffinity API async invoke error: ...(同步)与InferenceAffinity API async stream error: ...(流式),便于在日志中按 provider 定位问题。
流式 stream:SSE 行解析与增量 parser
stream(...)与invoke的差别仅在于stream: true与 SSE 解析(L160-L204)。其内部流程:
postStream复用postJson拿到响应体后按行切分(L461-L465);parseStreamChunk(line)只处理data:前缀行,忽略data: [DONE]结束标记;对每条 chunk 解析choices[0].delta中的content、reasoning_content、tool_calls,以及顶层usage与finish_reason(L414-L453)。单条 chunk 解析失败(JSON 异常、字段缺失)不会中断流,只记录 warning 并跳过;- 若传入了
outputParser,则走streamWithParser:累积content,对累积串尝试增量解析,解析成功后把结果挂到该 chunk 的parserContent并清空累积器(L355-L392)。
测试streamParsesSseChunksAndAppliesIncrementalParser用三条 SSE 行验证了该行为(测试 L154-L188):
data: {"choices":[{"delta":{"content":"Hello"}}]} data: {"choices":[{"delta":{"content":" world"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":2,"total_tokens":3}} data: [DONE]断言点包括:请求体携带model: override-model、max_tokens: 32、stream: true;产出 2 个 chunk,第 2 个 chunk 的finishReason为stop且usageMetadata.totalTokens为 3。
release:KV Cache 释放协议
release(sessionId, messages, messagesReleasedIndex, tools, toolsReleasedIndex, model)是该客户端最具特色的方法(L244-L301),其协议细节:
- 请求体(
POST /release_kv_cache,无调用侧 timeout 参数):
| 字段 | 来源 | 说明 |
|---|---|---|
model | 入参model,缺省回退modelConfig.getModelName() | 目标模型名 |
cache_salt | sessionId | 与会话绑定的缓存标识,和请求时的cache_salt一致 |
cache_sharing | 恒为true | 标记这是共享缓存的释放请求 |
messages | 经convertMessagesToDict+sanitizeToolCalls清洗后的消息列表 | 服务端据此定位缓存窗口 |
messages_released_index | 入参 | 从该索引(含)开始的消息对应的缓存被释放 |
tools/tools_released_index | 可选 | 仅在非空时放入请求体 |
- 返回语义:HTTP 200 返回
true并记录KV cache release successful日志;非 200 记录 error 日志并返回false(不抛异常);其他异常包装为MODEL_CALL_FAILED(错误前缀Release error: ...)。
测试releasePostsKvCachePayloadAndReturnsFalseForNonSuccessStatus验证了完整请求体(model、cache_salt、cache_sharing、messages_released_index: 1、tools_released_index: 0)以及 500 状态码时返回false的行为(测试 L190-L228)。
与上层 KVCacheManager 的协作
从源码结构看,release的消费方是上下文层的 KV Cache 管理组件:KVCacheManager 定义了ReleaseCapableModel接口(L144-L147),而 InferenceAffinityModel 实现了该接口并在内部委托给InferenceAffinityModelClient.release(...)。当上下文窗口发生不可逆变更(如压缩、截断)时,KVCacheManager通过firstChangedDecision计算出首个变更索引(ReleaseDecision),若判定需要释放,就对实现了ReleaseCapableModel的模型发起release(sessionId, messages, messagesReleasedIndex, tools, toolsReleasedIndex)(invokeRelease 实现)。对应的 API 文档可参考 KVCacheManager 与 InferenceAffinityModel。
InferenceAffinityModel:面向上层的统一调用入口
除直接使用 client 外,推荐的调用入口是 InferenceAffinityModel。它把session_id与enable_cache_sharing提升为一等参数:
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) public Iterator<AssistantMessageChunk> stream(Object messages, /* 同上参数 */ ...) public CompletionStage<Boolean> release(String sessionId, List<?> messages, Integer messagesReleasedIndex, List<?> tools, Integer toolsReleasedIndex, String model)其内部normalizeKwargs会把sessionId/enableCacheSharing写回 kwargs 后透传给 client(L171-L179),与上文 client 侧“kwargs 剥离”的逻辑正好配对。静态辅助方法buildKvCacheInvokeKwargs(session, enableKvCacheRelease)则用于从 session 对象中反射读取getSessionId/get_session_id并构造 kwargs(L159-L169)。
需要注意的是,InferenceAffinityModel创建底层 client 时走的是反射适配层ReflectiveInferenceAffinityClient(按方法名 + 参数个数绑定invoke/stream/release,L274-L382),从源码结构看,这一设计保持了 Python 版Model/ModelClient两件套的依赖解耦形态。
使用边界与注意事项
结合仓库文档与源码,使用该客户端时有以下前提和边界:
- 服务端必须实现私有端点:
/release_kv_cache不属于 OpenAI 协议,仅 InferenceAffinity 风格(vLLM 扩展)网关提供;对普通 OpenAI 端点发起释放请求没有意义。cache_sharing/cache_salt字段同样依赖服务端支持。 - 与 AscendAffinity 亲和模式互斥:仓库文档 Ascend算力亲和 明确指出,Ascend-affinity(通过
OpenAIModelClient+agent_hint实现 prefetch/offload/evict)与 InferenceAffinity 的 release 模式互斥,二者不可同时启用。选型时可参考 examples/kv-cache-demo 中的 Ascend-affinity 调度演示。 - 重试语义:
makeAsyncRequest中maxRetries是总尝试次数上限(默认 3),失败退避为2^attempt秒,与 接入大模型 文档中 OpenAI 客户端“追加重试次数”的表述不同,调参时需注意。 - 超时覆盖:
invoke/stream的timeout参数(秒)会覆盖ModelClientConfig.timeout;内部换算为Duration时不足 1ms 会向上取整到 1ms(timeoutDuration)。 - HTTP 版本:
HttpClient由ModelHttpClients按httpVersion构建,测试用例验证了HTTP_1_1可正确生效(测试 L231-L243)。
小结
InferenceAffinityModelClient在 agent-core-java 中承担着一个明确的职责:在 OpenAI 兼容对话协议之上,把“会话级 KV Cache 共享”与“缓存窗口主动释放”落成两个可复现的 HTTP 行为——请求时以cache_salt绑定会话、读取cached_tokens观察命中;窗口不可逆变化时经InferenceAffinityModel.release调用/release_kv_cache回收旧缓存。它的实现细节(kwargs 剥离、tool_calls 清洗、SSE 增量解析、指数退避重试、200/非 200 的差异化返回)均可在 InferenceAffinityModelClient.java 与 InferenceAffinityModelClientTest.java 中逐条对照验证,适合作为理解该仓库“客户端—模型—KV Cache 管理”三层协作的切入点。
- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
相关推荐
ok-wuthering-waves:告别手刷日常,自动战斗与刷声骸一步到位|实操手册
ok wuthering waves:告别手刷日常,自动战斗与刷声骸一步到位|实操手册 鸣潮日常与刷声骸,还浪费多少手动时间 每晚八点开始,进深黯之井、打、领、
GUI 自动化计算机视觉RPA人工智能Mooncake 分布式 KV Cache 存储与共享:基于 vLLM 的 KV Cache Storage & Sharing 部署实战指南
Mooncake 分布式 KV Cache 存储与共享:基于 vLLM 的 KV Cache Storage & Sharing 部署实战指南 本文是一份面向
人工智能大模型模型推理服务后端Xinference Xavier 实战指南:跨 vLLM 副本共享 KV Cache,加速长文档查询与多轮对话推理
Xinference Xavier 实战指南:跨 vLLM 副本共享 KV Cache,加速长文档查询与多轮对话推理 在长文档查询、多轮对话等场景中,大模型推理
模型推理服务人工智能大模型本地部署多模态语音
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考