Skip to content

Runtime Event Loop

运行时事件循环

Supported in ADKPython v0.1.0Typescript v0.2.0Go v0.1.0Java v0.1.0

The ADK Runtime is the underlying engine that powers your agent application during user interactions. It's the system that takes your defined agents, tools, and callbacks and orchestrates their execution in response to user input, managing the flow of information, state changes, and interactions with external services like LLMs or storage.

ADK 运行时是驱动您的智能体应用程序在用户交互期间运行的底层引擎。它是这样一个系统:接收您定义的智能体、工具和回调,并编排它们以响应用户输入而执行,管理信息流、状态变更以及与 LLM 或存储等外部服务的交互。

Think of Runtime as the "engine" of your agentic application. You define the parts (agents, tools), and the Runtime handles how they connect and run together to fulfill a user's request.

可以将运行时视为您智能体应用程序的 "引擎"。您定义部件(智能体、工具),而运行时处理它们如何连接和运行以满足用户的请求。

Core Idea: The Event Loop

核心概念:事件循环

At its heart, ADK Runtime operates on an Event Loop. This loop facilitates a back-and-forth communication between the Runner component and your defined "Execution Logic" (which includes your Agents, the LLM calls they make, Callbacks, and Tools).

ADK 运行时的核心基于 事件循环 运作。该循环促进 Runner 组件与您定义的"执行逻辑"(包括您的智能体、它们进行的 LLM 调用、回调和工具)之间的来回通信。

intro_components.png

In simple terms:

简单来说:

  1. The Runner receives a user query and asks the main Agent to start processing.

Runner 接收用户查询并要求主 Agent 开始处理。

  1. The Agent (and its associated logic) runs until it has something to report (like a response, a request to use a tool, or a state change) – it then yields or emits an Event.

Agent(及其相关逻辑)运行直到有内容需要报告(如响应、使用工具的请求或状态变更),然后 产生(yields)或 发出(emits)一个 Event

  1. The Runner receives this Event, processes any associated actions (like saving state changes via Services), and forwards the event onwards (e.g., to the user interface).

Runner 接收此 Event,处理任何关联的操作(如通过 Services 保存状态变更),并将事件向前传递(例如,到用户界面)。

  1. Only after the Runner has processed the event does the Agent's logic resume from where it paused, now potentially seeing the effects of the changes committed by the Runner.

只有在 Runner 处理完事件之后Agent 的逻辑才会从暂停处恢复,现在可能看到 Runner 提交的更改的效果。

  1. This cycle repeats until the agent has no more events to yield for the current user query.

此循环重复,直到智能体没有更多事件可以为当前用户查询产生。

This event-driven loop is the fundamental pattern governing how ADK executes your agent code.

这种事件驱动循环是控制 ADK 如何执行您的智能体代码的基本模式。

The Heartbeat: The Event Loop - Inner workings

核心:事件循环 - 内部工作机制

The Event Loop is the core operational pattern defining the interaction between the Runner and your custom code (Agents, Tools, Callbacks, collectively referred to as "Execution Logic" or "Logic Components" in the design document). It establishes a clear division of responsibilities:

事件循环是定义 Runner 与您的自定义代码(智能体、工具、回调,在设计文档中统称为"执行逻辑"或"逻辑组件")之间交互的核心操作模式。它建立了明确的职责划分:

Note

The specific method names and parameter names may vary slightly by SDK language (e.g., agent.run_async(...) in Python, agent.Run(...) in Go, agent.runAsync(...) in Java and TypeScript). Refer to the language-specific API documentation for details.

特定方法名称和参数名称可能因 SDK 语言略有不同(例如,Python 中的 agent.run_async(...),Go 中的 agent.Run(...),Java 和 TypeScript 中的 agent.runAsync(...))。请参阅特定语言的 API 文档了解详细信息。

Runner's Role (Orchestrator)

Runner 的角色(编排器)

The Runner acts as the central coordinator for a single user invocation. Its responsibilities in the loop are:

Runner 作为单次用户调用的中央协调器。它在循环中的职责是:

  1. Initiation: Receives the end user's query (new_message) and typically appends it to the session history via the SessionService.

启动: 接收最终用户的查询(new_message),通常通过 SessionService 将其附加到会话历史记录中。

  1. Kick-off: Starts the event generation process by calling the main agent's execution method (e.g., agent_to_run.run_async(...)).

启动: 通过调用主智能体的执行方法(例如 agent_to_run.run_async(...))开始事件生成过程。

  1. Receive & Process: Waits for the agent logic to yield or emit an Event. Upon receiving an event, the Runner promptly processes it. This involves:
    • Using configured Services (SessionService, ArtifactService, MemoryService) to commit changes indicated in event.actions (like state_delta, artifact_delta).
    • Performing other internal bookkeeping.

接收和处理: 等待智能体逻辑 yieldemit 一个 Event。接收到事件后,Runner 立即处理它。这包括: * 使用配置的 ServicesSessionServiceArtifactServiceMemoryService)提交 event.actions 中指示的更改(如 state_deltaartifact_delta)。 * 执行其他内部簿记工作。

  1. Yield Upstream: Forwards the processed event onwards (e.g., to the calling application or UI for rendering).

向上传递: 将处理后的事件向前传递(例如,到调用应用程序或 UI 进行渲染)。

  1. Iterate: Signals the agent logic that processing is complete for the yielded event, allowing it to resume and generate the next event.

迭代: 向智能体逻辑发出信号,表明已对产生的事件完成处理,允许它恢复并生成下一个事件。

Conceptual Runner Loop:

概念性 Runner 循环:

# Simplified view of Runner's main loop logic
# Runner 主循环逻辑的简化视图
def run(new_query, ...) -> Generator[Event]:
    # 1. Append new_query to session event history (via SessionService)
    # 1. 将 new_query 附加到会话事件历史记录(通过 SessionService)
    session_service.append_event(session, Event(author='user', content=new_query))

    # 2. Kick off event loop by calling the agent
    # 2. 通过调用智能体启动事件循环
    agent_event_generator = agent_to_run.run_async(context)

    async for event in agent_event_generator:
        # 3. Process the generated event and commit changes
        # 3. 处理生成的事件并提交更改
        session_service.append_event(session, event) # Commits state/artifact deltas etc.
        # memory_service.update_memory(...) # If applicable
        # artifact_service might have already been called via context during agent run

        # 4. Yield event for upstream processing (e.g., UI rendering)
        # 4. 为上游处理产生事件(例如 UI 渲染)
        yield event
        # Runner implicitly signals agent generator can continue after yielding
        # Runner 在产生后隐式地向智能体生成器发出可以继续的信号
