From a0787c2316bb6b764008b05632918daf0e1d6b41 Mon Sep 17 00:00:00 2001 From: narawat Date: Wed, 29 Jul 2026 13:52:02 +0700 Subject: [PATCH] update --- docs/agent_loop_diagram.md | 148 ++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/docs/agent_loop_diagram.md b/docs/agent_loop_diagram.md index 0b43789..bb69088 100644 --- a/docs/agent_loop_diagram.md +++ b/docs/agent_loop_diagram.md @@ -22,9 +22,9 @@ │ 2. AGENT LOOP START (runAgentLoop) │ │ │ │ new_messages = copy(prompts) │ -│ current_context.messages = vcat(context.messages, copy(prompts)) │ +│ current_context.messages = vcat(context.messages, copy(prompts)) │ │ │ │ -│ └─→ User messages are IMMEDIATELY added to context.messages │ +│ └─→ User messages are IMMEDIATELY added to context.messages │ │ (They are NOT in the steering queue!) │ │ │ │ emit(AgentStartEvent) │ @@ -42,7 +42,7 @@ │ │ │ pending_messages = get_steering_messages() │ │ │ │ -│ └─→ Steering queue: messages from agent.steer() │ +│ └─→ Steering queue: messages from agent.steer() │ │ These are for CONTINUING conversation (NOT new user prompts) │ │ │ │ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ @@ -51,17 +51,17 @@ │ │ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ 4. PENDING MESSAGE HANDLING (steering messages only) │ │ │ │ │ │ │ │ │ -│ │ │ pending_messages = get_steering() │ │ │ +│ │ │ pending_messages = get_steering() │ │ │ │ │ │ if !isempty(pending_messages): │ │ │ │ │ │ for msg in pending_messages: │ │ │ │ │ │ emit(MessageStartEvent(msg)) │ │ │ │ │ │ emit(MessageEndEvent(msg)) │ │ │ │ │ │ push to current_context.messages ← Steering messages go HERE │ │ │ -│ │ │ push to new_messages │ │ │ +│ │ │ push to new_messages │ │ │ │ │ │ pending_messages = [] │ │ │ │ │ │ │ │ │ -│ │ │ Note: User messages from Agent.prompt() are ALREADY in context.messages │ │ │ -│ │ │ (They were added in runAgentLoop via vcat(), not via this queue) │ │ │ +│ │ │ Note: User messages from Agent.prompt() are ALREADY in context.messages │ │ │ +│ │ │ (They were added in runAgentLoop via vcat(), not via this queue) │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ │ @@ -168,7 +168,7 @@ │ new_messages = [UserMessage("What is Julia?")] │ │ current_context.messages = vcat([...existing...], [UserMessage("What is Julia?")]) │ │ │ │ -│ └─→ User message IMMEDIATELY added to context.messages (NOT via steering queue!) │ +│ └─→ User message IMMEDIATELY added to context.messages (NOT via steering queue!) │ │ emit(AgentStartEvent), emit(TurnStartEvent) │ │ emit(MessageStart/End) for user message │ │ │ @@ -207,11 +207,11 @@ │ │ follow_up_queue: [] │ │ │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ -│ LLM SEES (convert_to_llm() filters): │ │ -│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ -│ │ Messages passed to LLM API: │ │ -│ │ [UserMessage("What is Julia?"), AssistantMessage("Julia is...")] │ │ -│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +│ LLM SEES (convert_to_llm() filters): │ +│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Messages passed to LLM API: │ │ +│ │ [UserMessage("What is Julia?"), AssistantMessage("Julia is...")] │ │ +│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ TURN #2: User asks "How does it work?" │ │ ───────────────────────────────────────── │ @@ -223,7 +223,7 @@ │ new_messages = [UserMessage("How does it work?")] │ │ current_context.messages = vcat([...previous..., UserMessage("How does it work?")]) │ │ │ │ -│ └─→ User message added (context preserved from Turn #1) │ +│ └─→ User message added (context preserved from Turn #1) │ │ emit(AgentStartEvent), emit(TurnStartEvent) │ │ emit(MessageStart/End) for user message │ │ │ @@ -245,13 +245,13 @@ │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ LLM SEES: │ -│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ -│ │ Messages passed to LLM API: │ │ -│ │ [UserMessage("What is Julia?"), │ │ -│ │ AssistantMessage("Julia is..."), │ │ -│ │ UserMessage("How does it work?"), │ │ -│ │ AssistantMessage("It works by...")] │ │ -│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Messages passed to LLM API: │ │ +│ │ [UserMessage("What is Julia?"), │ │ +│ │ AssistantMessage("Julia is..."), │ │ +│ │ UserMessage("How does it work?"), │ │ +│ │ AssistantMessage("It works by...")] │ │ +│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ @@ -261,83 +261,83 @@ │ │ │ What is a steering message? │ │ • A message (any AgentMessage type) injected via: `agent.steer(message)` │ -│ • Goes into the steering queue, not immediately to context.messages │ +│ • Goes into the steering queue, not immediately to context.messages │ │ │ │ How is it created? │ -│ • User code calls: agent.steer(UserMessage("...")) │ -│ • Or: agent.steer(AssistantMessage("...")) │ -│ • Or any other AgentMessage subtype │ +│ • User code calls: agent.steer(UserMessage("...")) │ +│ • Or: agent.steer(AssistantMessage("...")) │ +│ • Or any other AgentMessage subtype │ │ │ │ When is it processed? │ │ • At the START of the next loop iteration (line 194-202 in agent_loop.jl) │ │ • AFTER the previous assistant turn completes │ -│ • BEFORE the next assistant response is streamed │ +│ • BEFORE the next assistant response is streamed │ │ │ │ Why use steering? │ -│ Use case 1: Tool execution result injection │ -│ - Agent calls a tool (e.g., read_file, bash) │ -│ - Tool returns result │ -│ - You want to inject a follow-up question based on the result │ -│ - agent.steer(UserMessage("Based on the file, what should we do next?")) │ +│ Use case 1: Tool execution result injection │ +│ - Agent calls a tool (e.g., read_file, bash) │ +│ - Tool returns result │ +│ - You want to inject a follow-up question based on the result │ +│ - agent.steer(UserMessage("Based on the file, what should we do next?")) │ │ │ -│ Use case 2: Multi-turn conversation without user input │ -│ - Agent responds to user │ -│ - Before user types again, you want to inject a system message │ -│ - agent.steer(BashExecutionMessage(...)) or custom message │ +│ Use case 2: Multi-turn conversation without user input │ +│ - Agent responds to user │ +│ - Before user types again, you want to inject a system message │ +│ - agent.steer(BashExecutionMessage(...)) or custom message │ │ - This continues the conversation automatically │ │ │ -│ Use case 3: Branch navigation recovery │ -│ - User navigates between conversation branches │ -│ - After switching branches, you want to inject a context message │ -│ - agent.steer(BranchSummaryMessage(...)) │ -│ - The agent can then continue from the new branch context │ +│ Use case 3: Branch navigation recovery │ +│ - User navigates between conversation branches │ +│ - After switching branches, you want to inject a context message │ +│ - agent.steer(BranchSummaryMessage(...)) │ +│ - The agent can then continue from the new branch context │ │ │ -│ Use case 4: Compaction summary injection │ -│ - Conversation history is compacted │ -│ - After compaction, inject summary message │ -│ - agent.steer(CompactionSummaryMessage(...)) │ +│ Use case 4: Compaction summary injection │ +│ - Conversation history is compacted │ +│ - After compaction, inject summary message │ +│ - agent.steer(CompactionSummaryMessage(...)) │ │ - Agent knows old history was summarized │ │ │ │ Example: │ -│ agent.steer(UserMessage("Follow-up question here")) │ -│ # This will be processed in the next loop iteration, │ -│ # appearing in context.messages before the next LLM call │ +│ agent.steer(UserMessage("Follow-up question here")) │ +│ # This will be processed in the next loop iteration, │ +│ # appearing in context.messages before the next LLM call │ │ │ │ The LLM sees: │ -│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ -│ │ All messages become Message[] via convert_to_llm(): │ │ -│ │ [UserMessage(...), AssistantMessage(...), UserMessage(from_steer), ...] │ │ -│ │ │ │ -│ │ The LLM cannot tell which came from Agent.prompt() vs agent.steer() │ │ -│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ All messages become Message[] via convert_to_llm(): │ │ +│ │ [UserMessage(...), AssistantMessage(...), UserMessage(from_steer), ...] │ │ +│ │ │ │ +│ │ The LLM cannot tell which came from Agent.prompt() vs agent.steer() │ │ +│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ LLM PROCESSING: How LLM sees messages │ +│ LLM PROCESSING: How LLM sees messages │ ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ │ -│ The LLM NEVER sees "user message" vs "steering message" - it only sees Message types: │ +│ The LLM NEVER sees "user message" vs "steering message" - it only sees Message types: │ │ │ -│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ -│ │ convert_to_llm() transforms ALL AgentMessages to Message[]: │ │ -│ │ │ │ -│ │ UserMessage("user") → UserMessage (for LLM) │ │ -│ │ Steering UserMessage("user") → UserMessage (for LLM) ← Same! │ │ -│ │ AssistantMessage("assistant") → AssistantMessage (for LLM) │ │ -│ │ ToolResultMessage("toolResult") → ToolResultMessage (for LLM) │ │ -│ │ │ │ -│ │ BranchSummaryMessage → UserMessage (wrapped in summary tags) │ │ -│ │ CompactionSummaryMessage → UserMessage (wrapped in summary tags) │ │ -│ │ BashExecutionMessage → UserMessage (if not excluded) │ │ -│ │ CustomMessage → UserMessage │ │ -│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ convert_to_llm() transforms ALL AgentMessages to Message[]: │ │ +│ │ │ │ +│ │ UserMessage("user") → UserMessage (for LLM) │ │ +│ │ Steering UserMessage("user") → UserMessage (for LLM) ← Same! │ │ +│ │ AssistantMessage("assistant") → AssistantMessage (for LLM) │ │ +│ │ ToolResultMessage("toolResult") → ToolResultMessage (for LLM) │ │ +│ │ │ │ +│ │ BranchSummaryMessage → UserMessage (wrapped in summary tags) │ │ +│ │ CompactionSummaryMessage → UserMessage (wrapped in summary tags) │ │ +│ │ BashExecutionMessage → UserMessage (if not excluded) │ │ +│ │ CustomMessage → UserMessage │ │ +│ └─────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ │ -│ The difference is ONLY in HOW messages enter the system: │ -│ • User messages: Agent.prompt() → vcat() → context.messages (direct) │ -│ • Steering: agent.steer() → queue → loop → context.messages (indirect) │ +│ The difference is ONLY in HOW messages enter the system: │ +│ • User messages: Agent.prompt() → vcat() → context.messages (direct) │ +│ • Steering: agent.steer() → queue → loop → context.messages (indirect) │ │ │ -│ At LLM level: BOTH become UserMessage in the conversation! │ +│ At LLM level: BOTH become UserMessage in the conversation! │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ @@ -347,13 +347,13 @@ │ │ │ 1. User prompts go DIRECTLY to context.messages via vcat() in runAgentLoop() │ │ │ -│ 2. Steering queue is for messages injected via agent.steer() AFTER a turn finishes │ +│ 2. Steering queue is for messages injected via agent.steer() AFTER a turn finishes │ │ This allows continuing conversation without calling Agent.prompt() again │ │ │ │ 3. Context is preserved across turns - context.messages grows with each turn │ │ LLM sees the full conversation history │ │ │ -│ 4. At LLM level, ALL messages become Message types (UserMessage/AssistantMessage/ToolResultMessage) │ +│ 4. At LLM level, ALL messages become Message types (UserMessage/AssistantMessage/ToolResultMessage) │ │ The "steering" vs "user" distinction is just a control mechanism, not a message type │ │ │ │ 5. New turn is triggered by: │