From dab2264c5509bb80538c79dcfa0ab6b2d654d698 Mon Sep 17 00:00:00 2001 From: narawat Date: Tue, 28 Jul 2026 10:31:11 +0700 Subject: [PATCH] update --- docs/agent_loop_diagram.md | 325 ++++++++++++++++++++++++++++ docs/agent_loop_sequence_diagram.md | 130 ----------- 2 files changed, 325 insertions(+), 130 deletions(-) create mode 100644 docs/agent_loop_diagram.md delete mode 100644 docs/agent_loop_sequence_diagram.md diff --git a/docs/agent_loop_diagram.md b/docs/agent_loop_diagram.md new file mode 100644 index 0000000..2e35f96 --- /dev/null +++ b/docs/agent_loop_diagram.md @@ -0,0 +1,325 @@ +# 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 + +```mermaid +flowchart TD + Start[Start Agent Loop] --> EmitStart[Emitter: AgentStartEvent] + EmitStart --> EmitTurnStart[Emitter: TurnStartEvent] + + subgraph SteeringQueue "Check Steering Queue" + SteeringQueue --> HasSteering{Has pending
steering messages?} + HasSteering -- Yes --> ProcessSteering[Process steering messages
Emit MessageStart/End] + ProcessSteering --> AddToContext[Add to context messages] + AddToContext --> PollSteering + HasSteering -- No --> PollSteering + end + + PollSteering{Next iteration:
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
with error message] + CheckStop -- normal --> ExtractTools[Extract ToolCall objects] + + FailTools --> HasMoreTools + ExtractTools --> HasMoreTools{Has tool
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
& collect results] + + SeqExec --> SeqFinalize[Finalize tool call
Emit: ToolExecutionEndEvent] + ParaExec --> ParaFinalize[Finalize tool calls
Emit: ToolExecutionEndEvent] + + SeqFinalize --> SeqResults[Create ToolResultMessages] + ParaFinalize --> ParaResults[Create ToolResultMessages] + + SeqResults --> AddToolResults + ParaResults --> AddToolResults[Add tool results to context
Emit: MessageStart/End] + end + + AddToolResults --> HasMoreTools + + CheckFollowUp{Has pending
follow-up messages?} + CheckFollowUp -- Yes --> ProcessFollowUp[Process follow-up messages
Emit MessageStart/End] + ProcessFollowUp --> AddToContext2[Add to context messages] + AddToContext2 --> CheckPrepareNext + + CheckPrepareNext{Should prepare
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 + +```mermaid +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 + +```mermaid +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
assistant turn] + SQueue --> SProcess[Processed in
next turn iteration] + end + + subgraph "Follow-up Queue" + follow[followUp(message)] --> FQueue[Queued to run
when agent would stop] + FQueue --> FProcess[Processed only
when no more work] + end + + F --> SProcess + F --> FProcess +``` + +## Tool Execution Modes + +### Sequential Execution +```mermaid +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 +```mermaid +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 + +```mermaid +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 +```julia +struct AgentContext + system_prompt::String + messages::Vector{AgentMessage} + tools::Union{Vector{AgentTool}, Nothing} +end +``` + +### AgentLoopConfig +```julia +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 | diff --git a/docs/agent_loop_sequence_diagram.md b/docs/agent_loop_sequence_diagram.md deleted file mode 100644 index 6b1b4ea..0000000 --- a/docs/agent_loop_sequence_diagram.md +++ /dev/null @@ -1,130 +0,0 @@ -# Agent Loop Sequence Diagram - -``` -+---------+ +---------+ +------------+ +-------+ +-------+ -| User | | Agent | | AgentLoop | | LLM | | Tool | -+---------+ +---------+ +------------+ +-------+ +-------+ - | | | | | - | prompt(messages)| | | | - |---------------->--> | | | - | | normalizePrompt()| | | - | |----------------->>| | | - | | runPromptMessages| | | - | |----------------->>| | | - | | | | | - | | createLoopConfig | | | - | |----------------->>| | | - | | | | | - | | agentLoop() | | | - | |----------------->>| | | - | | | emit(AgentStart) | | - | | |------------------> | | - | | | | | - | | | transform_context() | | - | | |------------------> | | - | | | convert_to_llm() | | - | | |------------------> | | - | | | stream_function() | | - | | |-------------------------> | - | | | | response | - | | |<------------------------- | - | | | emit(MessageStart)| | - | | |------------------> | | - | | | emit(MessageEnd) | | - | | |------------------> | | - | | | | | - | | | extract_tool_calls()| | - | | |------------------> | | - | | | | | - | | | executeToolCalls() | | - | | |-------------------------> | - | | | | execute() | - | | |-------------------------> | - | | | | result | - | | |<------------------------- | - | | | emit(ToolExecutionEnd)| | - | | |------------------> | | - | | | createToolResult() | | - | | |------------------> | | - | | | emit(MessageEnd) | | - | | |------------------> | | - | | | | | - | | | emit(TurnEnd) | | - | | |------------------> | | - | | | | | - | | | prepare_next_turn() | | - | | |------------------> | | - | | | | | - | | | should_stop_check() | | - | | |------------------> | | - | | | | | - | | | drain(steering_queue)| | - | | |------------------> | | - | | | | | - | | | drain(follow_up_queue)| | - | | |------------------> | | - | | | | | - | | emit(AgentEnd) | | | - | |------------------>| | | - | | | | | - | | appendMessage() | | | - | |------------------> Session | | - |<----------------| | | | - | messages | | | | - | | | | | -``` - -## Key Participants - -``` -User - Initiates conversation by calling prompt() -Agent - High-level wrapper managing state, queues, and events -AgentLoop - Low-level execution engine (agentLoop, runLoop) -LLM - Language model API (via stream_function) -Tool - Tool execution (bash, read, write, edit) -Session - Conversation history persistence -``` - -## Main Loop Flow - -``` -1. [Agent] normalizePromptInput() - Convert input to messages -2. [Agent] runPromptMessages() - Start processing -3. [Agent] createLoopConfig() - Build loop configuration -4. [Agent] agentLoop() - Start low-level loop -5. [AgentLoop] emit(AgentStart) - Signal start -6. [AgentLoop] transform_context() - Optional context transform -7. [AgentLoop] convert_to_llm() - Convert to LLM messages -8. [AgentLoop] stream_function() - Call LLM -9. [AgentLoop] emit(MessageStart/End) - Stream response -10. [AgentLoop] extract_tool_calls() - Check for tool calls -11. [AgentLoop] executeToolCalls() - Execute tools (seq/parallel) -12. [AgentLoop] emit(TurnEnd) - Signal turn complete -13. [AgentLoop] prepare_next_turn() - Optional next turn setup -14. [AgentLoop] should_stop_check() - Check termination -15. [AgentLoop] drain queues() - Check steering/follow-up -16. [AgentLoop] emit(AgentEnd) - Signal completion -17. [Agent] appendMessage() - Persist to session -``` - -## Queue Processing - -``` -steering_queue: Injected after assistant turn, before next LLM call -follow_up_queue: Run only when agent would otherwise stop (after loop exit) -``` - -## Event Flow - -``` -AgentStart → [TurnStart → MessageStart → MessageEnd → ...] → TurnEnd → ... - ↓ - AgentEnd -``` - -## Tool Execution Options - -``` -Sequential: One tool at a time, in order -Parallel: All tools spawned concurrently, results collected -```