// Simplified view of Runner's main loop logic
// Runner 主循环逻辑的简化视图
async * runAsync(newQuery: Content, ...): AsyncGenerator<Event, void, void> {
    // 1. Append newQuery to session event history (via SessionService)
    // 1. 将 newQuery 附加到会话事件历史记录(通过 SessionService)
    await sessionService.appendEvent({
        session,
        event: createEvent({author: 'user', content: newQuery})
    });

    // 2. Kick off event loop by calling the agent
    // 2. 通过调用智能体启动事件循环
    const agentEventGenerator = agentToRun.runAsync(context);

    for await (const event of agentEventGenerator) {
        // 3. Process the generated event and commit changes
        // 3. 处理生成的事件并提交更改
        // Commits state/artifact deltas etc.
        await sessionService.appendEvent({session, event});
        // memoryService.updateMemory(...) // If applicable
        // artifactService might have already been called via context during agent run

        // 4. Yield event for upstream processing (e.g., UI rendering)
        // 4. 为上游处理产生事件(例如 UI 渲染)
        yield event;
        // Runner implicitly signals agent generator can continue after yielding
        // Runner 在产生后隐式地向智能体生成器发出可以继续的信号
    }
}
// Simplified conceptual view of Runner's main loop logic in Go
// Go 中 Runner 主循环逻辑的简化概念视图
func (r *Runner) RunConceptual(ctx context.Context, session *session.Session, newQuery *genai.Content) iter.Seq2[*Event, error] {
    return func(yield func(*Event, error) bool) {
        // 1. Append new_query to session event history (via SessionService)
        // 1. 将 new_query 附加到会话事件历史记录(通过 SessionService)
        // ...
        userEvent := session.NewEvent(ctx.InvocationID()) // Simplified for conceptual view
        userEvent.Author = "user"
        userEvent.LLMResponse = model.LLMResponse{Content: newQuery}

        if _, err := r.sessionService.Append(ctx, &session.AppendRequest{Event: userEvent}); err != nil {
            yield(nil, err)
            return
        }

        // 2. Kick off event stream by calling the agent
        // 2. 通过调用智能体启动事件流
        // Assuming agent.Run also returns iter.Seq2[*Event, error]
        agentEventsAndErrs := r.agent.Run(ctx, &agent.RunRequest{Session: session, Input: newQuery})

        for event, err := range agentEventsAndErrs {
            if err != nil {
                if !yield(event, err) { // Yield event even if there's an error, then stop
                    return
                }
                return // Agent finished with an error
            }

            // 3. Process the generated event and commit changes
            // 3. 处理生成的事件并提交更改
            // Only commit non-partial event to a session service (as seen in actual code)
            if !event.LLMResponse.Partial {
                if _, err := r.sessionService.Append(ctx, &session.AppendRequest{Event: event}); err != nil {
                    yield(nil, err)
                    return
                }
            }
            // memory_service.update_memory(...) // If applicable
            // artifact_service might have already been called via context during agent run

            // 4. Yield event for upstream processing
            // 4. 为上游处理产生事件
            if !yield(event, nil) {
                return // Upstream consumer stopped
            }
        }
        // Agent finished successfully
        // 智能体成功完成
    }
}
// Simplified conceptual view of Runner's main loop logic in Java.
// Java 中 Runner 主循环逻辑的简化概念视图。
public Flowable<Event> runConceptual(
    Session session,
    InvocationContext invocationContext,
    Content newQuery
    ) {

    // 1. Append new_query to session event history (via SessionService)
    // 1. 将 new_query 附加到会话事件历史记录(通过 SessionService)
    // ...
    sessionService.appendEvent(session, userEvent).blockingGet();

    // 2. Kick off event stream by calling the agent
    // 2. 通过调用智能体启动事件流
    Flowable<Event> agentEventStream = agentToRun.runAsync(invocationContext);

    // 3. Process each generated event, commit changes, and "yield" or "emit"
    // 3. 处理每个生成的事件,提交更改,并"yield"或"emit"
    return agentEventStream.map(event -> {
        // This mutates the session object (adds event, applies stateDelta).
        // The return value of appendEvent (a Single<Event>) is conceptually
        // just that event itself after processing.
        sessionService.appendEvent(session, event).blockingGet(); // Simplified blocking call

        // memory_service.update_memory(...) // If applicable - conceptual
        // artifact_service might have already been called via context during agent run

        // 4. "Yield" event for upstream processing
        //    In RxJava, returning an event in map effectively yields it to the next operator or subscriber.
        // 4. 为上游处理"产生"事件
        //    在 RxJava 中,在 map 中返回事件有效地将其产生给下一个操作符或订阅者。
        return event;
    });
}

Execution Logic's Role (Agent, Tool, Callback)

执行逻辑的角色(智能体、工具、回调)

Your code within agents, tools, and callbacks is responsible for the actual computation and decision-making. Its interaction with the loop involves:

您在智能体、工具和回调中的代码负责实际计算和决策。它与循环的交互涉及:

  1. Execute: Runs its logic based on the current InvocationContext, including the session state as it was when execution resumed.

执行: 基于当前 InvocationContext 运行其逻辑,包括执行恢复时的会话状态。

  1. Yield: When the logic needs to communicate (send a message, call a tool, report a state change), it constructs an Event containing relevant content and actions, and then yields this event back to the Runner.

产生: 当逻辑需要通信(发送消息、调用工具、报告状态变更)时,它构建一个包含相关内容和操作的 Event,然后将其 yieldRunner

  1. Pause: Crucially, execution of the agent logic pauses immediately after the yield statement (or return in RxJava). It waits for the Runner to complete step 3 (processing and committing).

暂停: 关键的是,智能体逻辑的执行在 yield 语句(或 RxJava 中的 return)之后立即暂停。它等待 Runner 完成步骤 3(处理和提交)。

  1. Resume: Only after the Runner has processed the yielded event does the agent logic resume execution from the statement immediately following the yield.

恢复: 只有在 Runner 处理完产生的事件之后,智能体逻辑才从紧随 yield 的语句恢复执行。

  1. See Updated State: Upon resumption, the agent logic can now reliably access the session state (ctx.session.state) reflecting the changes that were committed by the Runner from the previously yielded event.

查看更新后的状态: 恢复后,智能体现在可以可靠地访问会话状态(ctx.session.state),该状态反映 Runner先前产生的事件提交的更改。

Conceptual Execution Logic:

概念性执行逻辑:

# Simplified view of logic inside Agent.run_async, callbacks, or tools
# Agent.run_async、回调或工具内部逻辑的简化视图

# ... previous code runs based on current state ...
# ... 之前的代码基于当前状态运行 ...

# 1. Determine a change or output is needed, construct the event
# 1. 确定需要更改或输出,构造事件
# Example: Updating state
# 示例:更新状态
update_data = {'field_1': 'value_2'}
event_with_state_change = Event(
    author=self.name,
    actions=EventActions(state_delta=update_data),
    content=types.Content(parts=[types.Part(text="State updated.")])
    # ... other event fields ...
)

# 2. Yield the event to the Runner for processing & commit
# 2. 将事件产生给 Runner 进行处理和提交
yield event_with_state_change
# <<<<<<<<<<<< EXECUTION PAUSES HERE >>>>>>>>>>>>
# <<<<<<<<<<<< 执行在此处暂停 >>>>>>>>>>>>

# <<<<<<<<<<<< RUNNER PROCESSES & COMMITS THE EVENT >>>>>>>>>>>>
# <<<<<<<<<<<< RUNNER 处理并提交事件 >>>>>>>>>>>>

