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

47 KiB

Agent Loop Diagram

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

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 1. INITIALIZATION                                                                                                                                         │
│    ┌──────────────────────┐                                                                                                                               │
│    │  Agent.start(prompt) │                                                                                                                               │
│    └──────────┬───────────┘                                                                                                                               │
│               │                                                                                                                                           │
│               ▼                                                                                                                                           │
│    ┌──────────────────────┐                                                                                                                               │
│    │  normalizePrompt()   │ ← Convert input (String/Message/Vector) to AgentMessage[]                                                                    │
│    └──────────┬───────────┘                                                                                                                               │
│               │                                                                                                                                           │
│               ▼                                                                                                                                           │
│    ┌──────────────────────┐                                                                                                                               │
│    │  runPromptMessages() │                                                                                                                              │
│    └──────────┬───────────┘                                                                                                                               │
│               │                                                                                                                                           │
│               ▼                                                                                                                                           │
│    ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│    │ 2. AGENT LOOP START (agentLoop/runAgentLoop)                                                                                                  │ │
│    │                                                                                                                                               │ │
│    │  emit(AgentStartEvent)                                                                                                                        │ │
│    │  emit(TurnStartEvent)                                                                                                                         │ │
│    │                                                                                                                                               │ │
│    │  for prompt in prompts:                                                                                                                       │ │
│    │    emit(MessageStartEvent(prompt))                                                                                                            │ │
│    │    emit(MessageEndEvent(prompt))                                                                                                              │ │
│    │                                                                                                                                               │ │
│    │  ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │ │
│    │  │ 3. MAIN LOOP (while true)                                                                                                         │    │ │
│    │  │                                                                                                                                   │    │ │
│    │  │  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │    │ │
│    │  │  │ 4. PENDING MESSAGE HANDLING                                                                                             │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  pending_messages = getSteeringMessages()                                                                               │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  while has pending_messages OR has_tool_calls:                                                                         │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │    if pending_messages:                                                                                                  │    │    │ │
│    │  │  │      for msg in pending_messages:                                                                                       │    │    │ │
│    │  │  │        emit(MessageStartEvent)                                                                                          │    │    │ │
│    │  │  │        emit(MessageEndEvent)                                                                                            │    │    │ │
│    │  │  │        push to context.messages                                                                                         │    │    │ │
│    │  │  │      pending_messages = []                                                                                              │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │    │ │
│    │  │  │ 5. STREAM ASSISTANT RESPONSE                                                                                            │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  a) transform_context (if configured)                                                                                  │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  b) convert_to_llm(messages)                                                                                           │    │    │ │
│    │  │  │     ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐  │    │    │ │
│    │  │  │     │ Converts AgentMessage[] to Message[] (filters out compaction/branch summaries, converts bash/custom)         │  │    │    │ │
│    │  │  │     └──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘  │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  c) create Context(system_prompt, llm_messages, tools)                                                                │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  d) stream_function(model, context, config)                                                                            │    │    │ │
│    │  │  │     ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐  │    │    │ │
│    │  │  │     │ LLM Stream Events:                                                                                              │  │    │    │ │
│    │  │  │     │  • start → create partial AssistantMessage                                                                     │  │    │    │ │
│    │  │  │     │  • text_start/delta/end → update partial message                                                               │  │    │    │ │
│    │  │  │     │  • thinking_start/delta/end → update partial message                                                           │  │    │    │ │
│    │  │  │     │  • toolcall_start/delta/end → update partial message                                                           │  │    │    │ │
│    │  │  │     │  • done → finalize message                                                                                      │  │    │    │ │
│    │  │  │     │  • error → handle error                                                                                         │  │    │    │ │
│    │  │  │     └──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘  │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  e) emit(MessageStartEvent(final_message))                                                                             │    │    │ │
│    │  │  │  f) emit(MessageEndEvent(final_message))                                                                               │    │    │ │
│    │  │  │  g) push to context.messages & new_messages                                                                            │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │    │ │
│    │  │  │ 6. CHECK STOP REASON                                                                                                    │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  if stop_reason in ("error", "aborted"):                                                                               │    │    │ │
│    │  │  │    emit(TurnEndEvent)                                                                                                   │    │    │ │
│    │  │  │    emit(AgentEndEvent) ← EXIT LOOP                                                                                     │    │    │ │
│    │  │  │    return                                                                                                                │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │    │ │
│    │  │  │ 7. EXTRACT TOOL CALLS                                                                                                   │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  tool_calls = filter(content, isa ToolCall)                                                                            │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  if tool_calls:                                                                                                         │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │   ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐   │    │    │ │
│    │  │  │   │ 8. EXECUTE TOOL CALLS                                                                                             │   │    │    │ │
│    │  │  │   │                                                                                                                   │   │    │    │ │
│    │  │  │   │  if config.tool_execution == SEQUENTIAL                                                                            │   │    │    │ │
│    │  │  │   │    OR has_sequential_tool:                                                                                         │   │    │    │ │
│    │  │  │   │      executeToolCallsSequential()                                                                                  │   │    │    │ │
│    │  │  │   │   else:                                                                                                            │   │    │    │ │
│    │  │  │   │      executeToolCallsParallel()                                                                                    │   │    │    │ │
│    │  │  │   │                                                                                                                   │   │    │    │ │
│    │  │  │   │   ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐  │   │    │    │ │
│    │  │  │   │   │ For EACH tool_call:                                                                                           │  │   │    │    │ │
│    │  │  │   │   │                                                                                                               │  │   │    │    │ │
│    │  │  │   │   │   emit(ToolExecutionStartEvent)                                                                               │  │   │    │    │ │
│    │  │  │   │   │                                                                                                               │  │   │    │    │ │
│    │  │  │   │   │   prepareToolCall():                                                                                          │  │   │    │    │ │
│    │  │  │   │   │     • Find tool by name                                                                                       │  │   │    │    │ │
│    │  │  │   │   │     • prepare_arguments (if configured)                                                                       │  │   │    │    │ │
│    │  │  │   │   │     • validateToolArguments                                                                                   │  │   │    │    │ │
│    │  │  │   │   │     • before_tool_call hook (if configured)                                                                   │  │   │    │    │ │
│    │  │  │   │   │     └─→ PreparedToolCall("prepared") or ImmediateToolCallOutcome("immediate")                                  │  │   │    │    │ │
│    │  │  │   │   │                                                                                                               │  │   │    │    │ │
│    │  │  │   │   │   if "immediate":                                                                                             │  │   │    │    │ │
│    │  │  │   │   │     FinalizedToolCallOutcome (synchronous tool)                                                               │  │   │    │    │ │
│    │  │  │   │   │   else:                                                                                                       │  │   │    │    │ │
│    │  │  │   │   │     executePreparedToolCall() → ExecutedToolCallOutcome                                                      │  │   │    │    │ │
│    │  │  │   │   │     finalizeExecutedToolCall() (after_tool_call hook)                                                        │  │   │    │    │ │
│    │  │  │   │   │     └─→ FinalizedToolCallOutcome                                                                              │  │   │    │    │ │
│    │  │  │   │   │                                                                                                               │  │   │    │    │ │
│    │  │  │   │   │   emit(ToolExecutionEndEvent)                                                                                 │  │   │    │    │ │
│    │  │  │   │   │   createToolResultMessage()                                                                                   │  │   │    │    │ │
│    │  │  │   │   │   emitToolResultMessage()                                                                                     │  │   │    │    │ │
│    │  │  │   │   │                                                                                                               │  │   │    │    │ │
│    │  │  │   │   └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘  │   │    │    │ │
│    │  │  │   │                                                                                                                   │   │    │    │ │
│    │  │  │   │   Append tool_result_messages to context.messages & new_messages                                                 │   │    │    │ │
│    │  │  │   │                                                                                                                   │   │    │    │ │
│    │  │  │   └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘   │    │    │ │
│    │  │  │                                                                                                                           │    │    │ │
│    │  │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  emit(TurnEndEvent(message, tool_results))                                                                                    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐    │    │ │
│    │  │  │ 9. PREPARE NEXT TURN                                                                                                    │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  next_turn_context = PrepareNextTurnContext(                                                                            │    │    │ │
│    │  │  │    message, tool_results, current_context, new_messages                                                                │    │    │ │
│    │  │  │  )                                                                                                                      │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  next_turn_snapshot = prepare_next_turn(config, next_turn_context)                                                     │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  if !isnothing(next_turn_snapshot):                                                                                     │    │    │ │
│    │  │  │    update context, model, thinking_level                                                                               │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  │  if should_stop_after_turn(config, next_turn_context):                                                                 │    │    │ │
│    │  │  │    emit(AgentEndEvent) ← EXIT LOOP                                                                                     │    │    │ │
│    │  │  │    return                                                                                                                │    │    │ │
│    │  │  │                                                                                                                         │    │    │ │
│    │  │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │    │ │
│    │  │                                                                                                                               │    │ │
│    │  │  pending_messages = getSteeringMessages() ← Check for new steering messages                                                  │    │ │
│    │  │                                                                                                                               │    │ │
│    │  └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘    │ │
│    │                                                                                                                                        │ │
│    │  follow_up_messages = getFollowUpMessages()                                                                                          │ │
│    │                                                                                                                                       │ │
│    │  if follow_up_messages:                                                                                                              │ │
│    │    pending_messages = follow_up_messages  ← Continue loop to handle follow-ups                                                      │ │
│    │    continue                                                                                                                          │ │
│    │                                                                                                                                       │ │
│    │  break  ← EXIT MAIN LOOP (no more pending messages)                                                                                  │ │
│    │                                                                                                                                       │ │
│    │  emit(AgentEndEvent(new_messages))                                                                                                   │ │
│    │                                                                                                                                       │ │
│    └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                                                                              │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ HELPER STRUCTURES                                                                                                                                         │
│                                                                                                                                                           │
│  AgentMessage (union of):                                                                                                                                 │
│    • UserMessage  ("user")                                                                                                                                │
│    • AssistantMessage  ("assistant")                                                                                                                      │
│    • ToolResultMessage  ("toolResult")                                                                                                                    │
│    • BranchSummaryMessage  ("branchSummary") - converted to UserMessage                                                                                   │
│    • CompactionSummaryMessage  ("compactionSummary") - converted to UserMessage                                                                          │
│    • BashExecutionMessage  ("bash") - converted to UserMessage if not excluded                                                                           │
│    • CustomMessage  ("custom") - converted to UserMessage                                                                                                 │
│                                                                                                                                                           │
│  Message (LLM interface):                                                                                                                                 │
│    • UserMessage  (role: "user")                                                                                                                          │
│    • AssistantMessage  (role: "assistant")                                                                                                                │
│    • ToolResultMessage  (role: "toolResult")                                                                                                              │
│                                                                                                                                                           │
│  Steering vs Follow-up:                                                                                                                                   │
│    • Steering: Injected AFTER current assistant turn finishes (can continue conversation)                                                                 │
│    • Follow-up: Run ONLY after agent would otherwise stop (final messages)                                                                                │
│                                                                                                                                                           │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DATA FLOW SUMMARY                                                                                                                                         │
│                                                                                                                                                           │
│  User Input                                                                                      LLM Response                                          │
│       │                                       convert_to_llm()                                    │                                          │
│       ├───────────────────────────────────→ AgentMessage[] ──────────────→ Message[] ──────────→ │ ─────────────────→ AssistantMessage (streamed)      │
│       │                                       (filter+convert)                                    │                                          │
│       │                                                                           │                     │                                         │
│       │                                                                           ▼                     │                                         │
│       │                                                                    Context (system_prompt,     │                                         │
│       │                                                                          messages, tools)     │                                         │
│       │                                                                                             │                                         │
│       │                                                                                             │                                         │
│       │                                                                    stream_function() ─────────┤ ─────────→ LLM API                       │
│       │                                                                                             │                                         │
│       │                                                                                             │                                         │
│       │                                                                                             │                                         │
│       │                                                                                             ▼                                         │
│       │                                                                                        LLM Stream Events (text_delta, toolcall_delta, ...) │
│       │                                                                                             │                                         │
│       │                                                                                             ▼                                         │
│       │                                                                                    AssistantMessage (finalized)                            │
│       │                                                                                             │                                         │
│       │                                                                                             ▼                                         │
│       │                                                                                    check tool_calls? ──────────→ YES ────────────→ execute tools  │
│       │                                                                                             │                                          │          │
│       │                                                                                             │                                          │          │
│       │                                                                                             NO                                         │          │
│       │                                                                                             │                                          │          │
│       │                                                                                             │                                          ▼          ▼
│       │                                                                                             │                                    emit ToolResultMessage
│       │                                                                                             │                                          │          │
│       │                                                                                             │                                          │          │
│       │                                                                                             ▼                                          ▼
│       │                                                                                    append to context.messages ────────────────────────────────→ ────→ next turn
│       │                                                                                                                                                 │
│       │                                                                                                                                                 ▼
│       │                                                                                                                                             check pending/follow-up
│       │                                                                                                                                                 │
│       │                                                                                                                                                 │
│       └─────────────────────────────────────────────────────────────────────────────────→ ────────────────────────────────────────────────────────────┘
│                                                                                             (loop back to main loop or end)
│
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Key Flow Points

  1. Input Normalization: User input (String/Message/Vector) → AgentMessage[]
  2. LLM Boundary: AgentMessage[] → Message[] (filtered, converted for LLM)
  3. Streaming: LLM returns event stream → AssistantMessage (built incrementally)
  4. Tool Execution:
    • Extract ToolCall objects from AssistantMessage.content
    • Execute in parallel (default) or sequential mode
    • Emit ToolResultMessage for each
  5. Message Queues:
    • Steering: Injected between turns (keeps conversation going)
    • Follow-up: Injected after agent stops (final messages)
  6. Context Updates: All messages appended to context.messages for next iteration
  7. Loop Control: Continue until no pending messages AND agent signals stop

Hook Points

  • transform_context: Modify messages before LLM call
  • convert_to_llm: Transform AgentMessage[] to Message[]
  • before_tool_call: Block or modify tool execution
  • after_tool_call: Modify tool results
  • prepare_next_turn: Update context/model/thinking level between turns
  • should_stop_after_turn: Signal when agent should terminate