Files
YiemAgent/docs/agent_loop_diagram.md
T
2026-07-28 10:31:11 +07:00

12 KiB

Agent Loop Diagram

Overview

This document describes the agent loop execution flow in AgentCore.jl, showing how the agent processes messages, executes tools, and handles steering/follow-up messages.

Architecture Layers

┌─────────────────────────────────────────────────────────────────────────┐
│                         Agent (High-Level)                              │
│  - State management, event streaming, queueing                          │
│  - Steering queue (steer()) and Follow-up queue (followUp())            │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                      AgentLoop (Low-Level)                              │
│  - Core loop execution with tool calling                                │
│  - Tool execution (parallel/sequential)                                 │
│  - Event emission lifecycle                                             │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                          LLM Provider                                   │
│  - Stream function calls the LLM API                                    │
│  - Returns assistant response (text + tool calls)                       │
└─────────────────────────────────────────────────────────────────────────┘

Main Agent Loop Flow

flowchart TD
    Start[Start Agent Loop] --> EmitStart[Emitter: AgentStartEvent]
    EmitStart --> EmitTurnStart[Emitter: TurnStartEvent]
    
    subgraph SteeringQueue "Check Steering Queue"
        SteeringQueue --> HasSteering{Has pending<br/>steering messages?}
        HasSteering -- Yes --> ProcessSteering[Process steering messages<br/>Emit MessageStart/End]
        ProcessSteering --> AddToContext[Add to context messages]
        AddToContext --> PollSteering
        HasSteering -- No --> PollSteering
    end
    
    PollSteering{Next iteration:<br/>Poll steering queue again}
    
    subgraph AssistantLoop "Assistant Response Loop"
        PollSteering --> StreamResponse[Stream Assistant Response from LLM]
        StreamResponse --> CheckStop{Stop reason?}
        
        CheckStop -- error/aborted --> EmitTurnEnd1[Emit: TurnEndEvent]
        EmitTurnEnd1 --> EmitAgentEnd1[Emit: AgentEndEvent]
        EmitAgentEnd1 --> End1[End Loop]
        
        CheckStop -- length/truncated --> FailTools[Fail all tool calls<br/>with error message]
        CheckStop -- normal --> ExtractTools[Extract ToolCall objects]
        
        FailTools --> HasMoreTools
        ExtractTools --> HasMoreTools{Has tool<br/>calls?}
        
        HasMoreTools -- Yes --> ExecuteTools[Execute Tools]
        HasMoreTools -- No --> CheckFollowUp
        
        subgraph ToolExecution "Tool Execution"
            direction TB
            ExecuteTools --> ExecMode{Execution Mode?}
            
            ExecMode -- Sequential --> SeqLoop[For each tool call:]
            ExecMode -- Parallel --> ParaLoop[For each tool call:]
            
            SeqLoop --> SeqPrepare[Prepare tool call]
            ParaLoop --> ParaPrepare[Prepare tool call]
            
            SeqPrepare --> SeqExec[Execute sequentially]
            ParaPrepare --> ParaExec[Execute in parallel<br/>& collect results]
            
            SeqExec --> SeqFinalize[Finalize tool call<br/> Emit: ToolExecutionEndEvent]
            ParaExec --> ParaFinalize[Finalize tool calls<br/>Emit: ToolExecutionEndEvent]
            
            SeqFinalize --> SeqResults[Create ToolResultMessages]
            ParaFinalize --> ParaResults[Create ToolResultMessages]
            
            SeqResults --> AddToolResults
            ParaResults --> AddToolResults[Add tool results to context<br/>Emit: MessageStart/End]
        end
        
        AddToolResults --> HasMoreTools
        
        CheckFollowUp{Has pending<br/>follow-up messages?}
        CheckFollowUp -- Yes --> ProcessFollowUp[Process follow-up messages<br/>Emit MessageStart/End]
        ProcessFollowUp --> AddToContext2[Add to context messages]
        AddToContext2 --> CheckPrepareNext
        
        CheckPrepareNext{Should prepare<br/>next turn?}
        CheckPrepareNext -- Yes --> PrepareNext[Call prepare_next_turn hook]
        PrepareNext --> UpdateContext[Update context & config]
        UpdateContext --> CheckStop2
        CheckPrepareNext -- No --> CheckStop2
        
        CheckStop2{Stop after turn?}
        CheckStop2 -- Yes --> EmitAgentEnd2[Emit: AgentEndEvent]
        EmitAgentEnd2 --> End2[End Loop]
        CheckStop2 -- No --> PollSteering
    end
    
    CheckFollowUp -- No --> CheckPrepareNext

Detailed Event Lifecycle

sequenceDiagram
    participant Agent
    participant Loop as AgentLoop
    participant Emitter as Event Sink
    participant LLM
    participant Tools
    
    Agent->>Loop: agentLoop(prompts, context, config)
    Loop->>Emitter: AgentStartEvent
    Loop->>Emitter: TurnStartEvent
    
    note over Loop: Process prompts
    Loop->>Emitter: MessageStartEvent(prompt)
    Loop->>Emitter: MessageEndEvent(prompt)
    
    Loop->>LLM: stream_function(model, context, config)
    LLM-->>Loop: AssistantResponse with ToolCalls
    
    note over Loop: Process response
    Loop->>Emitter: MessageStartEvent(assistant)
    
    alt Has Tool Calls
        Loop->>Tools: Execute tools
        Tools-->>Loop: ToolResults
        
        note over Loop: Emit tool execution events
        Loop->>Emitter: ToolExecutionStartEvent
        Loop->>Emitter: ToolExecutionUpdateEvent (if streaming)
        Loop->>Emitter: ToolExecutionEndEvent
        
        Loop->>Emitter: MessageStartEvent(toolResult)
        Loop->>Emitter: MessageEndEvent(toolResult)
    end
    
    Loop->>Emitter: MessageEndEvent(assistant)
    Loop->>Emitter: TurnEndEvent(message, tool_results)
    
    note over Loop: Check for follow-up/steering
    alt Has more work
        Loop->>Loop: Continue loop
    else Done
        Loop->>Emitter: AgentEndEvent(messages)
    end

