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 | | Agent | | AgentLoop | | LLM | | Tool |
└─────────────┘ └─────────────┘ └─────────────────┘ └───────────┘ +---------+ +---------+ +------------+ +-------+ +-------+
| | | | |
| prompt(messages)| | | |
┌─────────────┐ ┌─────────────────┐ ┌───────────┐ ┌─────────────┐ |---------------->--> | | |
│ User │<─────│ AgentLoop │─────>│ Agent │─────>│ Session │ | | normalizePrompt()| | |
└─────────────┘ └─────────────────┘ └───────────┘ └─────────────┘ | |----------------->>| | |
| | runPromptMessages| | |
| |----------------->>| | |
┌─────────────┐ | | | | |
│ Session │ | | createLoopConfig | | |
└─────────────┘ | |----------------->>| | |
| | | | |
┌─────────────────────────────────────────────────────────────────────────────────────────┐ | | agentLoop() | | |
│ AGENT LOOP MAIN FLOW │ | |----------------->>| | |
└─────────────────────────────────────────────────────────────────────────────────────────┘ | | | emit(AgentStart) | |
| | |------------------> | |
User | | | | |
| | | transform_context() | |
│ prompt(messages) | | |------------------> | |
| | | convert_to_llm() | |
Agent | | |------------------> | |
| | | stream_function() | |
│ normalizePromptInput() | | |-------------------------> |
│ runPromptMessages() | | | | response |
| | |<------------------------- |
├─ alt steering_queue has messages | | | emit(MessageStart)| |
│ │ drain(steering_queue) | | |------------------> | |
│ │ runPromptMessages() | | | emit(MessageEnd) | |
| | |------------------> | |
├─ alt follow_up_queue has messages | | | | |
│ │ drain(follow_up_queue) | | | extract_tool_calls()| |
│ │ runPromptMessages() | | |------------------> | |
| | | | |
└─ else (normal prompt) | | | executeToolCalls() | |
│ createLoopConfig() | | |-------------------------> |
│ createActiveRun() | | | | execute() |
| | |-------------------------> |
| | | | result |
AgentLoop | | |<------------------------- |
| | | emit(ToolExecutionEnd)| |
│ agentLoop(messages, context, config) | | |------------------> | |
| | | createToolResult() | |
| | |------------------> | |
┌─────────────────────────────────────────────────────────────┐ | | | emit(MessageEnd) | |
│ MAIN LOOP │ | | |------------------> | |
├─────────────────────────────────────────────────────────────┤ | | | | |
│ │ | | | emit(TurnEnd) | |
│ 1. Handle queued messages │ | | |------------------> | |
│ ├─ drain(steering_queue) │ | | | | |
│ ├─ emit(MessageStartEvent) │ | | | prepare_next_turn() | |
│ ├─ push to context.messages │ | | |------------------> | |
│ └─ emit(MessageEndEvent) │ | | | | |
│ │ | | | should_stop_check() | |
│ 2. Get LLM Response │ | | |------------------> | |
│ ├─ transform_context?(messages) │ | | | | |
│ ├─ convert_to_llm(messages) │ | | | drain(steering_queue)| |
│ └─ stream_function(model, context, config) │ | | |------------------> | |
│ │ | | | | |
│ LLM │ | | | drain(follow_up_queue)| |
│ │ response stream │ | | |------------------> | |
│ └─ emit(MessageStartEvent) │ | | | | |
└─ emit(MessageUpdateEvent) (streaming) | | emit(AgentEnd) | | |
│ └─ emit(MessageEndEvent(final_message)) │ | |------------------>| | |
│ │ | | | | |
│ 3. Check for Tool Calls │ | | appendMessage() | | |
│ └─ extract_tool_calls(message.content) │ | |------------------> Session | |
│ │ |<----------------| | | |
│ 4. Execute Tools (if any) │ | messages | | | |
│ ├─ 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 ## Key Participants
### 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 - Initiates conversation by calling prompt()
User messages → Agent.normalizePromptInput() → AgentLoop Agent - High-level wrapper managing state, queues, and events
AgentLoop - Low-level execution engine (agentLoop, runLoop)
LLM Call: LLM - Language model API (via stream_function)
AgentMessage[] → transform_context() → convert_to_llm() → Tool - Tool execution (bash, read, write, edit)
LLM.stream() → AssistantMessage Session - Conversation history persistence
Tool Execution:
ToolCall[] → prepareToolCall() →
before_tool_call? → execute() → after_tool_call? →
ToolResultMessage[]
Output:
AgentEndEvent(messages) → Session.appendMessage() → User promise
``` ```
## EXECUTION MODES ## Main Loop Flow
### Sequential (EXECUTION_SEQUENTIAL)
``` ```
ToolCall1 → ToolCall2 → ToolCall3 1. [Agent] normalizePromptInput() - Convert input to messages
│ │ │ 2. [Agent] runPromptMessages() - Start processing
▼ ▼ ▼ 3. [Agent] createLoopConfig() - Build loop configuration
Result1 Result2 Result3 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 ─┐ steering_queue: Injected after assistant turn, before next LLM call
ToolCall2──┼→ Execute all → Wait for all → Results follow_up_queue: Run only when agent would otherwise stop (after loop exit)
ToolCall3 ─┘ ```
## 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
``` ```