主题
31 · 回调与拦截器(Interceptors)
工具循环回调(Tool Loop Callbacks)
Embabel 中的 LLM 调用发生在 ToolLoop 内部。 Embabel 的工具循环高度可定制,提供了清晰的扩展点,并把**观察(inspectors)与变换(transformers)**区分开来。
Inspectors(检查器) 观察循环,不修改它。实现任一回调即可:
beforeLlmCallafterLlmCallafterToolResultafterIteration
Inspectors 非常适合日志、指标采集、调试,设计上是只读的。
Transformers(变换器) 修改流经循环的数据。用它们来截断过大的工具结果、对对话历史施加滑动窗口、脱敏敏感内容。 它们改变的是 LLM 看到的内容。
工具循环回调接口
以下是回调接口(Kotlin 原文):
kotlin
/**
* Read-only observer for tool loop lifecycle events.
* Use for logging, metrics, debugging - does not modify state.
*/
interface ToolLoopInspector : ToolLoopCallback {
/** Called before each LLM invocation. Default no-op. */
fun beforeLlmCall(context: BeforeLlmCallContext) = Unit
/** Called after LLM returns a response, before processing tool calls. Default no-op. */
fun afterLlmCall(context: AfterLlmCallContext) = Unit
/** Called after each tool produces a result. Default no-op. */
fun afterToolResult(context: AfterToolResultContext) = Unit
/** Called after each complete iteration (all tool calls processed). Default no-op. */
fun afterIteration(context: AfterIterationContext) = Unit
}
/**
* Transforms message history or tool results during tool loop execution.
* Use for compression, summarization, windowing.
*/
interface ToolLoopTransformer : ToolLoopCallback {
/** Transform history before sending to LLM. Return modified list. */
fun transformBeforeLlmCall(context: BeforeLlmCallContext): List<Message> = context.history
/** Transform LLM response before adding to history. Return modified message. */
fun transformAfterLlmCall(context: AfterLlmCallContext): Message = context.response
/** Transform tool result before adding to history. Return modified string. */
fun transformAfterToolResult(context: AfterToolResultContext): String = context.resultAsString
/** Transform history after iteration completes. Return modified list. */
fun transformAfterIteration(context: AfterIterationContext): List<Message> = context.history
}开箱即用的简单回调
框架提供了几个开箱即用的简单回调:
ToolLoopLoggingInspector—— 在 LLM 调用前后、工具执行之后、以及工具循环每轮迭代之后记录调用详情;ToolResultTruncatingTransformer—— 截断工具调用结果;SlidingWindowTransformer—— 维护消息的滑动窗口以控制上下文大小,同时保留对话上下文的 system message。
用法:工具循环回调
java
// 带工具与回调执行
var result = ai.withDefaultLlm()
.withTools(tools)
.withToolLoopInspectors(callbackTracker, loggingInspector)
.withToolLoopTransformers(truncatingTransformer, slidingWindowTransformer)
.creating(RestaurantRecommendation.class)
.fromPrompt("""
I'm looking for an Italian restaurant near the Upper East Side in NYC.
You have access to these tools to fetch restaurant menus:
%s
Please fetch the menus and recommend the best restaurant for a romantic dinner.
""".formatted(String.join(", ", toolNames)));工具调用拦截器(Tool Call Interceptors)
工具循环回调为"观察 LLM 调用、对话历史与工具执行"提供了强大的能力; 但在实践中,也存在对精简版 inspector 回调的需求。
动机:流式模式
流式是事件驱动的。流式模型提供了获取 thinking 块与结构化对象的回调。
框架还提供了另一类流式回调 —— Tool Call 回调。工具调用回调包含工具定义、工具结果以及工具执行耗时的信息。
工具调用接口
kotlin
/**
* Read-only observer for individual tool call events.
*
* Provides observation of tool execution without access to conversation history
* or iteration state. Works in both streaming mode (where the framework manages
* the tool loop internally) and non-streaming mode (as a lightweight alternative
* to [ToolLoopInspector] when history/iteration context is not needed).
*
* @see ToolLoopInspector for tool loop-level inspection with full conversation context
*/
interface ToolCallInspector {
/**
* Called before tool execution starts.
* Default no-op.
*/
fun beforeToolCall(context: BeforeToolCallContext) = Unit
/**
* Called after tool execution completes (success or failure).
* Default no-op.
*/
fun afterToolCall(context: AfterToolCallContext) = Unit
}开箱即用的工具调用拦截器
采集工具调用指标请参考 ToolCallLoggingInspector。
用法:工具调用拦截器
java
PromptRunner runner = ai.withDefaultLlm()
.withToolObject(new Tooling())
.withToolCallInspectors(new ToolCallLoggingInspector(ToolLoopLoggingInspector.LogLevel.INFO, logger));工具调用拦截器同时适用于流式与非流式模式。