news 2026/11/5 19:12:44

openJiuwen agent-core-java 旧版 LLM 调用兼容层:LLMCall 包装器全面解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
openJiuwen agent-core-java 旧版 LLM 调用兼容层:LLMCall 包装器全面解析
  • 人工智能
  • 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
点击查看免费下载

LLMCall 是 openJiuwen agent-core-java 在旧版兼容路径上提供的 LLM 调用包装器,负责按system + history + user顺序组装消息、驱动模型推理,并在调用完成后触发旧版 optimizer 回调,是提示词调优(tune)链路中可直接绑定优化的参数句柄。本文将以 LLMCall.md 文档为主体,结合仓库源码与测试用例,完整讲解其构造参数、核心 API、流式聚合机制、回调协议,以及与新版LLMCallOperator的差异,帮助开发者在迁移到算子化体系前快速接入或复用这一兼容层。

一、定位:旧版兼容路径上的 LLM 调用包装器

LLMCall位于com.openjiuwen.core.operator.legacy.llm_call包,对应实现文件为 LLMCall.java。从源码注释可以看到,它镜像自 Python 版openjiuwen/core/operator/legacy/llm_call/base.py,是 pre-operator 时代的 LLM 调用封装,承担两件核心职责:

  1. 消息格式化:按照system + history + user的顺序把PromptTemplate渲染结果、会话历史与用户输入组装成List<BaseMessage>,再交给底层Model执行推理;
  2. 完成回调:在收到完整响应(或流式场景聚合完所有 chunk)后,触发已设置的LegacyOptimizerCallback,为提示词调优提供观察与梯度采集入口。

需要特别强调的是,该兼容类不继承Operator,因此不会像新版LLMCallOperator那样把当前算子 ID 写入Session。这意味着它不参与新版自演化算子的 checkpoint 与算子归因体系,属于纯"调用 + 回调"的轻量封装。

二、构造方法详解

2.1 全参构造(7 参数)

public LLMCall(String modelName, Model llm, Object systemPrompt, Object userPrompt, boolean freezeSystemPrompt, boolean freezeUserPrompt, String llmCallId)

各参数含义与取值规则如下:

参数说明默认回退行为
modelName调用的模型名称,写入ModelInvokeOptions.model无
llm底层大模型封装(com.openjiuwen.core.foundation.llm.Model)无
systemPrompt系统提示词,可为字符串、消息列表或模板内容无
userPrompt用户提示词,可为字符串、消息列表或模板内容为null、空字符串或空列表时回退到默认模板{{query}}
freezeSystemPrompt是否冻结系统提示词false(默认不冻结)
freezeUserPrompt是否冻结用户提示词全参构造由调用方显式传入
llmCallId本次调用标识,会透传给 optimizer 回调为null时回退为llm_call

从源码看,构造时会将systemPrompt与userPrompt分别包装为PromptTemplate(PromptTemplate.builder().content(...).build()),其中默认用户提示词常量为:

public static final String DEFAULT_USER_PROMPT = "{{query}}";

{{query}}是占位符语法,在invoke/stream时通过PromptTemplate.format(Map<String, Object> keywords)渲染,query即来自inputs入参中的键。

2.2 便捷构造(4 参数)

public LLMCall(String modelName, Model llm, Object systemPrompt, Object userPrompt)

等价于使用默认参数调用全参构造:

this(modelName, llm, systemPrompt, userPrompt, false, true, "llm_call");

即默认值为:

  • freezeSystemPrompt = false:系统提示词默认不冻结,后续updateSystemPrompt()生效;
  • freezeUserPrompt = true:用户提示词默认冻结,后续updateUserPrompt()不生效;
  • llmCallId = "llm_call"。

注意这一默认组合的差异:便捷构造下用户提示词被冻结、系统提示词可更新,这与LLMCallOperator的默认行为(freezeSystemPrompt=false, freezeUserPrompt=true, operatorId="llm_call")保持一致。

三、核心 API 一览