# 3. Resume execution ONLY after Runner is done processing above event.
# 3. 仅在 Runner 完成处理上述事件后恢复执行。
# Now, the state committed by the Runner is reliably reflected.
# 现在,Runner 提交的状态已可靠反映。
# Subsequent code can safely assume change from the yielded event happened.
# 后续代码可以安全地假设从产生的事件的更改已发生。
val = ctx.session.state['field_1']
# here `val` is guaranteed to be "value_2" (assuming Runner committed successfully)
# 这里 `val` 保证是 "value_2"(假设 Runner 成功提交)
print(f"Resumed execution. Value of field_1 is now: {val}")

# ... subsequent code continues ...
# Maybe yield another event later...
# ... 后续代码继续 ...
# Maybe yield another event later...
# 也许稍后产生另一个事件...
// Simplified view of logic inside Agent.runAsync, callbacks, or tools
// Agent.runAsync、回调或工具内部逻辑的简化视图

// ... previous code runs based on current state ...
// ... 之前的代码基于当前状态运行 ...

// 1. Determine a change or output is needed, construct the event
// 1. 确定需要更改或输出,构造事件
// Example: Updating state
// 示例:更新状态
const updateData = {'field_1': 'value_2'};
const eventWithStateChange = createEvent({
    author: this.name,
    actions: createEventActions({stateDelta: updateData}),
    content: {parts: [{text: "State updated."}]}
    // ... other event fields ...
});

// 2. Yield the event to the Runner for processing & commit
// 2. 将事件产生给 Runner 进行处理和提交
yield eventWithStateChange;
// <<<<<<<<<<<< EXECUTION PAUSES HERE >>>>>>>>>>>>
// <<<<<<<<<<<< 执行在此处暂停 >>>>>>>>>>>>

// <<<<<<<<<<<< RUNNER PROCESSES & COMMITS THE EVENT >>>>>>>>>>>>
// <<<<<<<<<<<< RUNNER 处理并提交事件 >>>>>>>>>>>>

// 3. Resume execution ONLY after Runner is done processing above event.
// 3. 仅在 Runner 完成处理上述事件后恢复执行。
// Now, the state committed by the Runner is reliably reflected.
// 现在,Runner 提交的状态已可靠反映。
// Subsequent code can safely assume change from the yielded event happened.
// 后续代码可以安全地假设从产生的事件的更改已发生。
const val = ctx.session.state['field_1'];
// here `val` is guaranteed to be "value_2" (assuming Runner committed successfully)
// 这里 `val` 保证是 "value_2"(假设 Runner 成功提交)
console.log(`Resumed execution. Value of field_1 is now: ${val}`);

// ... subsequent code continues ...
// Maybe yield another event later...
// ... 后续代码继续 ...
// 也许稍后产生另一个事件...
// Simplified view of logic inside Agent.Run, callbacks, or tools
// Agent.Run、回调或工具内部逻辑的简化视图

// ... previous code runs based on current state ...
// ... 之前的代码基于当前状态运行 ...

// 1. Determine a change or output is needed, construct the event
// 1. 确定需要更改或输出,构造事件
// Example: Updating state
// 示例:更新状态
updateData := map[string]interface{}{"field_1": "value_2"}
eventWithStateChange := &Event{
    Author: self.Name(),
    Actions: &EventActions{StateDelta: updateData},
    Content: genai.NewContentFromText("State updated.", "model"),
    // ... other event fields ...
}

// 2. Yield the event to the Runner for processing & commit
// 2. 将事件产生给 Runner 进行处理和提交
// In Go, this is done by sending the event to a channel.
// 在 Go 中,这是通过将事件发送到通道来完成的。
eventsChan <- eventWithStateChange
// <<<<<<<<<<<< EXECUTION PAUSES HERE (conceptually) >>>>>>>>>>>>
// <<<<<<<<<<<< 执行在此处暂停(概念上) >>>>>>>>>>>>
// The Runner on the other side of the channel will receive and process the event.
// 通道另一侧的 Runner 将接收并处理事件。
// The agent's goroutine might continue, but the logical flow waits for the next input or step.
// 智能体的 goroutine 可能继续,但逻辑流等待下一个输入或步骤。

// <<<<<<<<<<<< RUNNER PROCESSES & COMMITS THE EVENT >>>>>>>>>>>>
// <<<<<<<<<<<< RUNNER 处理并提交事件 >>>>>>>>>>>>

// 3. Resume execution ONLY after Runner is done processing above event.
// 3. 仅在 Runner 完成处理上述事件后恢复执行。
// In a real Go implementation, this would likely be handled by the agent receiving
// a new RunRequest or context indicating the next step. The updated state
// would be part of session object in that new request.
// 在实际的 Go 实现中,这可能通过智能体接收
// 新的 RunRequest 或指示下一步骤的上下文来处理。更新的状态
// 将是该新请求中会话对象的一部分。
// For this conceptual example, we'll just check the state.
// 对于此概念性示例,我们只检查状态。
val := ctx.State.Get("field_1")
// here `val` is guaranteed to be "value_2" because the Runner would have
// updated the session state before calling the agent again.
// 这里 `val` 保证是 "value_2",因为 Runner 会在
// 再次调用智能体之前更新会话状态。
fmt.Printf("Resumed execution. Value of field_1 is now: %v\n", val)

// ... subsequent code continues ...
// Maybe send another event to the channel later...
// ... 后续代码继续 ...
// 也许稍后向通道发送另一个事件...
// Simplified view of logic inside Agent.runAsync, callbacks, or tools
// Agent.runAsync、回调或工具内部逻辑的简化视图
// ... previous code runs based on current state ...
// ... 之前的代码基于当前状态运行 ...

// 1. Determine a change or output is needed, construct the event
// 1. 确定需要更改或输出,构造事件
// Example: Updating state
// 示例:更新状态
ConcurrentMap<String, Object> updateData = new ConcurrentHashMap<>();
updateData.put("field_1", "value_2");

EventActions actions = EventActions.builder().stateDelta(updateData).build();
Content eventContent = Content.builder().parts(Part.fromText("State updated.")).build();

Event eventWithStateChange = Event.builder()
    .author(self.name())
    .actions(actions)
    .content(Optional.of(eventContent))
    // ... other event fields ...
    .build();

// 2. "Yield" the event. In RxJava, this means emitting it into the stream.
//    The Runner (or upstream consumer) will subscribe to this Flowable.
//    When Runner receives this event, it will process it (e.g., call sessionService.appendEvent).
//    The 'appendEvent' in Java ADK mutates the 'Session' object held within 'ctx' (InvocationContext).
// 2. "产生"事件。在 RxJava 中,这意味着将其发射到流中。
//    Runner(或上游消费者)将订阅此 Flowable。
//    当 Runner 接收此事件时,它将处理它(例如,调用 sessionService.appendEvent)。
//    Java ADK 中的 'appendEvent' 改变 'ctx'(InvocationContext)中持有的 'Session' 对象。

// <<<<<<<<<<<< CONCEPTUAL PAUSE POINT >>>>>>>>>>>>
// <<<<<<<<<<<< 概念性暂停点 >>>>>>>>>>>>
// In RxJava, the emission of 'eventWithStateChange' happens, and then the stream
// might continue with a 'flatMap' or 'concatMap' operator that represents
// logic *after* the Runner has processed this event.
// 在 RxJava 中,'eventWithStateChange' 的发射发生,然后流
// 可能继续使用 'flatMap' 或 'concatMap' 操作符,表示
// Runner 处理此事件*之后*的逻辑。

