222 lines
12 KiB
Markdown
222 lines
12 KiB
Markdown
# 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 ─┘
|
|
```
|