方法返回类型说明
invoke(Map<String, Object> inputs, Object session, List<BaseMessage> history, List<ToolInfo> tools)CompletionStage<AssistantMessage>格式化 prompt 后调用Model.invoke(...);若设置了LegacyOptimizerCallback,收到完整响应后触发回调
invoke(Map<String, Object> inputs, Object session)CompletionStage<AssistantMessage>不传history/tools的便捷重载(内部传null)
stream(Map<String, Object> inputs, Object session, List<BaseMessage> history, List<ToolInfo> tools)Iterator<AssistantMessageChunk>调用Model.stream(...),流关闭或自然结束时把全部 chunk 聚合为字符串再触发回调
stream(Map<String, Object> inputs, Object session)Iterator<AssistantMessageChunk>不传history/tools的流式便捷重载
getOptimizerCallback()/setOptimizerCallback(LegacyOptimizerCallback)LegacyOptimizerCallback/void读取或设置旧版完成回调
getSystemPrompt()/getUserPrompt()PromptTemplate返回当前系统/用户 prompt 模板对象
updateSystemPrompt(Object)/updateUserPrompt(Object)void在对应 freeze 开关关闭时替换 prompt 模板
setFreezeSystemPrompt(boolean)/setFreezeUserPrompt(boolean)void切换系统/用户 prompt 的冻结状态
getFreezeSystemPrompt()/getFreezeUserPrompt()boolean读取当前冻结状态

说明:文档 API 表中session参数类型标注为Session,而当前源码实现为Object session,以保持对旧版 Session 与新版会话对象的双向兼容;stream实际返回的是内部类LegacyStream(实现了Iterator<AssistantMessageChunk>与AutoCloseable)。

四、消息组装与 invoke 调用链

4.1 消息顺序:system + history + user

invoke的核心逻辑位于源码formatLlmInput(...)(LLMCall.java):

private List<BaseMessage> formatLlmInput(Map<String, Object> inputs, List<BaseMessage> history) { List<BaseMessage> systemMessages = new ArrayList<>(); for (BaseMessage message : systemPrompt.format(inputs).toMessages()) { systemMessages.add(new SystemMessage(message.getContentAsString(), message.getName())); } List<BaseMessage> userMessages = userPrompt.format(inputs).toMessages(); List<BaseMessage> historyMessages = history == null ? List.of() : history; ... messages.addAll(systemMessages); messages.addAll(historyMessages); messages.addAll(userMessages); return messages; }

组装顺序明确为:系统提示词(渲染后) → 历史消息 → 用户提示词(渲染后)。其中:

  • 系统提示词会统一转换为SystemMessage(保留name);
  • history为null时按空列表处理;
  • 用户提示词支持字符串、消息列表或带role/content/name的Map列表,normalizePromptContent(...)与toMessage(...)负责把这些异构输入归一化为BaseMessage(role缺省按user处理,支持system、user及自定义角色)。

4.2 底层模型调用

invoke通过ModelInvokeOptions携带模型名与工具信息调用底层模型:

private ModelInvokeOptions invokeOptions(List<ToolInfo> tools) { return ModelInvokeOptions.builder() .model(modelName) .tools(tools == null ? null : List.copyOf(tools)) .build(); }

随后执行llm.invoke(messages, invokeOptions(tools)),返回CompletionStage<AssistantMessage>。若未设置optimizerCallback,直接原样返回该异步结果;若已设置,则通过response.thenCompose(...)在完整响应到达后执行:

optimizerCallback.onComplete(llmCallId, safeInputs, message, session);

回调异常会通过CompletableFuture.failedFuture(exception)传播,而inputs为null时会被safeInputs(...)归一化为空Map。

五、流式 stream 与 chunk 聚合机制

stream并不直接返回裸迭代器,而是返回内部静态类LegacyStream(见 LLMCall.java),其行为如下:

  1. 逐 chunk 消费:next()取出AssistantMessageChunk后,将其内容追加到内部StringBuilder(nullchunk 会追加字符串"null");
  2. 自然结束即聚合回调:hasNext()返回false或next()后检测到无后续元素时,自动调用close();
  3. close 触发回调:close()内(仅执行一次)把所有 chunk 聚合为完整字符串,调用:
callback.onComplete(llmCallId, inputs, response.toString(), session);

也就是说,流式场景下回调收到的response是拼接后的完整字符串,而非AssistantMessage对象——这与invoke场景(回调收到完整AssistantMessage)不同,是两类调用方式的重要差异。

另外,close()中回调若抛出异常,会包装为IllegalStateException("Legacy LLMCall stream callback failed", exception)抛出;但文档同时说明,在兼容路径上消费方如果捕获异常则不影响主流程(关闭兜底以AutoCloseable语义保证清理)。

