- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
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 调用封装,承担两件核心职责:
- 消息格式化:按照
system + history + user的顺序把PromptTemplate渲染结果、会话历史与用户输入组装成List<BaseMessage>,再交给底层Model执行推理; - 完成回调:在收到完整响应(或流式场景聚合完所有 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),其行为如下:
- 逐 chunk 消费:
next()取出AssistantMessageChunk后,将其内容追加到内部StringBuilder(nullchunk 会追加字符串"null"); - 自然结束即聚合回调:
hasNext()返回false或next()后检测到无后续元素时,自动调用close(); - 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透传回调,不写入 Session | getOperatorId()参与 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 覆盖了两个关键行为:
invokeCallsOptimizerCallbackWithoutOperatorContext:mock 底层Model返回AssistantMessage("legacy-response")后,断言:- 回调收到的
response与invoke返回值是同一个AssistantMessage对象; - 会话的
getOperatorHistory()为空、getCurrentOperatorId()为null——从测试层面证实"该兼容类不写入 Session 算子 ID";
- 回调收到的
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能力。
相关推荐
agent-core-java 旧版 LLM 调用兼容层 llm_call 全解析:LLMCall 包装器与 LegacyOptimizerCallback 回调契约
agent core java 旧版 LLM 调用兼容层 llm_call 全解析:LLMCall 包装器与 LegacyOptimizerCallback 回
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core-java 旧版算子兼容层解析:LLMCall 与 LegacyOptimizerCallback 的定位、原理与实战
openJiuwen agent core java 旧版算子兼容层解析:LLMCall 与 LegacyOptimizerCallback 的定位、原理与实战
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core-java 旧版 LLM 调用完成回调接口 LegacyOptimizerCallback 使用与原理详解
openJiuwen agent core java 旧版 LLM 调用完成回调接口 LegacyOptimizerCallback 使用与原理详解 Legac
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考