Steering vs Follow-up Messages

graph LR
    subgraph "Main Conversation"
        A[User Message] --> B[Assistant Response]
        B --> C[Tool Execution]
        C --> D[Tool Result]
        D --> E{Decision Point}
    end
    
    E -->|Continue loop| B
    E -->|Stop & check queues| F
    
    subgraph "Steering Queue"
        steer[steer(message)] --> SQueue[Queued after<br/>assistant turn]
        SQueue --> SProcess[Processed in<br/>next turn iteration]
    end
    
    subgraph "Follow-up Queue"
        follow[followUp(message)] --> FQueue[Queued to run<br/>when agent would stop]
        FQueue --> FProcess[Processed only<br/>when no more work]
    end
    
    F --> SProcess
    F --> FProcess

Tool Execution Modes

Sequential Execution

flowchart LR
    Tool1[Tool Call 1] --> Prepare1[Prepare]
    Prepare1 --> Execute1[Execute]
    Execute1 --> Finalize1[Finalize]
    Finalize1 --> Result1[Tool Result 1]
    
    Result1 --> Prepare2[Prepare]
    Prepare2 --> Execute2[Execute]
    Execute2 --> Finalize2[Finalize]
    Finalize2 --> Result2[Tool Result 2]
    
    note right of Execute1 "Must complete before next tool"
    note right of Execute2 "Tools run one at a time"

Parallel Execution

flowchart LR
    Tool1[Tool Call 1] --> Prepare1[Prepare]
    Tool2[Tool Call 2] --> Prepare2[Prepare]
    Tool3[Tool Call 3] --> Prepare3[Prepare]
    
    Prepare1 --> Execute1[Execute]
    Prepare2 --> Execute2[Execute]
    Prepare3 --> Execute3[Execute]
    
    Execute1 --> Finalize1[Finalize] --> Result1[Tool Result 1]
    Execute2 --> Finalize2[Finalize] --> Result2[Tool Result 2]
    Execute3 --> Finalize3[Finalize] --> Result3[Tool Result 3]
    
    note right of Prepare1 "Preparation can happen"
    note right of Execute1 "All tools execute"
    note right of Finalize1 "Results collected in order"

Message Conversion at LLM Boundary

flowchart LR
    subgraph "Agent Messages (Internal)"
        AM1[UserMessage]
        AM2[AssistantMessage]
        AM3[ToolResultMessage]
        AM4[CompactionSummaryMessage]
        AM5[BranchSummaryMessage]
        AM6[CustomMessage]
    end
    
    AM1 --> Convert[convertToLlm]
    AM2 --> Convert
    AM3 --> Convert
    AM4 --> Convert
    AM5 --> Convert
    AM6 --> Convert
    
    Convert --> LM1[UserMessage]
    Convert --> LM2[AssistantMessage]
    Convert --> LM3[ToolResultMessage]
    Convert --> LM4[UserMessage (summary)]
    Convert --> LM5[UserMessage (branch)]
    Convert --> LM6[UserMessage (custom)]
    
    LM1 --> LLM[LLM API]
    LM2 --> LLM
    LM3 --> LLM
    LM4 --> LLM
    LM5 --> LLM
    LM6 --> LLM

Key Data Structures

AgentContext

struct AgentContext
    system_prompt::String
    messages::Vector{AgentMessage}
    tools::Union{Vector{AgentTool}, Nothing}
end

AgentLoopConfig

struct AgentLoopConfig
    model::Model
    reasoning::Union{ThinkingLevel, Nothing}
    session_id::Union{String, Nothing}
    convert_to_llm::Function
    transform_context::Union{Function, Nothing}
    get_api_key::Union{Function, Nothing}
    get_steering_messages::Function
    get_follow_up_messages::Function
    before_tool_call::Union{Function, Nothing}
    after_tool_call::Union{Function, Nothing}
    prepare_next_turn::Union{Function, Nothing}
    should_stop_after_turn::Function
    tool_execution::ToolExecutionMode
    # ... other options
end

Entry Points

  1. prompt(agent, input) - Start new conversation

    • Validates no active run
    • Normalizes input to messages
    • Calls runPromptMessages
  2. continue!(agent) - Continue from last message

    • Checks last message is not assistant
    • Drains steering/follow-up queues first
    • Calls agent loop continuation

Termination Conditions

The agent loop terminates when:

  1. LLM response has stop_reason = "error" or "aborted"
  2. No tool calls and no pending steering/follow-up messages
  3. should_stop_after_turn returns true
  4. Agent is aborted via abort signal

Event Summary

Event When Emitted
AgentStartEvent Loop begins
TurnStartEvent Each conversation turn
MessageStartEvent Message added to context
MessageEndEvent Message fully processed
MessageUpdateEvent Streaming updates
ToolExecutionStartEvent Tool execution begins
ToolExecutionUpdateEvent Tool execution progress
ToolExecutionEndEvent Tool execution completes
TurnEndEvent Turn completes
AgentEndEvent Loop terminates