六、LegacyOptimizerCallback 回调协议

回调接口定义于 LegacyOptimizerCallback.java,是一个函数式接口:

@FunctionalInterface public interface LegacyOptimizerCallback { void onComplete(String llmCallId, Map<String, Object> inputs, Object response, Object session) throws Exception; }

四个参数的含义:

参数含义
llmCallId本次调用的标识(缺省为llm_call),用于区分同一批绑定中的多个调用点
inputs本次调用的入参(Map),即 prompt 渲染所需的 keyword 来源
response调用结果:invoke场景为完整AssistantMessage,stream场景为聚合后的字符串
session会话对象,可用于追溯会话上下文

正是通过这个回调,旧版调优框架才能把"一次 LLM 调用"的输入输出记录进优化历史,作为后续梯度计算的样本(详见下一节)。

七、实战:在提示词调优(tune)链路中的角色

LLMCall是旧版 prompt 调优(dev_tools/tune)体系的参数载体。相关代码位于core/src/main/java/com/openjiuwen/dev_tools/tune/optimizer/:

7.1 TextualParameter:包裹 LLMCall 的调优参数

TextualParameter.java 将LLMCall包装为一个"可调优参数",内部维护Map<String, String> gradients(按system_prompt/user_prompt分别记录文本梯度),并提供getLlmCall()访问底层调用器。

7.2 BaseOptimizer:回调绑定与梯度采集

BaseOptimizer.java 在enter()/aenter()时通过batchSetOptimizerCallback(this::traceCallback)为所有绑定参数的LLMCall统一注入回调;traceCallback(...)会把每次调用的llmCallId + 输入 + 输出写入OptimizeHistory,供 backward 阶段使用。bindParameter(Map<String, LLMCall>)负责把命名参数注册进来——这正是LLMCall与调优框架的衔接点。

7.3 优化器如何使用 freeze / update

  • InstructionOptimizer.java:doUpdate()中依据getFreezeSystemPrompt()/getFreezeUserPrompt()三态组合,选择只优化 system、只优化 user,或通过PROMPT_INSTRUCTION_OPTIMIZE_BOTH_TEMPLATE同时优化两者,最终调用llmCall.updateSystemPrompt(...)/llmCall.updateUserPrompt(...)落回模板;
  • ExampleOptimizer.java:few-shot 示例优化器,同样先读取冻结状态,再把精选示例追加到未被冻结的 prompt 上。

这解释了 freeze 开关的工程意义:被冻结的 prompt 不会进入梯度更新范围,从而保护关键指令不被自动改写。

7.4 最小接入示意

// 构建旧版兼容调用器:默认 freezeUserPrompt=true,llmCallId="llm_call" LLMCall llmCall = new LLMCall("gpt-4", model, "你是助手", "{{query}}"); // 注册完成回调(也可由 BaseOptimizer.enter() 批量注入) llmCall.setOptimizerCallback((llmCallId, inputs, response, session) -> { // invoke 场景:response 为 AssistantMessage;stream 场景:response 为聚合字符串 }); // 触发调用 llmCall.invoke(Map.of("query", "hello"), session) .toCompletableFuture() .join();

八、与新版 LLMCallOperator 的差异对比

新版算子位于com.openjiuwen.core.operator.llm_call,LLMCallOperator.java 继承自 Operator.java,两者核心差异如下:

维度旧版LLMCall(legacy)新版LLMCallOperator
继承关系普通类,不继承Operator继承Operator,是自演化参数句柄
算子 ID仅作为llmCallId透传回调,不写入 SessiongetOperatorId()参与 Session 当前算子 ID 记录
参数暴露通过 freeze 开关 +update*Prompt()手动控制getTunables()返回TunableSpec(system_prompt/user_prompt),setParameter()/loadState()支持结构化更新与回滚
状态管理无getState()/loadState()支持 checkpoint 快照
模型调用内置invoke/stream并触发回调不直接持有模型,聚焦参数管理
默认值freezeSystemPrompt=false, freezeUserPrompt=true, llmCallId="llm_call"同名默认值,且默认operatorId="llm_call"