// To model the "resume execution ONLY after Runner is done processing":
// The Runner's `appendEvent` is usually an async operation itself (returns Single<Event>).
// The agent's flow needs to be structured such that subsequent logic
// that depends on the committed state runs *after* that `appendEvent` completes.
// 要对"仅在 Runner 完成处理后恢复执行"进行建模:
// Runner 的 `appendEvent` 本身通常是异步操作(返回 Single<Event>)。
// 智能体的流需要结构化,使得依赖于提交状态的后续逻辑
// 在该 `appendEvent` 完成*之后*运行。

// This is how Runner typically orchestrates it:
// Runner 通常这样编排它:
// Runner:
//   agent.runAsync(ctx)
//     .concatMapEager(eventFromAgent ->
//         sessionService.appendEvent(ctx.session(), eventFromAgent) // This updates ctx.session().state()
//             .toFlowable() // Emits the event after it's processed
//     )
//     .subscribe(processedEvent -> { /* UI renders processedEvent */ });

// So, within the agent's own logic, if it needs to do something *after* an event it yielded
// has been processed and its state changes are reflected in ctx.session().state(),
// that subsequent logic would typically be in another step of its reactive chain.
// 因此,在智能体自己的逻辑内,如果它需要在产生的事件
// 被处理且其状态更改反映在 ctx.session().state() 中*之后*做某事,
// 该后续逻辑通常会在其响应链的另一个步骤中。

// For this conceptual example, we'll emit the event, and then simulate "resume"
// as a subsequent operation in the Flowable chain.
// 对于此概念性示例,我们将发射事件,然后模拟"恢复"
// 作为 Flowable 链中的后续操作。

return Flowable.just(eventWithStateChange) // Step 2: Yield the event
    .concatMap(yieldedEvent -> {
        // <<<<<<<<<<<< RUNNER CONCEPTUALLY PROCESSES & COMMITS THE EVENT >>>>>>>>>>>>
        // <<<<<<<<<<<< RUNNER 概念性地处理并提交事件 >>>>>>>>>>>>
        // At this point, in a real runner, ctx.session().appendEvent(yieldedEvent) would have been called
        // by the Runner, and ctx.session().state() would be updated.
        // 在此点,在实际的 runner 中,ctx.session().appendEvent(yieldedEvent) 会被
        // Runner 调用,并且 ctx.session().state() 会被更新。
        // Since we are *inside* the agent's conceptual logic trying to model this,
        // we assume the Runner's action has implicitly updated our 'ctx.session()'.
        // 因为我们*在*智能体的概念逻辑内部尝试对此进行建模,
        // 我们假设 Runner 的操作已隐式更新了我们的 'ctx.session()'。

        // 3. Resume execution.
        // 3. 恢复执行。
        // Now, the state committed by the Runner (via sessionService.appendEvent)
        // is reliably reflected in ctx.session().state().
        // 现在,Runner 提交的状态(通过 sessionService.appendEvent)
        // 已可靠地反映在 ctx.session().state() 中。
        Object val = ctx.session().state().get("field_1");
        // here `val` is guaranteed to be "value_2" because `sessionService.appendEvent`
        // called by the Runner would have updated the session state within the `ctx` object.
        // 这里 `val` 保证是 "value_2",因为 Runner 调用的
        // `sessionService.appendEvent` 会更新 `ctx` 对象内的会话状态。

        System.out.println("Resumed execution. Value of field_1 is now: " + val);

        // ... subsequent code continues ...
        // If this subsequent code needs to yield another event, it would do so here.
        // ... 后续代码继续 ...
        // 如果此后续代码需要产生另一个事件,它将在此处进行。

This cooperative yield/pause/resume cycle between the Runner and your Execution Logic, mediated by Event objects, forms the core of the ADK Runtime.

Runner 与您的执行逻辑之间通过 Event 对象介导的协作产生/暂停/恢复循环,构成了 ADK 运行时的核心。

Key components of Runtime

运行时的关键组件

Several components work together within the ADK Runtime to execute an agent invocation. Understanding their roles clarifies how the event loop functions:

多个组件在 ADK 运行时内协同工作以执行智能体调用。理解它们的角色可以阐明事件循环如何运作:

  1. Runner

    • Role: The main entry point and orchestrator for a single user query (run_async).

      角色: 单个用户查询(run_async)的主入口点和编排器。

    • Function: Manages the overall Event Loop, receives events yielded by the Execution Logic, coordinates with Services to process and commit event actions (state/artifact changes), and forwards processed events upstream (e.g., to the UI). It essentially drives the conversation turn by turn based on yielded events. (Defined in google.adk.runners.runner).

      功能: 管理整体事件循环,接收执行逻辑产生的事件,与服务协调以处理和提交事件操作(状态/工件更改),并将处理后的事件向上传递(例如,到 UI)。它基本上基于产生的事件逐轮驱动对话。(在 google.adk.runners.runner 中定义)。

  2. Execution Logic Components

    • Role: The parts containing your custom code and the core agent capabilities.

      角色: 包含您的自定义代码和核心智能体功能的部件。

    • Components:

      组件: * Agent (BaseAgent, LlmAgent, etc.): Your primary logic units that process information and decide on actions. They implement the _run_async_impl method which yields events.

      AgentBaseAgentLlmAgent 等):您的主要逻辑单元,处理信息并决定操作。它们实现 _run_async_impl 方法,该方法产生事件。 * Tools (BaseTool, FunctionTool, AgentTool, etc.): External functions or capabilities used by agents (often LlmAgent) to interact with the outside world or perform specific tasks. They execute and return results, which are then wrapped in events.

      ToolsBaseToolFunctionToolAgentTool 等):智能体(通常是 LlmAgent)用来与外部世界交互或执行特定任务的外部函数或功能。它们执行并返回结果,然后结果被包装在事件中。 * Callbacks (Functions): User-defined functions attached to agents (e.g., before_agent_callback, after_model_callback) that hook into specific points in the execution flow, potentially modifying behavior or state, whose effects are captured in events.

      Callbacks(函数):附加到智能体的用户定义函数(例如 before_agent_callbackafter_model_callback),它们挂钩到执行流中的特定点,可能修改行为或状态,其效果被捕获在事件中。 * Function: Perform the actual thinking, calculation, or external interaction. They communicate their results or needs by yielding Event objects and pausing until the Runner processes them.

      功能: 执行实际思考、计算或外部交互。它们通过产生 Event 对象传达其结果或需求,并暂停直到 Runner 处理它们。

  3. Event

    • Role: The message passed back and forth between the Runner and the Execution Logic.

      角色:Runner 和执行逻辑之间来回传递的消息。

    • Function: Represents an atomic occurrence (user input, agent text, tool call/result, state change request, control signal). It carries both the content of the occurrence and the intended side effects (actions like state_delta).

      功能: 表示原子发生(用户输入、智能体文本、工具调用/结果、状态更改请求、控制信号)。它既携带发生的内容,也携带预期的副作用(如 state_delta 等操作)。

  4. Services

    • Role: Backend components responsible for managing persistent or shared resources. Used primarily by the Runner during event processing.

      角色: 负责管理持久化或共享资源的后端组件。主要由 Runner 在事件处理期间使用。

    • Components:

      组件: * SessionService (BaseSessionService, InMemorySessionService, etc.): Manages Session objects, including saving/loading them, applying state_delta to the session state, and appending events to the event history.

      SessionServiceBaseSessionServiceInMemorySessionService 等):管理 Session 对象,包括保存/加载它们、将 state_delta 应用于会话状态,以及将事件附加到 event history。 * ArtifactService (BaseArtifactService, InMemoryArtifactService, GcsArtifactService, etc.): Manages the storage and retrieval of binary artifact data. Although save_artifact is called via context during execution logic, the artifact_delta in the event confirms the action for the Runner/SessionService.

      ArtifactServiceBaseArtifactServiceInMemoryArtifactServiceGcsArtifactService 等):管理二进制工件数据的存储和检索。虽然 save_artifact 在执行逻辑期间通过上下文调用,但事件中的 artifact_delta 向 Runner/SessionService 确认该操作。 * MemoryService (BaseMemoryService, etc.): (Optional) Manages long-term semantic memory across sessions for a user.

      MemoryServiceBaseMemoryService 等):(可选)管理用户跨会话的长期语义记忆。 * Function: Provide the persistence layer. The Runner interacts with them to ensure changes signaled by event.actions are reliably stored before the Execution Logic resumes.

      功能: 提供持久化层。Runner 与它们交互以确保 event.actions 信号指示的更改在执行逻辑恢复之前被可靠地存储。

  5. Session

    • Role: A data container holding the state and history for one specific conversation between a user and the application.

      角色: 一个数据容器,保存用户和应用程序之间一个特定对话的状态和历史记录。

    • Function: Stores the current state dictionary, the list of all past events (event history), and references to associated artifacts. It's the primary record of the interaction, managed by the SessionService.

      功能: 存储当前 state 字典、所有过去 events 的列表(event history),以及对关联工件的引用。它是交互的主要记录,由 SessionService 管理。

  6. Invocation

    • Role: A conceptual term representing everything that happens in response to a single user query, from the moment the Runner receives it until the agent logic finishes yielding events for that query.

      角色: 一个概念性术语,表示响应单个用户查询时发生的所有事情,从 Runner 接收查询的那一刻开始,直到智能体逻辑完成为该查询产生事件为止。

    • Function: An invocation might involve multiple agent runs (if using agent transfer or AgentTool), multiple LLM calls, tool executions, and callback executions, all tied together by a single invocation_id within the InvocationContext. State variables prefixed with temp: are strictly scoped to a single invocation and discarded afterwards.

      功能: 一次调用可能涉及多个智能体运行(如果使用智能体转移或 AgentTool)、多个 LLM 调用、工具执行和回调执行,所有这些都通过 InvocationContext 中的单个 invocation_id 绑在一起。以 temp: 为前缀的状态变量严格限定于单次调用,并在之后被丢弃。

