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