This commit is contained in:
2026-07-28 10:05:52 +07:00
parent 072d0e16af
commit d5af7c85aa
+116 -207
View File
@@ -1,221 +1,130 @@
# Agent Loop Sequence Diagram (ASCII)
# Agent Loop Sequence Diagram
```
┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ ┌───────────┐
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)
+---------+ +---------+ +------------+ +-------+ +-------+
| 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 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
## Key Participants
```
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
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
```
## EXECUTION MODES
## Main Loop Flow
### Sequential (EXECUTION_SEQUENTIAL)
```
ToolCall1 → ToolCall2 → ToolCall3
│ │ │
▼ ▼ ▼
Result1 Result2 Result3
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
```
### Parallel (EXECUTION_PARALLEL)
## Queue Processing
```
ToolCall1 ─┐
ToolCall2──┼→ Execute all → Wait for all → Results
ToolCall3 ─┘
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
```