These players interact continuously through the Event Loop to process a user's request.

这些参与者通过事件循环持续交互以处理用户的请求。

How It Works: A Simplified Invocation

如何运作:简化的调用

Let's trace a simplified flow for a typical user query that involves an LLM agent calling a tool:

让我们追踪一个典型用户查询的简化流程,该查询涉及调用工具的 LLM 智能体:

intro_components.png

Step-by-Step Breakdown

分步详解

  1. User Input: The User sends a query (e.g., "What's the capital of France?").

用户输入: 用户发送查询(例如,"法国的首都是什么?")。

  1. Runner Starts: Runner.run_async begins. It interacts with the SessionService to load the relevant Session and adds the user query as the first Event to the session history. An InvocationContext (ctx) is prepared.

Runner 启动: Runner.run_async 开始。它与 SessionService 交互以加载相关的 Session,并将用户查询作为第一个 Event 添加到会话历史记录中。准备了一个 InvocationContextctx)。

  1. Agent Execution: The Runner calls agent.run_async(ctx) on the designated root agent (e.g., an LlmAgent).

智能体执行: Runner 在指定的根智能体(例如 LlmAgent)上调用 agent.run_async(ctx)

  1. LLM Call (Example): The Agent_Llm determines it needs information, perhaps by calling a tool. It prepares a request for the LLM. Let's assume the LLM decides to call MyTool.

LLM 调用(示例): Agent_Llm 确定它需要信息,可能通过调用工具。它准备一个对 LLM 的请求。让我们假设 LLM 决定调用 MyTool

  1. Yield FunctionCall Event: The Agent_Llm receives the FunctionCall response from the LLM, wraps it in an Event(author='Agent_Llm', content=Content(parts=[Part(function_call=...)])), and yields or emits this event.