另外新版包中还提供 LLMCall.java 作为LLMCallOperator的向后兼容别名。旧版LLMCall的价值在于:在尚未迁移到算子体系的代码路径上,以最小的改动获得"提示词封装 + 完成回调"能力。

九、测试验证:LegacyLLMCallTest

仓库测试 LegacyLLMCallTest.java 覆盖了两个关键行为:

  1. invokeCallsOptimizerCallbackWithoutOperatorContext:mock 底层Model返回AssistantMessage("legacy-response")后,断言:
    • 回调收到的response与invoke返回值是同一个AssistantMessage对象;
    • 会话的getOperatorHistory()为空、getCurrentOperatorId()为null——从测试层面证实"该兼容类不写入 Session 算子 ID";
  2. streamAggregatesChunksForOptimizerCallback:mock 流返回"Hel"、"lo"两个 chunk,消费完整迭代后断言回调收到聚合结果"Hello"——验证了流式场景的 chunk 拼接机制。

这两条测试用例可以直接作为使用兼容层时的行为契约参考。

十、使用建议与总结

  • 何时选用LLMCall:当业务代码仍处于 pre-operator 兼容路径、需要快速获得"system+history+user 消息组装 + LLM 调用 + 完成回调"能力,且不需要算子 ID 归因与 checkpoint 时,LLMCall是最轻量的选择;
  • 注意默认冻结差异:4 参数便捷构造默认冻结 user prompt,若希望调优用户提示词,请显式传入freezeUserPrompt=false;
  • 回调入参形态不同:invoke回调收到AssistantMessage,stream回调收到聚合字符串,消费方需按调用方式区分处理;
  • 流式异常语义:流关闭时回调抛出的异常会被包装抛出,兼容路径上的消费方可自行捕获,避免影响主流程;
  • 迁移方向:如需接入新版自演化算子的TunableSpec、setParameter、loadState能力,应迁移到LLMCallOperator,两者共享{{query}}默认模板与同名 freeze 默认值,迁移成本较低。

总体而言,LLMCall是理解 openJiuwen agent-core-java 从"裸调用"走向"算子化自演化"的关键过渡组件:它保留了旧版回调协议,又与新版算子共享一致的默认语义,开发者可以据此平稳迁移提示词调优链路。

  • 人工智能
  • 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
点击查看免费下载

相关推荐

上一篇:安全审计日报 - 2024-01-15
下一篇:Qiskit量子计算终极指南:如何成为开源量子项目的核心贡献者

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 10:00:49

GPT-4应用工程化:构建可监控、可回滚的LLM服务闭环

简介&#xff1a;本资源是面向AI应用开发者与技术实践者的前沿学习材料&#xff0c;聚焦GPT-4与ChatGPT在真实场景中的工程化落地&#xff0c;涵盖智能聊天机器人、自动化内容生成等核心应用开发方法。全书以O’Reilly Early Release形式发布&#xff0c;内容为作者Olivier Cae…

作者头像 李华
网站建设 2026/10/23 15:58:14

WMI英文开发文档实战指南:从查询到远程采集

简介&#xff1a;这份资源是面向 Windows 平台开发者与 IT 管理员的 WMI 英文官方开发文档&#xff0c;适合需要编写 C、C、Visual Basic 或脚本程序来自动化管理任务的工程师&#xff0c;也适合希望系统理解 WMI 体系结构、类模型与查询语言的技术人员。文档围绕 WMI 基本概念…

作者头像 李华
网站建设 2026/10/24 20:51:06

Sunshine 自托管游戏串流主机:10 分钟搭好低延迟串流服务器

Sunshine 自托管游戏串流主机&#xff1a;10 分钟搭好低延迟串流服务器 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine 是一款自托管的游戏串流宿主程序&#xff0c;配合…

作者头像 李华
网站建设 2026/10/23 15:38:17

TTSR解析:用Transformer为图像超分迁移参考纹理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/23 14:59:18

Qt环境下bsdiff增量更新:从移植到qlz压缩的完整实践

简介&#xff1a;面向需要在 Qt 应用中实现差分升级的开发者&#xff0c;这份资源将开源工具 bsdiff 移植到 Qt 环境&#xff0c;并使用快速压缩算法 qlz 生成 patch 包。其基本思路是比较新旧版本文件&#xff0c;只提取差异部分并压缩输出&#xff0c;特别适合嵌入式、网络带…

作者头像 李华