update
This commit is contained in:
@@ -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<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
|
||||||
|
|
||||||
|
```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<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
|
||||||
|
```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 |
|
||||||
@@ -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
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user