产生 FunctionCall 事件: Agent_Llm 从 LLM 接收 FunctionCall 响应,将其包装在 Event(author='Agent_Llm', content=Content(parts=[Part(function_call=...)]) 中,然后 yieldemit 此事件。

  1. Agent Pauses: The Agent_Llm's execution pauses immediately after the yield.

智能体暂停: Agent_Llm 的执行在 yield 之后立即暂停。

  1. Runner Processes: The Runner receives the FunctionCall event. It passes it to the SessionService to record it in the history. The Runner then yields the event upstream to the User (or application).

Runner 处理: Runner 接收 FunctionCall 事件。它将其传递给 SessionService 以在历史记录中记录它。Runner 然后将事件向上传递给 User(或应用程序)。

  1. Agent Resumes: The Runner signals that the event is processed, and Agent_Llm resumes execution.

智能体恢复: Runner 发出信号表明事件已处理,Agent_Llm 恢复执行。

  1. Tool Execution: The Agent_Llm's internal flow now proceeds to execute the requested MyTool. It calls tool.run_async(...).

工具执行: Agent_Llm 的内部流程现在继续执行请求的 MyTool。它调用 tool.run_async(...)

  1. Tool Returns Result: MyTool executes and returns its result (e.g., {'result': 'Paris'}).

    工具返回结果: MyTool 执行并返回其结果(例如,{'result': 'Paris'})。

  2. Yield FunctionResponse Event: The agent (Agent_Llm) wraps the tool result into an Event containing a FunctionResponse part (e.g., Event(author='Agent_Llm', content=Content(role='user', parts=[Part(function_response=...)])). This event might also contain actions if the tool modified state (state_delta) or saved artifacts (artifact_delta). The agent yields this event.

    产生 FunctionResponse 事件: 智能体(Agent_Llm)将工具结果包装到一个包含 FunctionResponse 部分的 Event 中(例如,Event(author='Agent_Llm', content=Content(role='user', parts=[Part(function_response=...)]))。如果工具修改了状态(state_delta)或保存了工件(artifact_delta),此事件可能还包含 actions。智能体 yield 此事件。

  3. Agent Pauses: Agent_Llm pauses again.

    智能体暂停: Agent_Llm 再次暂停。

  4. Runner Processes: Runner receives the FunctionResponse event. It passes it to SessionService which applies any state_delta/artifact_delta and adds the event to history. Runner yields the event upstream.

    Runner 处理: Runner 接收 FunctionResponse 事件。它将其传递给 SessionService,后者应用任何 state_delta/artifact_delta 并将事件添加到历史记录中。Runner 向上产生事件。

  5. Agent Resumes: Agent_Llm resumes, now knowing the tool result and any state changes are committed.

    智能体恢复: Agent_Llm 恢复,现在知道工具结果和任何状态更改都已提交。

  6. Final LLM Call (Example): Agent_Llm sends the tool result back to the LLM to generate a natural language response.

    最终 LLM 调用(示例): Agent_Llm 将工具结果发送回 LLM 以生成自然语言响应。

  7. Yield Final Text Event: Agent_Llm receives the final text from the LLM, wraps it in an Event(author='Agent_Llm', content=Content(parts=[Part(text=...)])), and yields it.

    产生最终文本事件: Agent_LlmLLM 接收最终文本,将其包装在 Event(author='Agent_Llm', content=Content(parts=[Part(text=...)]) 中,然后 yield 它。

  8. Agent Pauses: Agent_Llm pauses.

    智能体暂停: Agent_Llm 暂停。

  9. Runner Processes: Runner receives the final text event, passes it to SessionService for history, and yields it upstream to the User. This is likely marked as the is_final_response().

    Runner 处理: Runner 接收最终文本事件,将其传递给 SessionService 以进行历史记录,并向上产生给 User。这很可能被标记为 is_final_response()

  10. Agent Resumes & Finishes: Agent_Llm resumes. Having completed its task for this invocation, its run_async generator finishes.

    智能体恢复并完成: Agent_Llm 恢复。完成此调用的任务后,其 run_async 生成器结束。

  11. Runner Completes: The Runner sees the agent's generator is exhausted and finishes its loop for this invocation.

    Runner 完成: Runner 看到智能体的生成器已耗尽,并完成此次调用的循环。

This yield/pause/process/resume cycle ensures that state changes are consistently applied and that the execution logic always operates on the most recently committed state after yielding an event.

这种产生/暂停/处理/恢复循环确保状态变更被一致地应用,并且执行逻辑在产生事件后总是在最近提交的状态上运行。

Important Runtime Behaviors

重要的运行时行为

Understanding a few key aspects of how the ADK Runtime handles state, streaming, and asynchronous operations is crucial for building predictable and efficient agents.

理解 ADK 运行时如何处理状态、流式传输和异步操作的几个关键方面,对于构建可预测且高效的智能体至关重要。

State Updates & Commitment Timing

状态更新和提交时机

  • The Rule: When your code (in an agent, tool, or callback) modifies the session state (e.g., context.state['my_key'] = 'new_value'), this change is initially recorded locally within the current InvocationContext. The change is only guaranteed to be persisted (saved by the SessionService) after the Event carrying the corresponding state_delta in its actions has been yield-ed by your code and subsequently processed by the Runner.

规则: 当您的代码(在智能体、工具或回调中)修改会话状态(例如,context.state['my_key'] = 'new_value')时,此更改最初在当前 InvocationContext 内本地记录。只有当携带相应 state_deltaEvent 在其 actions 中被您的代码 yield 并随后被 Runner 处理之后,该更改才保证被持久化(由 SessionService 保存)。

  • Implication: Code that runs after resuming from a yield can reliably assume that the state changes signaled in the yielded event have been committed.

含义: 在从 yield 恢复之后运行的代码可以可靠地假设产生的事件中信号指示的状态更改已被提交。

# Inside agent logic (conceptual)
# 在智能体逻辑内部(概念性)

# 1. Modify state
# 1. 修改状态
ctx.session.state['status'] = 'processing'
event1 = Event(..., actions=EventActions(state_delta={'status': 'processing'}))

# 2. Yield event with the delta
# 2. 产生带有 delta 的事件
yield event1
# --- PAUSE --- Runner processes event1, SessionService commits 'status' = 'processing' ---
# --- 暂停 --- Runner 处理 event1,SessionService 提交 'status' = 'processing' ---

# 3. Resume execution
# 3. 恢复执行
# Now it's safe to rely on the committed state
# 现在可以安全地依赖提交的状态
current_status = ctx.session.state['status'] # Guaranteed to be 'processing'
# 保证是 'processing'
print(f"Status after resuming: {current_status}")
// Inside agent logic (conceptual)
// 在智能体逻辑内部(概念性)

// 1. Modify state
// 1. 修改状态
// In TypeScript, you modify state via the context, which tracks the change.
// 在 TypeScript 中,您通过上下文修改状态,上下文会跟踪更改。
ctx.state.set('status', 'processing');
// The framework will automatically populate actions with the state
// delta from the context. For illustration, it's shown here.
// 框架将自动从上下文填充具有状态 delta 的操作。
// 为了说明,在此处展示。
const event1 = createEvent({
    actions: createEventActions({stateDelta: {'status': 'processing'}}),
    // ... other event fields
    // ... 其他事件字段
});

// 2. Yield event with the delta
// 2. 产生带有 delta 的事件
yield event1;
// --- PAUSE --- Runner processes event1, SessionService commits 'status' = 'processing' ---
// --- 暂停 --- Runner 处理 event1,SessionService 提交 'status' = 'processing' ---

// 3. Resume execution
// 3. 恢复执行
// Now it's safe to rely on the committed state in the session object.
// 现在可以安全地依赖会话对象中提交的状态。
const currentStatus = ctx.session.state['status']; // Guaranteed to be 'processing'
// 保证是 'processing'
console.log(`Status after resuming: ${currentStatus}`);
  // Inside agent logic (conceptual)
  // 在智能体逻辑内部(概念性)

func (a *Agent) RunConceptual(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] {
  // The entire logic is wrapped in a function that will be returned as an iterator.
  // 整个逻辑被包装在一个将作为迭代器返回的函数中。
  return func(yield func(*session.Event, error) bool) {
      // ... previous code runs based on current state from the input `ctx` ...
      // ... 之前的代码基于输入 `ctx` 的当前状态运行 ...
      // e.g., val := ctx.State().Get("field_1") might return "value_1" here.
      // 例如,val := ctx.State().Get("field_1") 可能在此处返回 "value_1"。

      // 1. Determine a change or output is needed, construct the event
      // 1. 确定需要更改或输出,构造事件
      updateData := map[string]interface{}{"field_1": "value_2"}
      eventWithStateChange := session.NewEvent(ctx.InvocationID())
      eventWithStateChange.Author = a.Name()
      eventWithStateChange.Actions = &session.EventActions{StateDelta: updateData}
      // ... other event fields ...
      // ... 其他事件字段 ...


      // 2. Yield the event to the Runner for processing & commit.
      // 2. 将事件产生给 Runner 进行处理和提交。
      // The agent's execution continues immediately after this call.
      // 智能体的执行在此调用后立即继续。
      if !yield(eventWithStateChange, nil) {
          // If yield returns false, it means the consumer (the Runner)
          // 如果 yield 返回 false,这意味着消费者(Runner)
          // has stopped listening, so we should stop producing events.
          // 已停止监听,所以我们应该停止产生事件。
          return
      }

      // <<<<<<<<<<<< RUNNER PROCESSES & COMMITS THE EVENT >>>>>>>>>>>>
      // <<<<<<<<<<<< RUNNER 处理并提交事件 >>>>>>>>>>>>
      // This happens outside the agent, after the agent's iterator has
      // produced the event.
      // 这发生在智能体外部,在智能体的迭代器
      // 产生事件之后。

      // 3. The agent CANNOT immediately see the state change it just yielded.
      // 3. 智能体不能立即看到它刚刚产生的状态更改。
      // The state is immutable within a single `Run` invocation.
      // 状态在单个 `Run` 调用内是不可变的。
      val := ctx.State.Get("field_1")
      // `val` here is STILL "value_1" (or whatever it was at the start).
      // 这里的 `val` 仍然是 "value_1"(或开始时的任何值)。
      // The updated state ("value_2") will only be available in `ctx`
      // 更新的状态("value_2")将仅在 `ctx` 中可用
      // of the *next* `Run` invocation in a subsequent turn.
      // 在后续轮次的*下一个* `Run` 调用中。

      // ... subsequent code continues, potentially yielding more events ...
      // ... 后续代码继续,可能产生更多事件 ...
      finalEvent := session.NewEvent(ctx.InvocationID())
      finalEvent.Author = a.Name()
      // ...
      yield(finalEvent, nil)
  }
}
// Inside agent logic (conceptual)
// 在智能体逻辑内部(概念性)
// ... previous code runs based on current state ...
// ... 之前的代码基于当前状态运行 ...

// 1. Prepare state modification and construct the event
// 1. 准备状态修改并构造事件
ConcurrentHashMap<String, Object> stateChanges = new ConcurrentHashMap<>();
stateChanges.put("status", "processing");

EventActions actions = EventActions.builder().stateDelta(stateChanges).build();
Content content = Content.builder().parts(Part.fromText("Status update: processing")).build();

Event event1 = Event.builder()
    .actions(actions)
    // ...
    .build();

// 2. Yield event with the delta
// 2. 产生带有 delta 的事件
return Flowable.just(event1)
    .map(
        emittedEvent -> {
            // --- CONCEPTUAL PAUSE & RUNNER PROCESSING ---
            // --- 概念性暂停和 RUNNER 处理 ---
            // 3. Resume execution (conceptually)
            // 3. 恢复执行(概念上)
            // Now it's safe to rely on the committed state.
            // 现在可以安全地依赖提交的状态。
            String currentStatus = (String) ctx.session().state().get("status");
            System.out.println("Status after resuming (inside agent logic): " + currentStatus); // Guaranteed to be 'processing'
            // 保证是 'processing'

            // The event itself (event1) is passed on.
            // 事件本身(event1)被传递。
            // If subsequent logic within this agent step produced *another* event,
            // 如果此智能体步骤内的后续逻辑产生了*另一个*事件,
            // you'd use concatMap to emit that new event.
            // 您将使用 concatMap 来发射那个新事件。
            return emittedEvent;
        });

// ... subsequent agent logic might involve further reactive operators
// ... 后续智能体逻辑可能涉及更多响应式操作符
// or emitting more events based on the now-updated `ctx.session().state()`.
// 或基于现在更新的 `ctx.session().state()` 产生更多事件。

"Dirty Reads" of Session State

会话状态的"脏读"

  • Definition: While commitment happens after the yield, code running later within the same invocation, but before the state-changing event is actually yielded and processed, can often see the local, uncommitted changes. This is sometimes called a "dirty read".

定义: 虽然提交发生在 yield 之后,但在同一调用内之后运行、在状态更改事件实际产生和处理之前的代码,通常可以看到本地、未提交的更改。这有时被称为"脏读"。

  • Example:

示例:

# Code in before_agent_callback
# before_agent_callback 中的代码
callback_context.state['field_1'] = 'value_1'
# State is locally set to 'value_1', but not yet committed by Runner
# 状态在本地设置为 'value_1',但尚未由 Runner 提交

# ... agent runs ...
# ... 智能体运行 ...

# Code in a tool called later *within the same invocation*
# 在同一调用内稍后调用的工具中的代码
# Readable (dirty read), but 'value_1' isn't guaranteed persistent yet.
# 可读(脏读),但 'value_1' 尚不保证持久化。
val = tool_context.state['field_1'] # 'val' will likely be 'value_1' here
# 'val' 在这里可能是 'value_1'
print(f"Dirty read value in tool: {val}")

# Assume the event carrying the state_delta={'field_1': 'value_1'}
# 假设携带 state_delta={'field_1': 'value_1'} 的事件
# is yielded *after* this tool runs and is processed by the Runner.
# 在此工具运行之后并由 Runner 处理时被产生。
// Code in beforeAgentCallback
// beforeAgentCallback 中的代码
callbackContext.state.set('field_1', 'value_1');
// State is locally set to 'value_1', but not yet committed by Runner
// 状态在本地设置为 'value_1',但尚未由 Runner 提交

// --- agent runs ... ---
// --- 智能体运行 ... ---

// --- Code in a tool called later *within the same invocation* ---
// --- 在同一调用内稍后调用的工具中的代码 ---
// Readable (dirty read), but 'value_1' isn't guaranteed persistent yet.
// 可读(脏读),但 'value_1' 尚不保证持久化。
const val = toolContext.state.get('field_1'); // 'val' will likely be 'value_1' here
// 'val' 在这里可能是 'value_1'
console.log(`Dirty read value in tool: ${val}`);

// Assume the event carrying the state_delta={'field_1': 'value_1'}
// 假设携带 state_delta={'field_1': 'value_1'} 的事件
// is yielded *after* this tool runs and is processed by the Runner.
// 在此工具运行之后并由 Runner 处理时被产生。
// Code in before_agent_callback
// before_agent_callback 中的代码
// The callback would modify the context's session state directly.
// 回调将直接修改上下文的会话状态。
// This change is local to the current invocation context.
// 此更改对于当前调用上下文是本地的。
ctx.State.Set("field_1", "value_1")
// State is locally set to 'value_1', but not yet committed by Runner
// 状态在本地设置为 'value_1',但尚未由 Runner 提交

// ... agent runs ...
// ... 智能体运行 ...

// Code in a tool called later *within the same invocation*
// 在同一调用内稍后调用的工具中的代码
// Readable (dirty read), but 'value_1' isn't guaranteed persistent yet.
// 可读(脏读),但 'value_1' 尚不保证持久化。
val := ctx.State.Get("field_1") // 'val' will likely be 'value_1' here
// 'val' 在这里可能是 'value_1'
fmt.Printf("Dirty read value in tool: %v\n", val)

// Assume the event carrying the state_delta={'field_1': 'value_1'}
// 假设携带 state_delta={'field_1': 'value_1'} 的事件
// is yielded *after* this tool runs and is processed by the Runner.
// 在此工具运行之后并由 Runner 处理时被产生。
// Modify state - Code in BeforeAgentCallback
// 修改状态 - BeforeAgentCallback 中的代码
// AND stages this change in callbackContext.eventActions().stateDelta().
// 并在 callbackContext.eventActions().stateDelta() 中暂存此更改。
callbackContext.state().put("field_1", "value_1");

// --- agent runs ... ---
// --- 智能体运行 ... ---

// --- Code in a tool called later *within the same invocation* ---
// --- 在同一调用内稍后调用的工具中的代码 ---
// Readable (dirty read), but 'value_1' isn't guaranteed persistent yet.
// 可读(脏读),但 'value_1' 尚不保证持久化。
Object val = toolContext.state().get("field_1"); // 'val' will likely be 'value_1' here
// 'val' 在这里可能是 'value_1'
System.out.println("Dirty read value in tool: " + val);
// Assume the event carrying the state_delta={'field_1': 'value_1'}
// 假设携带 state_delta={'field_1': 'value_1'} 的事件
// is yielded *after* this tool runs and is processed by the Runner.
// 在此工具运行之后并由 Runner 处理时被产生。
  • Implications:

含义: * Benefit: Allows different parts of your logic within a single complex step (e.g., multiple callbacks or tool calls before the next LLM turn) to coordinate using state without waiting for a full yield/commit cycle.

**好处:** 允许您的逻辑中的不同部分在单个复杂步骤内(例如,下一个 LLM 轮次之前的多个回调或工具调用)使用状态进行协调,而无需等待完整的产生/提交循环。
  • Caveat: Relying heavily on dirty reads for critical logic can be risky. If the invocation fails before the event carrying the state_delta is yielded and processed by the Runner, the uncommitted state change will be lost. For critical state transitions, ensure they are associated with an event that gets successfully processed.

    注意事项: 过度依赖脏读进行关键逻辑可能是有风险的。如果调用在携带 state_delta 的事件被 Runner 产生和处理之前失败,未提交的状态更改将丢失。对于关键状态转换,确保它们与成功处理的事件关联。

Streaming vs. Non-Streaming Output (partial=True)

流式传输与非流式传输输出(partial=True

This primarily relates to how responses from the LLM are handled, especially when using streaming generation APIs.

这主要涉及如何处理来自 LLM 的响应,尤其是在使用流式生成 API 时。

  • Streaming: The LLM generates its response token-by-token or in small chunks.
  • The framework (often within BaseLlmFlow) yields multiple Event objects for a single conceptual response. Most of these events will have partial=True.
  • The Runner, upon receiving an event with partial=True, typically forwards it immediately upstream (for UI display) but skips processing its actions (like state_delta).
  • Eventually, the framework yields a final event for that response, marked as non-partial (partial=False or implicitly via turn_complete=True).
  • The Runner fully processes only this final event, committing any associated state_delta or artifact_delta.

流式传输: LLM 逐个令牌或以小块生成其响应。 * 框架(通常在 BaseLlmFlow 内)为单个概念性响应产生多个 Event 对象。这些事件中的大多数将具有 partial=True。 * Runner 在接收到具有 partial=True 的事件时,通常立即将其向上传递(用于 UI 显示),但跳过处理其 actions(如 state_delta)。 * 最终,框架为该响应产生一个最终事件,标记为非部分(partial=False 或通过 turn_complete=True 隐式)。 * Runner 仅完全处理此最终事件,提交任何关联的 state_deltaartifact_delta

  • Non-Streaming: The LLM generates the entire response at once. The framework yields a single event marked as non-partial, which the Runner processes fully.

非流式传输: LLM 一次性生成整个响应。框架产生一个标记为非部分的单一事件,Runner 完全处理它。

  • Why it Matters: Ensures that state changes are applied atomically and only once based on the complete response from the LLM, while still allowing the UI to display text progressively as it's generated.

为何重要: 确保基于来自 LLM 的完整响应原子地且仅一次应用状态更改,同时仍允许 UI 在生成文本时逐步显示。

Async is Primary (run_async)

异步是主要的(run_async

  • Core Design: The ADK Runtime is fundamentally built on asynchronous patterns and libraries (like Python's asyncio, Java's RxJava, and native Promises and AsyncGenerators in TypeScript) to handle concurrent operations (like waiting for LLM responses or tool executions) efficiently without blocking.

核心设计: ADK 运行时从根本上构建在异步模式和库之上(如 Python 的 asyncio、Java 的 RxJava 以及 TypeScript 中的原生 PromiseAsyncGenerator),以高效地处理并发操作(如等待 LLM 响应或工具执行)而不会阻塞。

  • Main Entry Point: Runner.run_async is the primary method for executing agent invocations. All core runnable components (Agents, specific flows) use asynchronous methods internally.

主入口点: Runner.run_async 是执行智能体调用的主要方法。所有核心可运行组件(智能体、特定流程)在内部使用 异步 方法。

  • Synchronous Convenience (run): A synchronous Runner.run method exists mainly for convenience (e.g., in simple scripts or testing environments). However, internally, Runner.run typically just calls Runner.run_async and manages the async event loop execution for you.

同步便利性(run): 同步 Runner.run 方法主要为了便利(例如,在简单脚本或测试环境中)而存在。但是,在内部,Runner.run 通常只是调用 Runner.run_async 并为您管理异步事件循环执行。

  • Developer Experience: We recommend designing your applications (e.g., web servers using ADK) to be asynchronous for best performance. In Python, this means using asyncio; in Java, leverage RxJava's reactive programming model; and in TypeScript, this means building using native Promises and AsyncGenerators.

开发者体验: 我们建议设计您的应用程序(例如,使用 ADK 的 Web 服务器)为异步以获得最佳性能。在 Python 中,这意味着使用 asyncio;在 Java 中,利用 RxJava 的响应式编程模型;在 TypeScript 中,这意味着使用原生 PromiseAsyncGenerator 构建。

  • Sync Callbacks/Tools: The ADK framework supports both asynchronous and synchronous functions for tools and callbacks.

同步回调/工具: ADK 框架支持工具和回调的异步和同步函数。 * Blocking I/O: For long-running synchronous I/O operations, the framework attempts to prevent stalls. Python ADK may use asyncio.to_thread, while Java ADK often relies on appropriate RxJava schedulers or wrappers for blocking calls. In TypeScript, the framework simply awaits the function; if a synchronous function performs blocking I/O, it will stall the event loop. Developers should use asynchronous I/O APIs (which return a Promise) whenever possible.

  **阻塞 I/O:** 对于长时间运行的同步 I/O 操作,框架尝试防止停滞。Python ADK 可能使用 asyncio.to_thread,而 Java ADK 通常依赖适当的 RxJava 调度器或阻塞调用的包装器。在 TypeScript 中,框架简单地等待函数;如果同步函数执行阻塞 I/O,它将停滞事件循环。开发者应尽可能使用异步 I/O API(返回 Promise)。

* **CPU-Bound Work:** Purely CPU-intensive synchronous tasks will still block their execution thread in both environments.

  **CPU 密集型工作:** 纯粹的 CPU 密集型同步任务仍将在两种环境中阻塞其执行线程。

Understanding these behaviors helps you write more robust ADK applications and debug issues related to state consistency, streaming updates, and asynchronous execution.

理解这些行为有助于您编写更健壮的 ADK 应用程序并调试与状态一致性、流式更新和异步执行相关的问题。