Files
YiemAgent/docs/agent_loop_sequence_diagram.md
T
2026-07-28 09:57:23 +07:00

12 KiB

Agent Loop Sequence Diagram (ASCII)

┌─────────────┐     ┌─────────────┐     ┌─────────────────┐     ┌───────────┐
│    User     │─────>│   Agent     │─────>│   AgentLoop     │─────>│   LLM     │
└─────────────┘     └─────────────┘     └─────────────────┘     └───────────┘
                                                                           │
                                                                           ▼
┌─────────────┐     ┌─────────────────┐     ┌───────────┐     ┌─────────────┐
│    User     │<─────│   AgentLoop     │─────>│   Agent   │─────>│   Session │
└─────────────┘     └─────────────────┘     └───────────┘     └─────────────┘
                                                                           │
                                                                           ▼
                                                                    ┌─────────────┐
                                                                    │   Session   │
                                                                    └─────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ AGENT LOOP MAIN FLOW                                                                    │
└─────────────────────────────────────────────────────────────────────────────────────────┘

User
  │
  │ prompt(messages)
  ▼
Agent
  │
  │ normalizePromptInput()
  │ runPromptMessages()
  │
  ├─ alt steering_queue has messages
  │   │ drain(steering_queue)
  │   │ runPromptMessages()
  │
  ├─ alt follow_up_queue has messages
  │   │ drain(follow_up_queue)
  │   │ runPromptMessages()
  │
  └─ else (normal prompt)
      │ createLoopConfig()
      │ createActiveRun()
      │
      ▼
    AgentLoop
      │
      │ agentLoop(messages, context, config)
      │
      ▼
    ┌─────────────────────────────────────────────────────────────┐
    │ MAIN LOOP                                                   │
    ├─────────────────────────────────────────────────────────────┤
    │                                                             │
    │ 1. Handle queued messages                                   │
    │    ├─ drain(steering_queue)                                 │
    │    ├─ emit(MessageStartEvent)                              │
    │    ├─ push to context.messages                             │
    │    └─ emit(MessageEndEvent)                                │
    │                                                             │
    │ 2. Get LLM Response                                         │
    │    ├─ transform_context?(messages)                         │
    │    ├─ convert_to_llm(messages)                             │
    │    └─ stream_function(model, context, config)             │
    │                                                             │
    │    LLM                                                     │
    │      │ response stream                                     │
    │      └─ emit(MessageStartEvent)                           │
    │      └─ emit(MessageUpdateEvent) (streaming)             │
    │      └─ emit(MessageEndEvent(final_message))             │
    │                                                             │
    │ 3. Check for Tool Calls                                     │
    │    └─ extract_tool_calls(message.content)                 │
    │                                                             │
    │ 4. Execute Tools (if any)                                   │
    │    ├─ emit(ToolExecutionStartEvent)                      │
    │    ├─ prepareToolCall()                                    │
    │    │   ├─ before_tool_call? (block if needed)            │
    │    │   └─ validateToolArguments()                         │
    │    ├─ execute(tool_call_id, args)                         │
    │    │   Tool                                                │
    │    │     │ execute()                                      │
    │    │     └─ on_update(partial_result)                    │
    │    ├─ emit(ToolExecutionEndEvent)                        │
    │    └─ createToolResultMessage()                           │
    │                                                             │
    │ 5. Emit Turn End Event                                      │
    │    └─ emit(TurnEndEvent(message, tool_results))          │
    │                                                             │
    │ 6. Prepare Next Turn                                        │
    │    └─ prepare_next_turn(context)                          │
    │        └─ AgentLoopTurnUpdate                             │
    │                                                             │
    │ 7. Check Stop Condition                                     │
    │    └─ should_stop_after_turn? (break if true)            │
    │                                                             │
    │ 8. Get Steering/Follow-up Messages                          │
    │    ├─ drain(steering_queue)                                │
    │    └─ drain(follow_up_queue)                               │
    │                                                             │
    │ 9. Continue or Break                                        │
    │    ├─ alt pending messages exist → continue loop          │
    │    └─ else → break                                        │
    │                                                             │
    └─────────────────────────────────────────────────────────────┘
            │
            ▼
      AgentLoop
        │
        │ emit(AgentEndEvent(messages))
        │
        ▼
      Agent
        │
        │ appendMessage() → Session
        │
        ▼
      User (via promise)

KEY COMPONENTS

Agent Layer

┌──────────────────────────────────────────────────────────────┐
│ Agent                                                        │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ state: AgentState                                        │ │
│ │   - system_prompt                                        │ │
│ │   - model                                                │ │
│ │   - messages[]                                           │ │
│ │   - tools[]                                              │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Queues:                                                  │ │
│ │ - steering_queue: inject after assistant turn            │ │
│ │ - follow_up_queue: run when agent stops                  │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

AgentLoop Layer

┌──────────────────────────────────────────────────────────────┐
│ AgentLoop                                                    │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ agentLoop()                                              │ │
│ │   - Main loop execution                                  │ │
│ │   - Tool orchestration                                   │ │
│ │   - Event emission                                       │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ executeToolCallsSequential()                             │ │
│ │ executeToolCallsParallel()                               │ │
│ │   - Tool execution coordination                          │ │
│ │   - Hook invocation                                      │ │
│ │   - Result collection                                    │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

Lifecycle Events Flow

AgentStartEvent
    │
    ├─ TurnStartEvent
    │   ├─ MessageStartEvent (user/assistant)
    │   ├─ MessageEndEvent
    │   ├─ ToolExecutionStartEvent
    │   │   ├─ ToolExecutionUpdateEvent (streaming)
    │   │   └─ ToolExecutionEndEvent
    │   └─ TurnEndEvent
    │
    └─ AgentEndEvent (with final messages)

QUEUE PROCESSING ORDER

  1. Initial steering messages (if any)
  2. Main loop:
    • Steering/follow-up messages (if any)
    • LLM call
    • Tool execution (if any)
    • Turn end event
    • Next turn preparation
    • Check stop condition
  3. Follow-up messages (after loop exits, only if no steering)

DATA FLOW

Input:
  User messages → Agent.normalizePromptInput() → AgentLoop

LLM Call:
  AgentMessage[] → transform_context() → convert_to_llm() → 
  LLM.stream() → AssistantMessage

Tool Execution:
  ToolCall[] → prepareToolCall() → 
  before_tool_call? → execute() → after_tool_call? → 
  ToolResultMessage[]

Output:
  AgentEndEvent(messages) → Session.appendMessage() → User promise

EXECUTION MODES

Sequential (EXECUTION_SEQUENTIAL)

ToolCall1 → ToolCall2 → ToolCall3
   │          │          │
   ▼          ▼          ▼
Result1     Result2     Result3

Parallel (EXECUTION_PARALLEL)

ToolCall1 ─┐
ToolCall2──┼→ Execute all → Wait for all → Results
ToolCall3 ─┘