update
This commit is contained in:
@@ -405,87 +405,295 @@
|
|||||||
└── Manages ──► PromptTemplates
|
└── Manages ──► PromptTemplates
|
||||||
```
|
```
|
||||||
|
|
||||||
## Data Flow
|
## Data Flow with Type Transformations
|
||||||
|
|
||||||
|
### Complete User Input → Conversation History Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ Data Flow Between Layers │
|
│ Level 1: User Input │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
User Input
|
||||||
|
• String: "Hello, what's in the directory?"
|
||||||
|
• AgentMessage: UserMessage(...)
|
||||||
|
• Vector{AgentMessage}: [UserMessage(...), AssistantMessage(...)]
|
||||||
|
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ Agent.prompt() / normalizePromptInput() │
|
||||||
|
│ │
|
||||||
|
│ Type Dispatch: │
|
||||||
|
│ • String → UserMessage("user", [TextContent(input)], ts) │
|
||||||
|
│ • AgentMessage → [input] (wrap in array) │
|
||||||
|
│ • Vector{AgentMessage} → input (pass-through) │
|
||||||
|
│ │
|
||||||
|
│ Output: Vector{AgentMessage} │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ AgentState.messages (AgentMessage[]) │
|
||||||
|
│ │
|
||||||
|
│ AgentMessage Types: │
|
||||||
|
│ • UserMessage (role: "user") │
|
||||||
|
│ • AssistantMessage (role: "assistant") │
|
||||||
|
│ • ToolResultMessage (role: "toolResult") │
|
||||||
|
│ • BashExecutionMessage (custom) │
|
||||||
|
│ • CompactionSummaryMessage (custom) │
|
||||||
|
│ • BranchSummaryMessage (custom) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Level 2: AgentLoop Processing │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
User Input (String/Message)
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ transform_context() (optional hook) │
|
||||||
|
│ │
|
||||||
|
│ Input: Vector{AgentMessage} │
|
||||||
|
│ Output: Vector{AgentMessage} (transformed) │
|
||||||
|
│ - Can truncate, filter, or modify messages │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
│ Agent.prompt() │
|
│ convertToLlm() - Type Transformation Pipeline │
|
||||||
│ - normalizeInput() │
|
│ │
|
||||||
└──────────────────────┘
|
│ Input: Vector{AgentMessage} │
|
||||||
|
│ Output: Vector{Message} (for LLM API) │
|
||||||
|
│ │
|
||||||
|
│ Single Dispatch Mapping: │
|
||||||
|
│ • UserMessage → UserMessage (pass-through) │
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through) │
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through) │
|
||||||
|
│ │
|
||||||
|
│ Custom Message Conversions: │
|
||||||
|
│ • BashExecutionMessage → UserMessage (via bashExecutionToText)│
|
||||||
|
│ • CompactionSummaryMessage → UserMessage (wrapped) │
|
||||||
|
│ • BranchSummaryMessage → UserMessage (wrapped) │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ AgentState.messages │ ──► AgentMessage[]
|
│ Context for LLM API │
|
||||||
└──────────────────────┘
|
│ - system_prompt: String │
|
||||||
|
│ - messages: Vector{Message} │
|
||||||
|
│ - tools: Vector{AgentTool} │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ AgentLoop │
|
│ LLM API Call (stream_fn) │
|
||||||
│ - transform_context │
|
│ │
|
||||||
└──────────────────────┘
|
│ Input: model, context, config │
|
||||||
|
│ Output: Stream{AssistantMessageEvent} │
|
||||||
|
│ • StartEvent: partial AssistantMessage │
|
||||||
|
│ • TextStartEvent/TextDeltaEvent/TextEndEvent │
|
||||||
|
│ • ToolCallStartEvent/ToolCallDeltaEvent/ToolCallEndEvent │
|
||||||
|
│ • DoneEvent: final AssistantMessage with usage │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ convertToLlm() │ ──► Transforms AgentMessage[] to Message[]
|
│ AssistantMessage (returned from LLM) │
|
||||||
└──────────────────────┘
|
│ │
|
||||||
|
│ • role: "assistant" │
|
||||||
|
│ • content: Vector{MessageContent} │
|
||||||
|
│ └─ Contains: TextContent[] and/or ToolCall[] │
|
||||||
|
│ • api, provider, model: String │
|
||||||
|
│ • usage: Usage (input, output, cache_read, cache_write) │
|
||||||
|
│ • stop_reason: String ("done", "length", "error", etc.) │
|
||||||
|
│ • error_message: Union{String, Nothing} │
|
||||||
|
│ • timestamp: Timestamp (Int64) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├─► Append to AgentState.messages (AssistantMessage)
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ LLM API (StreamFn) │
|
│ executeToolCalls() - Tool Processing │
|
||||||
│ - Context: Message[] │
|
│ │
|
||||||
└──────────────────────┘
|
│ Extract: filter(c -> c isa ToolCall, assistant.content) │
|
||||||
|
│ Output: ExecutedToolCallBatch │
|
||||||
|
│ • messages: Vector{ToolResultMessage} │
|
||||||
|
│ • terminate: Bool │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ Response (Streaming) │
|
│ ToolResultMessage (for each ToolCall) │
|
||||||
│ - Text deltas │
|
│ │
|
||||||
│ - Tool call deltas │
|
│ • role: "toolResult" │
|
||||||
└──────────────────────┘
|
│ • tool_call_id: String (matches ToolCall.id) │
|
||||||
|
│ • tool_name: String (matches ToolCall.name) │
|
||||||
|
│ • content: Vector{MessageContent} │
|
||||||
|
│ • details: Any (tool-specific) │
|
||||||
|
│ • usage: Union{Usage, Nothing} │
|
||||||
|
│ • added_tool_names: Union{Vector{String}, Nothing} │
|
||||||
|
│ • is_error: Bool │
|
||||||
|
│ • timestamp: Timestamp (Int64) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
├─► Append to AgentState.messages (ToolResultMessage)
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ AssistantMessage │
|
│ Updated AgentState.messages (AgentMessage[]) │
|
||||||
│ - content: Message[] │
|
│ │
|
||||||
└──────────────────────┘
|
│ Conversation History: │
|
||||||
|
│ [UserMessage, AssistantMessage, ToolResultMessage, ...] │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Level 3: Session Storage (optional, for persistence) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
AgentState.messages (Vector{AgentMessage})
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ AgentState.messages │ ──► Appended to conversation
|
│ Session Storage (JSONL) │
|
||||||
└──────────────────────┘
|
│ │
|
||||||
|
│ SessionTreeEntry Types: │
|
||||||
|
│ • MessageEntry (agent_message) │
|
||||||
|
│ • CompactionEntry (summary, tokens_before) │
|
||||||
|
│ • BranchSummaryEntry (from_id, summary) │
|
||||||
|
│ • ModelChangeEntry (provider, model_id) │
|
||||||
|
│ • ThinkingLevelChangeEntry (thinking_level) │
|
||||||
|
│ • ActiveToolsChangeEntry (active_tool_names) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ Tool Execution │
|
│ Persisted Data (JSON format) │
|
||||||
│ - Extract ToolCalls │
|
│ - Each entry has: id, parent_id, timestamp, type │
|
||||||
│ - Execute tools │
|
│ - MessageEntry contains full AgentMessage │
|
||||||
└──────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────────────┐
|
|
||||||
│ ToolResultMessage[] │
|
|
||||||
└──────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────────────┐
|
|
||||||
│ AgentState.messages │ ──► Tool results appended
|
|
||||||
└──────────────────────┘
|
|
||||||
│
|
|
||||||
│ (Loop back to LLM or end)
|
|
||||||
▼
|
|
||||||
┌──────────────────────┐
|
|
||||||
│ Session Storage │
|
|
||||||
│ - JSONL format │
|
|
||||||
│ - Tree entries │
|
|
||||||
└──────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Tool Call Execution Flow (Detailed)
|
||||||
|
|
||||||
|
```
|
||||||
|
ToolCall (from AssistantMessage.content)
|
||||||
|
│
|
||||||
|
├─ type: "tool"
|
||||||
|
├─ id: "tc_abc123"
|
||||||
|
├─ name: "bash"
|
||||||
|
├─ arguments: Dict("command" => "ls -la")
|
||||||
|
└─ partial_json: nothing
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ prepareToolCall() │
|
||||||
|
│ │
|
||||||
|
│ Input: tool_call::ToolCall │
|
||||||
|
│ Output: Union{PreparedToolCall, ImmediateToolCallOutcome} │
|
||||||
|
│ │
|
||||||
|
│ Steps: │
|
||||||
|
│ 1. Find tool by name in current_context.tools │
|
||||||
|
│ 2. before_tool_call hook (optional) │
|
||||||
|
│ Input: BeforeToolCallContext │
|
||||||
|
│ Output: BeforeToolCallResult (block, reason) or nothing │
|
||||||
|
│ 3. prepareToolCallArguments() (optional) │
|
||||||
|
│ Input: tool_call.arguments::Dict │
|
||||||
|
│ Output: prepared_arguments::Any │
|
||||||
|
│ 4. validateToolArguments() (optional) │
|
||||||
|
│ Input: prepared_tool_call.arguments │
|
||||||
|
│ Output: validated_args::Any │
|
||||||
|
│ 5. Return: PreparedToolCall(kind, tool_call, tool, args) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ executePreparedToolCall() (if prepared) │
|
||||||
|
│ │
|
||||||
|
│ Input: PreparedToolCall │
|
||||||
|
│ Output: ExecutedToolCallOutcome │
|
||||||
|
│ │
|
||||||
|
│ tool.execute(tool_call.id, args, signal, on_update) │
|
||||||
|
│ │ │
|
||||||
|
│ └─ Returns: AgentToolResultMutable │
|
||||||
|
│ • content::Vector{MessageContent} │
|
||||||
|
│ • details::Any │
|
||||||
|
│ • usage::Union{Usage, Nothing} │
|
||||||
|
│ • added_tool_names::Union{Vector{String}, Nothing} │
|
||||||
|
│ • terminate::Union{Bool, Nothing} │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌───────────────────────────────────────────────────────────────┐
|
||||||
|
│ finalizeExecutedToolCall() │
|
||||||
|
│ │
|
||||||
|
│ Input: ExecutedToolCallOutcome │
|
||||||
|
│ Output: FinalizedToolCallOutcome │
|
||||||
|
│ │
|
||||||
|
│ Steps: │
|
||||||
|
│ 1. after_tool_call hook (optional) │
|
||||||
|
│ Input: AfterToolCallContext │
|
||||||
|
│ Output: AfterToolCallResult (patches) │
|
||||||
|
│ 2. Apply patches to result │
|
||||||
|
│ 3. Return: FinalizedToolCallOutcome(tool_call, result, error)│
|
||||||
|
└───────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ createToolResultMessage() │
|
||||||
|
│ │
|
||||||
|
│ Input: FinalizedToolCallOutcome │
|
||||||
|
│ Output: ToolResultMessage │
|
||||||
|
│ │
|
||||||
|
│ Fields: │
|
||||||
|
│ • role: "toolResult" │
|
||||||
|
│ • tool_call_id: tool_call.id │
|
||||||
|
│ • tool_name: tool_call.name │
|
||||||
|
│ • content: result.content │
|
||||||
|
│ • details: result.details │
|
||||||
|
│ • usage: result.usage │
|
||||||
|
│ • added_tool_names: result.added_tool_names │
|
||||||
|
│ • is_error: is_error │
|
||||||
|
│ • timestamp: Int64(Dates.now(Dates.UTC).datetime) │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ Emit: ToolResultMessage to conversation │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Summary of Type Transformations │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
User Input (String)
|
||||||
|
│
|
||||||
|
├─► normalizePromptInput()
|
||||||
|
│ └─► UserMessage (AgentMessage subtype)
|
||||||
|
│
|
||||||
|
Vector{AgentMessage}
|
||||||
|
│
|
||||||
|
├─► transform_context() (optional)
|
||||||
|
│ └─► Vector{AgentMessage} (transformed)
|
||||||
|
│
|
||||||
|
├─► convertToLlm()
|
||||||
|
│ └─► Vector{Message} (LLM API format)
|
||||||
|
│ ├── UserMessage (pass-through)
|
||||||
|
│ ├── AssistantMessage (pass-through)
|
||||||
|
│ ├── ToolResultMessage (pass-through)
|
||||||
|
│ └── Custom messages → UserMessage
|
||||||
|
│
|
||||||
|
AssistantMessage (from LLM)
|
||||||
|
│
|
||||||
|
├─► executeToolCalls()
|
||||||
|
│ └─► ToolResultMessage[]
|
||||||
|
│
|
||||||
|
ToolResultMessage[]
|
||||||
|
│
|
||||||
|
└─► Appended to AgentState.messages
|
||||||
|
└─► Vector{AgentMessage} (updated conversation history)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
The AgentCore.jl architecture follows a clean separation of concerns:
|
The AgentCore.jl architecture follows a clean separation of concerns:
|
||||||
@@ -495,4 +703,48 @@ The AgentCore.jl architecture follows a clean separation of concerns:
|
|||||||
3. **AgentLoop** - Core LLM interaction loop
|
3. **AgentLoop** - Core LLM interaction loop
|
||||||
4. **Session** - Conversation history management
|
4. **Session** - Conversation history management
|
||||||
|
|
||||||
|
### Data Transformation Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Input Type Flow:
|
||||||
|
User Input (String/Message)
|
||||||
|
│
|
||||||
|
├─ normalizePromptInput()
|
||||||
|
│ └─► Vector{AgentMessage}
|
||||||
|
│
|
||||||
|
├─ transform_context() (optional)
|
||||||
|
│ └─► Vector{AgentMessage} (transformed)
|
||||||
|
│
|
||||||
|
├─ convertToLlm()
|
||||||
|
│ └─► Vector{Message} (LLM API format)
|
||||||
|
│
|
||||||
|
├─ LLM API (stream_fn)
|
||||||
|
│ └─► AssistantMessage
|
||||||
|
│
|
||||||
|
├─ executeToolCalls()
|
||||||
|
│ └─► ToolResultMessage[]
|
||||||
|
│
|
||||||
|
└─► Vector{AgentMessage} (final conversation)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Data Flow Patterns
|
||||||
|
|
||||||
|
1. **Message Transformation**: `AgentMessage[] → Message[]` via `convertToLlm()`
|
||||||
|
- UserMessage → UserMessage (pass-through)
|
||||||
|
- AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
- ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
- Custom messages (Bash, Compaction, Branch) → UserMessage
|
||||||
|
|
||||||
|
2. **Tool Execution**: `ToolCall → ToolResultMessage`
|
||||||
|
- prepareToolCall() validates and prepares
|
||||||
|
- execute() runs the tool
|
||||||
|
- finalize() applies hooks and returns outcome
|
||||||
|
- createToolResultMessage() creates result entry
|
||||||
|
|
||||||
|
3. **Event Streaming**: `Stream{Event}` with lifecycle events
|
||||||
|
- AgentStartEvent, TurnStartEvent
|
||||||
|
- MessageStartEvent, MessageUpdateEvent, MessageEndEvent
|
||||||
|
- ToolExecutionStartEvent, ToolExecutionEndEvent
|
||||||
|
- TurnEndEvent, AgentEndEvent
|
||||||
|
|
||||||
Each layer transforms data and passes it to the next layer, with clear interfaces and event hooks for customization.
|
Each layer transforms data and passes it to the next layer, with clear interfaces and event hooks for customization.
|
||||||
|
|||||||
@@ -243,16 +243,35 @@ followUp(agent, UserMessage(...))
|
|||||||
|
|
||||||
```julia
|
```julia
|
||||||
# Transform messages before sending to LLM
|
# Transform messages before sending to LLM
|
||||||
function myConvertToLlm(messages::Vector{AgentMessage})
|
function myConvertToLlm(messages::Vector{AgentMessage})::Vector{Message}
|
||||||
return filter(
|
result::Vector{Message} = Message[]
|
||||||
m -> m.role in ["user", "assistant", "toolResult"],
|
for m in messages
|
||||||
messages
|
converted = convertToLlmMessage(m)
|
||||||
)
|
if !isnothing(converted)
|
||||||
|
push!(result, converted)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
agent = Agent(Dict(:convertToLlm => myConvertToLlm))
|
agent = Agent(Dict(:convertToLlm => myConvertToLlm))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Data Flow**:
|
||||||
|
```
|
||||||
|
Vector{AgentMessage}
|
||||||
|
│
|
||||||
|
│ convertToLlmMessage() dispatches on type:
|
||||||
|
│ • UserMessage → UserMessage (pass-through)
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
│ • BashExecutionMessage → UserMessage (bashExecutionToText)
|
||||||
|
│ • CompactionSummaryMessage → UserMessage (wrapped)
|
||||||
|
│ • BranchSummaryMessage → UserMessage (wrapped)
|
||||||
|
▼
|
||||||
|
Vector{Message} (for LLM API)
|
||||||
|
```
|
||||||
|
|
||||||
#### transform_context
|
#### transform_context
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
|
|||||||
@@ -27,11 +27,16 @@ agentLoopContinue()
|
|||||||
└─ Returns: EventStream
|
└─ Returns: EventStream
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ Internal Flow │
|
│ Data Flow with Type Transformations │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ 1. runAgentLoop() ── Entry point for new conversation │
|
│ 1. runAgentLoop() ── Entry point for new conversation │
|
||||||
|
│ Input: prompts::Vector{AgentMessage} │
|
||||||
|
│ context::AgentContext (system_prompt, messages, tools) │
|
||||||
|
│ config::AgentLoopConfig │
|
||||||
|
│ Output: new_messages::Vector{AgentMessage} (appended prompts + turns) │
|
||||||
|
│ │
|
||||||
│ - Creates copy of prompts │
|
│ - Creates copy of prompts │
|
||||||
│ - Appends prompts to context.messages │
|
│ - Appends prompts to context.messages │
|
||||||
│ - Emits AgentStartEvent │
|
│ - Emits AgentStartEvent │
|
||||||
@@ -43,12 +48,20 @@ agentLoopContinue()
|
|||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ 2. runLoop() ── Main event loop │
|
│ 2. runLoop() ── Main event loop │
|
||||||
|
│ Input: current_context::AgentContext │
|
||||||
|
│ new_messages::Vector{AgentMessage} │
|
||||||
|
│ Output: N/A (writes to new_messages and context.messages) │
|
||||||
|
│ │
|
||||||
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
||||||
│ │ while true: │ │
|
│ │ while true: │ │
|
||||||
│ │ 1. Get steering/follow-up messages (if any) │ │
|
│ │ 1. Get steering/follow-up messages (if any) │ │
|
||||||
│ │ 2. Emit messages as UserMessage │ │
|
│ │ 2. Emit messages as UserMessage │ │
|
||||||
│ │ 3. streamAssistantResponse() │ │
|
│ │ 3. streamAssistantResponse() │ │
|
||||||
|
│ │ - Input: context.messages::Vector{AgentMessage} │ │
|
||||||
|
│ │ - Output: message::AssistantMessage │ │
|
||||||
│ │ 4. Execute tool calls (sequential or parallel) │ │
|
│ │ 4. Execute tool calls (sequential or parallel) │ │
|
||||||
|
│ │ - Input: AssistantMessage with ToolCall[] │ │
|
||||||
|
│ │ - Output: tool_results::Vector{ToolResultMessage} │ │
|
||||||
│ │ 5. Emit TurnEndEvent │ │
|
│ │ 5. Emit TurnEndEvent │ │
|
||||||
│ │ 6. prepare_next_turn (optional) │ │
|
│ │ 6. prepare_next_turn (optional) │ │
|
||||||
│ │ 7. should_stop_after_turn? (check termination) │ │
|
│ │ 7. should_stop_after_turn? (check termination) │ │
|
||||||
@@ -59,27 +72,89 @@ agentLoopContinue()
|
|||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ 3. streamAssistantResponse() ── LLM interaction │
|
│ 3. streamAssistantResponse() ── LLM interaction │
|
||||||
│ - transform_context (optional) │
|
│ Input: context::AgentContext │
|
||||||
│ - convert_to_llm (transform to Message[]) │
|
│ config::AgentLoopConfig │
|
||||||
│ - Call stream_fn (LLM API) │
|
│ Output: message::AssistantMessage │
|
||||||
│ - Stream response deltas │
|
│ │
|
||||||
│ - Emit MessageStart/Update/End events │
|
│ Data Transformations: │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Step 1: transform_context (optional) │ │
|
||||||
|
│ │ Input: context.messages::Vector{AgentMessage} │ │
|
||||||
|
│ │ Output: messages::Vector{AgentMessage} (transformed) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Step 2: convert_to_llm │ │
|
||||||
|
│ │ Input: messages::Vector{AgentMessage} │ │
|
||||||
|
│ │ Output: llm_messages::Vector{Message} │ │
|
||||||
|
│ │ - UserMessage → UserMessage │ │
|
||||||
|
│ │ - AssistantMessage → AssistantMessage │ │
|
||||||
|
│ │ - ToolResultMessage → ToolResultMessage │ │
|
||||||
|
│ │ - BashExecutionMessage → UserMessage │ │
|
||||||
|
│ │ - CompactionSummaryMessage → UserMessage │ │
|
||||||
|
│ │ - BranchSummaryMessage → UserMessage │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Step 3: Call stream_fn │ │
|
||||||
|
│ │ Input: model, llm_context::Context, config │ │
|
||||||
|
│ │ Output: response::Stream (events) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Step 4: Stream events │ │
|
||||||
|
│ │ Events: start, text_start/delta/end, toolcall_start/delta/end │ │
|
||||||
|
│ │ Final: AssistantMessage (with ToolCall[] in content) │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────────────┘ │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ 4. executeToolCalls() ── Tool execution │
|
│ 4. executeToolCalls() ── Tool execution │
|
||||||
|
│ Input: assistant_message::AssistantMessage (contains ToolCall[]) │
|
||||||
|
│ current_context::AgentContext │
|
||||||
|
│ Output: ExecutedToolCallBatch (messages::ToolResultMessage[], terminate) │
|
||||||
|
│ │
|
||||||
|
│ For each ToolCall: │
|
||||||
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
||||||
│ │ if EXECUTION_SEQUENTIAL || has_sequential_tool: │ │
|
│ │ prepareToolCall() │ │
|
||||||
│ │ executeToolCallsSequential() │ │
|
│ │ Input: tool_call::ToolCall │ │
|
||||||
│ │ else: │ │
|
│ │ Output: PreparedToolCall or ImmediateToolCallOutcome │ │
|
||||||
│ │ executeToolCallsParallel() │ │
|
│ │ - validates arguments │ │
|
||||||
|
│ │ - runs before_tool_call hook (optional) │ │
|
||||||
|
│ │ - runs prepare_arguments hook (optional) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ executePreparedToolCall() (if prepared) │ │
|
||||||
|
│ │ Input: PreparedToolCall │ │
|
||||||
|
│ │ Output: ExecutedToolCallOutcome │ │
|
||||||
|
│ │ - calls tool.execute() │ │
|
||||||
|
│ │ - returns AgentToolResultMutable │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ finalizeExecutedToolCall() │ │
|
||||||
|
│ │ Input: ExecutedToolCallOutcome │ │
|
||||||
|
│ │ Output: FinalizedToolCallOutcome │ │
|
||||||
|
│ │ - runs after_tool_call hook (optional) │ │
|
||||||
|
│ │ - returns ToolCall + AgentToolResultMutable + is_error │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ createToolResultMessage() │ │
|
||||||
|
│ │ Input: FinalizedToolCallOutcome │ │
|
||||||
|
│ │ Output: ToolResultMessage │ │
|
||||||
|
│ │ - role: "toolResult" │ │
|
||||||
|
│ │ - tool_call_id, tool_name, content, details │ │
|
||||||
|
│ │ - usage, added_tool_names, is_error, timestamp │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Sequential: executeToolCallsSequential() │ │
|
||||||
|
│ │ - Executes tools one at a time, waits for each │ │
|
||||||
|
│ │ - Returns batch of ToolResultMessage[] │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Parallel: executeToolCallsParallel() │ │
|
||||||
|
│ │ - Creates closures for async execution │ │
|
||||||
|
│ │ - Executes all closures, collects results │ │
|
||||||
|
│ │ - Returns batch of ToolResultMessage[] │ │
|
||||||
│ └────────────────────────────────────────────────────────────────────┘ │
|
│ └────────────────────────────────────────────────────────────────────┘ │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ 5. AgentEndEvent ── Final event with all messages │
|
│ 5. AgentEndEvent ── Final event with all messages │
|
||||||
|
│ Output: messages::Vector{AgentMessage} │
|
||||||
|
│ Contains: [UserMessage, AssistantMessage, ToolResultMessage, ...] │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -301,17 +376,44 @@ function streamAssistantResponse(
|
|||||||
)::AssistantMessage
|
)::AssistantMessage
|
||||||
```
|
```
|
||||||
|
|
||||||
**Flow**:
|
**Data Flow**:
|
||||||
1. Get messages from context
|
|
||||||
2. Apply transform_context (optional)
|
```
|
||||||
3. Convert to LLM messages with convert_to_llm
|
Input: context.messages::Vector{AgentMessage}
|
||||||
4. Create Context object
|
↓
|
||||||
5. Resolve API key
|
[transform_context] (optional hook)
|
||||||
6. Call stream_fn with model, context, and config
|
↓
|
||||||
7. Stream events:
|
messages::Vector{AgentMessage}
|
||||||
- "start" → MessageStartEvent
|
↓
|
||||||
- "text_start", "text_delta", "text_end" → MessageUpdateEvent
|
[convert_to_llm] - Type transformation pipeline
|
||||||
- "done", "error" → MessageEndEvent
|
↓
|
||||||
|
llm_messages::Vector{Message}
|
||||||
|
│
|
||||||
|
│ AgentMessage → Message mapping:
|
||||||
|
│ • UserMessage → UserMessage (pass-through)
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
│ • BashExecutionMessage → UserMessage (text conversion)
|
||||||
|
│ • CompactionSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│ • BranchSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Context(system_prompt, llm_messages, tools)
|
||||||
|
↓
|
||||||
|
stream_fn(model, context, config) - LLM API call
|
||||||
|
↓
|
||||||
|
Stream of AssistantMessageEvent:
|
||||||
|
• StartEvent (partial AssistantMessage)
|
||||||
|
• TextStartEvent/TextDeltaEvent/TextEndEvent
|
||||||
|
• ToolCallStartEvent/ToolCallDeltaEvent/ToolCallEndEvent
|
||||||
|
• DoneEvent (final AssistantMessage with usage, stop_reason)
|
||||||
|
↓
|
||||||
|
Return: AssistantMessage
|
||||||
|
- content::Vector{MessageContent}
|
||||||
|
- usage::Usage
|
||||||
|
- stop_reason::String
|
||||||
|
- Contains ToolCall[] if tool calls requested
|
||||||
|
```
|
||||||
|
|
||||||
### executeToolCalls()
|
### executeToolCalls()
|
||||||
|
|
||||||
@@ -320,27 +422,56 @@ function executeToolCalls(
|
|||||||
current_context::AgentContext,
|
current_context::AgentContext,
|
||||||
assistant_message::AssistantMessage,
|
assistant_message::AssistantMessage,
|
||||||
config::AgentLoopConfig,
|
config::AgentLoopConfig,
|
||||||
signal::Union{Nothing, AbortSignal},
|
signal::Union{Nothing, AbortSignal>,
|
||||||
emit::AgentEventSink,
|
emit::AgentEventSink,
|
||||||
)::ExecutedToolCallBatch
|
)::ExecutedToolCallBatch
|
||||||
```
|
```
|
||||||
|
|
||||||
**Logic**:
|
**Data Flow**:
|
||||||
```julia
|
|
||||||
tool_calls = filter(c -> c isa ToolCall, assistant_message.content)
|
|
||||||
|
|
||||||
# Check if any tool requires sequential execution
|
```
|
||||||
has_sequential = any(tc -> begin
|
Input: assistant_message::AssistantMessage
|
||||||
tool = findfirst(t -> t.name == tc.name, current_context.tools)
|
- content::Vector{MessageContent}
|
||||||
!isnothing(tool) && tool.execution_mode == EXECUTION_SEQUENTIAL
|
└─ Contains ToolCall[] and/or TextContent[]
|
||||||
end, tool_calls)
|
↓
|
||||||
|
filter(c -> c isa ToolCall, assistant_message.content)
|
||||||
# Determine execution mode
|
↓
|
||||||
if config.tool_execution == EXECUTION_SEQUENTIAL || has_sequential
|
tool_calls::Vector{ToolCall}
|
||||||
executeToolCallsSequential(...)
|
- type: "tool"
|
||||||
else
|
- id::String
|
||||||
executeToolCallsParallel(...)
|
- name::String
|
||||||
end
|
- arguments::Dict{String, Any}
|
||||||
|
- partial_json::Union{String, Nothing}
|
||||||
|
↓
|
||||||
|
Check execution mode:
|
||||||
|
• config.tool_execution (sequential/parallel)
|
||||||
|
• Any tool.execution_mode == EXECUTION_SEQUENTIAL?
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Sequential Mode (or has_sequential_tool) │
|
||||||
|
│ For each tool_call in tool_calls: │
|
||||||
|
│ prepareToolCall() → PreparedToolCall │
|
||||||
|
│ executePreparedToolCall() → ExecutedToolCallOutcome │
|
||||||
|
│ finalizeExecutedToolCall() → FinalizedToolCallOutcome │
|
||||||
|
│ createToolResultMessage() → ToolResultMessage │
|
||||||
|
│ (wait for completion before next tool) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Parallel Mode │
|
||||||
|
│ For each tool_call in tool_calls: │
|
||||||
|
│ prepareToolCall() → (PreparedToolCall | ImmediateOutcome) │
|
||||||
|
│ If prepared: create closure │
|
||||||
|
│ If immediate: execute and add to finalized_calls │
|
||||||
|
│ │
|
||||||
|
│ For each entry in finalized_calls: │
|
||||||
|
│ If closure: execute closure │
|
||||||
|
│ If finalized: use as-is │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
ExecutedToolCallBatch
|
||||||
|
- messages::Vector{ToolResultMessage}
|
||||||
|
- terminate::Bool (true if all tools have terminate=true)
|
||||||
```
|
```
|
||||||
|
|
||||||
### executeToolCallsSequential()
|
### executeToolCallsSequential()
|
||||||
@@ -400,29 +531,77 @@ function prepareToolCall(
|
|||||||
)::Union{PreparedToolCall, ImmediateToolCallOutcome}
|
)::Union{PreparedToolCall, ImmediateToolCallOutcome}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Flow**:
|
**Data Flow**:
|
||||||
1. Find tool by name
|
|
||||||
2. If not found → ImmediateToolCallOutcome (error)
|
```
|
||||||
3. before_tool_call hook (optional)
|
Input: tool_call::ToolCall
|
||||||
4. prepareToolCallArguments() (optional)
|
- id::String
|
||||||
5. validateToolArguments()
|
- name::String
|
||||||
6. Return PreparedToolCall
|
- arguments::Dict{String, Any}
|
||||||
|
↓
|
||||||
|
findfirst(t -> t.name == tool_call.name, current_context.tools)
|
||||||
|
↓
|
||||||
|
If tool is nothing:
|
||||||
|
→ ImmediateToolCallOutcome("immediate", error_result, is_error=true)
|
||||||
|
↓
|
||||||
|
If tool exists:
|
||||||
|
[before_tool_call hook] (optional)
|
||||||
|
Input: BeforeToolCallContext(assistant_message, tool_call, args, context)
|
||||||
|
Output: BeforeToolCallResult (block, reason) or nothing
|
||||||
|
If block=true → ImmediateToolCallOutcome(error)
|
||||||
|
↓
|
||||||
|
prepareToolCallArguments(tool, tool_call)
|
||||||
|
Input: tool_call.arguments::Dict{String, Any}
|
||||||
|
Output: prepared_arguments::Any
|
||||||
|
(Optional: transform arguments before validation)
|
||||||
|
↓
|
||||||
|
validateToolArguments(tool, prepared_tool_call)
|
||||||
|
Input: prepared_tool_call.arguments
|
||||||
|
Output: validated_args::Any
|
||||||
|
(Optional: JSON schema validation)
|
||||||
|
↓
|
||||||
|
Return: PreparedToolCall("prepared", tool_call, tool, validated_args)
|
||||||
|
- kind: "prepared"
|
||||||
|
- tool_call: ToolCall (original)
|
||||||
|
- tool: AgentTool
|
||||||
|
- args: validated arguments
|
||||||
|
```
|
||||||
|
|
||||||
### executePreparedToolCall()
|
### executePreparedToolCall()
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
function executePreparedToolCall(
|
function executePreparedToolCall(
|
||||||
prepared::PreparedToolCall,
|
prepared::PreparedToolCall,
|
||||||
signal::Union{Nothing, AbortSignal},
|
signal::Union{Nothing, AbortSignal>,
|
||||||
emit::AgentEventSink,
|
emit::AgentEventSink,
|
||||||
)::ExecutedToolCallOutcome
|
)::ExecutedToolCallOutcome
|
||||||
```
|
```
|
||||||
|
|
||||||
**Flow**:
|
**Data Flow**:
|
||||||
1. Call tool.execute(id, args, signal, on_update)
|
|
||||||
2. Collect update events (if any)
|
```
|
||||||
3. Wait for all update events
|
Input: prepared::PreparedToolCall
|
||||||
4. Return ExecutedToolCallOutcome(result)
|
- tool_call::ToolCall
|
||||||
|
- tool::AgentTool
|
||||||
|
- args::Any (validated)
|
||||||
|
↓
|
||||||
|
tool.execute(tool_call.id, args, signal, on_update)
|
||||||
|
Input: tool_call_id::String
|
||||||
|
args::Any
|
||||||
|
signal::Union{Any, Nothing}
|
||||||
|
on_update::Function (partial_result → void)
|
||||||
|
Output: AgentToolResultMutable
|
||||||
|
- content::Vector{MessageContent}
|
||||||
|
- details::Any
|
||||||
|
- usage::Union{Usage, Nothing}
|
||||||
|
- added_tool_names::Union{Vector{String}, Nothing}
|
||||||
|
- terminate::Union{Bool, Nothing}
|
||||||
|
↓
|
||||||
|
Collect update events from on_update callbacks
|
||||||
|
↓
|
||||||
|
Return: ExecutedToolCallOutcome(result, is_error=false)
|
||||||
|
- result::AgentToolResultMutable
|
||||||
|
```
|
||||||
|
|
||||||
### finalizeExecutedToolCall()
|
### finalizeExecutedToolCall()
|
||||||
|
|
||||||
@@ -433,13 +612,43 @@ function finalizeExecutedToolCall(
|
|||||||
prepared::PreparedToolCall,
|
prepared::PreparedToolCall,
|
||||||
executed::ExecutedToolCallOutcome,
|
executed::ExecutedToolCallOutcome,
|
||||||
config::AgentLoopConfig,
|
config::AgentLoopConfig,
|
||||||
signal::Union{Nothing, AbortSignal},
|
signal::Union{Nothing, AbortSignal>,
|
||||||
)::FinalizedToolCallOutcome
|
)::FinalizedToolCallOutcome
|
||||||
```
|
```
|
||||||
|
|
||||||
**Flow**:
|
**Data Flow**:
|
||||||
1. after_tool_call hook (optional)
|
|
||||||
2. Return FinalizedToolCallOutcome
|
```
|
||||||
|
Input: executed::ExecutedToolCallOutcome
|
||||||
|
- result::AgentToolResultMutable
|
||||||
|
- is_error::Bool
|
||||||
|
↓
|
||||||
|
[after_tool_call hook] (optional)
|
||||||
|
Input: AfterToolCallContext(
|
||||||
|
assistant_message,
|
||||||
|
tool_call,
|
||||||
|
args,
|
||||||
|
result,
|
||||||
|
is_error,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
Output: AfterToolCallResult (optional patches)
|
||||||
|
- content::Union{Vector{MessageContent}, Nothing}
|
||||||
|
- details::Union{Any, Nothing}
|
||||||
|
- is_error::Union{Bool, Nothing}
|
||||||
|
- usage::Union{Usage, Nothing}
|
||||||
|
- terminate::Union{Bool, Nothing}
|
||||||
|
↓
|
||||||
|
Apply patches to result (if any)
|
||||||
|
result.content = result.content ∪ patches.content
|
||||||
|
result.details = result.details ∪ patches.details
|
||||||
|
is_error = is_error ∪ patches.is_error
|
||||||
|
↓
|
||||||
|
Return: FinalizedToolCallOutcome
|
||||||
|
- tool_call::ToolCall (original)
|
||||||
|
- result::AgentToolResultMutable (final)
|
||||||
|
- is_error::Bool
|
||||||
|
```
|
||||||
|
|
||||||
### createToolResultMessage()
|
### createToolResultMessage()
|
||||||
|
|
||||||
@@ -449,19 +658,39 @@ function createToolResultMessage(
|
|||||||
)::ToolResultMessage
|
)::ToolResultMessage
|
||||||
```
|
```
|
||||||
|
|
||||||
**Creates**:
|
**Data Flow**:
|
||||||
```julia
|
|
||||||
ToolResultMessage(
|
```
|
||||||
"toolResult",
|
Input: finalized::FinalizedToolCallOutcome
|
||||||
finalized.tool_call.id,
|
- tool_call::ToolCall
|
||||||
finalized.tool_call.name,
|
- result::AgentToolResultMutable
|
||||||
finalized.result.content,
|
- content::Vector{MessageContent}
|
||||||
finalized.result.details,
|
- details::Any
|
||||||
finalized.result.usage,
|
- usage::Union{Usage, Nothing}
|
||||||
finalized.result.added_tool_names,
|
- added_tool_names::Union{Vector{String}, Nothing}
|
||||||
finalized.is_error,
|
- is_error::Bool
|
||||||
timestamp,
|
↓
|
||||||
)
|
Build ToolResultMessage:
|
||||||
|
• role: "toolResult"
|
||||||
|
• tool_call_id: tool_call.id
|
||||||
|
• tool_name: tool_call.name
|
||||||
|
• content: result.content
|
||||||
|
• details: result.details
|
||||||
|
• usage: result.usage
|
||||||
|
• added_tool_names: result.added_tool_names
|
||||||
|
• is_error: is_error
|
||||||
|
• timestamp: Int64(Dates.now(Dates.UTC).datetime)
|
||||||
|
↓
|
||||||
|
Output: ToolResultMessage
|
||||||
|
- role::String ("toolResult")
|
||||||
|
- tool_call_id::String
|
||||||
|
- tool_name::String
|
||||||
|
- content::Vector{MessageContent}
|
||||||
|
- details::Any
|
||||||
|
- usage::Union{Usage, Nothing}
|
||||||
|
- added_tool_names::Union{Vector{String}, Nothing}
|
||||||
|
- is_error::Bool
|
||||||
|
- timestamp::Timestamp (Int64)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Execution Modes
|
## Execution Modes
|
||||||
@@ -650,35 +879,70 @@ AgentStartEvent
|
|||||||
### 1. Message Transformation Pipeline
|
### 1. Message Transformation Pipeline
|
||||||
|
|
||||||
```
|
```
|
||||||
AgentMessage[] (internal)
|
Vector{AgentMessage} (internal conversation history)
|
||||||
│
|
│
|
||||||
│ transform_context()
|
├─ transform_context() (optional hook)
|
||||||
▼
|
│ Input: Vector{AgentMessage}
|
||||||
AgentMessage[] (transformed)
|
│ Output: Vector{AgentMessage} (transformed)
|
||||||
│
|
│
|
||||||
│ convert_to_llm()
|
└─ convert_to_llm()
|
||||||
▼
|
│
|
||||||
Message[] (LLM API)
|
│ Type mapping (single dispatch):
|
||||||
|
│ • UserMessage → UserMessage (pass-through)
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
│ • BashExecutionMessage → UserMessage (text conversion)
|
||||||
|
│ • CompactionSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│ • BranchSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Vector{Message} (for LLM API)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Tool Call Lifecycle
|
### 2. Tool Call Lifecycle (with Data Transformations)
|
||||||
|
|
||||||
```
|
```
|
||||||
ToolCall (in assistant message)
|
ToolCall (in AssistantMessage.content)
|
||||||
│
|
│
|
||||||
├─ before_tool_call (hook)
|
├─ before_tool_call hook (optional)
|
||||||
|
│ Input: BeforeToolCallContext(
|
||||||
|
│ assistant_message::AssistantMessage,
|
||||||
|
│ tool_call::ToolCall,
|
||||||
|
│ args::Dict{String, Any},
|
||||||
|
│ context::AgentContext
|
||||||
|
│ )
|
||||||
|
│ Output: BeforeToolCallResult (block, reason) or nothing
|
||||||
│
|
│
|
||||||
├─ prepareToolCall()
|
├─ prepareToolCall()
|
||||||
│ ├─ validate arguments
|
│ Input: tool_call::ToolCall
|
||||||
│ └─ prepare arguments (optional)
|
│ Output: Union{PreparedToolCall, ImmediateToolCallOutcome}
|
||||||
|
│ • PreparedToolCall (kind, tool_call, tool, args)
|
||||||
|
│ • ImmediateToolCallOutcome (immediate, result, is_error)
|
||||||
│
|
│
|
||||||
├─ execute()
|
├─ executePreparedToolCall() (if prepared)
|
||||||
│ ├─ Immediate: return result
|
│ Input: PreparedToolCall
|
||||||
│ └─ Prepared: async execution
|
│ Output: ExecutedToolCallOutcome
|
||||||
|
│ tool.execute() returns AgentToolResultMutable
|
||||||
|
│ • content::Vector{MessageContent}
|
||||||
|
│ • details::Any
|
||||||
|
│ • usage::Union{Usage, Nothing}
|
||||||
|
│ • terminate::Union{Bool, Nothing}
|
||||||
│
|
│
|
||||||
├─ after_tool_call (hook)
|
├─ finalizeExecutedToolCall()
|
||||||
|
│ Input: ExecutedToolCallOutcome
|
||||||
|
│ Output: FinalizedToolCallOutcome
|
||||||
|
│ • tool_call::ToolCall
|
||||||
|
│ • result::AgentToolResultMutable
|
||||||
|
│ • is_error::Bool
|
||||||
│
|
│
|
||||||
└─ createToolResultMessage()
|
└─ createToolResultMessage()
|
||||||
|
Input: FinalizedToolCallOutcome
|
||||||
|
Output: ToolResultMessage
|
||||||
|
• role: "toolResult"
|
||||||
|
• tool_call_id, tool_name
|
||||||
|
• content::Vector{MessageContent}
|
||||||
|
• details, usage, added_tool_names
|
||||||
|
• is_error, timestamp
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Turn Termination
|
### 3. Turn Termination
|
||||||
|
|||||||
+274
-15
@@ -98,6 +98,52 @@
|
|||||||
|
|
||||||
## Message Types
|
## Message Types
|
||||||
|
|
||||||
|
### Type Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
Message (for LLM API)
|
||||||
|
├── UserMessage (role: "user")
|
||||||
|
│ ├── content::Vector{MessageContent}
|
||||||
|
│ │ ├── TextContent (text::String)
|
||||||
|
│ │ └── ImageContent (data::String, mime_type::String)
|
||||||
|
│ └── timestamp::Timestamp (Int64)
|
||||||
|
├── AssistantMessage (role: "assistant")
|
||||||
|
│ ├── content::Vector{MessageContent}
|
||||||
|
│ │ ├── TextContent
|
||||||
|
│ │ └── ToolCall (type, id, name, arguments::Dict{String, Any})
|
||||||
|
│ ├── api::String
|
||||||
|
│ ├── provider::String
|
||||||
|
│ ├── model::String
|
||||||
|
│ ├── usage::Usage
|
||||||
|
│ │ ├── input, output, cache_read, cache_write, total_tokens::Int64
|
||||||
|
│ │ └── cost::UsageCost (input, output, cache_read, cache_write, total::Float64)
|
||||||
|
│ ├── stop_reason::String
|
||||||
|
│ ├── error_message::Union{String, Nothing}
|
||||||
|
│ └── timestamp::Timestamp
|
||||||
|
└── ToolResultMessage (role: "toolResult")
|
||||||
|
├── tool_call_id::String
|
||||||
|
├── tool_name::String
|
||||||
|
├── content::Vector{MessageContent}
|
||||||
|
├── details::Any
|
||||||
|
├── usage::Union{Usage, Nothing}
|
||||||
|
├── added_tool_names::Union{Vector{String}, Nothing}
|
||||||
|
├── is_error::Bool
|
||||||
|
└── timestamp::Timestamp
|
||||||
|
|
||||||
|
AgentMessage (internal, extends Message)
|
||||||
|
├── UserMessage (same as above)
|
||||||
|
├── AssistantMessage (same as above)
|
||||||
|
├── ToolResultMessage (same as above)
|
||||||
|
├── BashExecutionMessage (custom, converted to UserMessage)
|
||||||
|
│ ├── role, command, output, exit_code
|
||||||
|
│ ├── cancelled, truncated, full_output_path
|
||||||
|
│ └── exclude_from_context::Bool
|
||||||
|
├── CompactionSummaryMessage (custom, converted to UserMessage)
|
||||||
|
│ ├── summary, tokens_before, timestamp
|
||||||
|
└── BranchSummaryMessage (custom, converted to UserMessage)
|
||||||
|
├── summary, from_id, timestamp
|
||||||
|
```
|
||||||
|
|
||||||
### UserMessage
|
### UserMessage
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
@@ -108,6 +154,16 @@ struct UserMessage <: Message
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
```julia
|
||||||
|
# Simple text message
|
||||||
|
UserMessage(
|
||||||
|
"user",
|
||||||
|
[TextContent("Hello, how are you?")],
|
||||||
|
Int64(Dates.now(Dates.UTC).datetime)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
**Usage**:
|
**Usage**:
|
||||||
```julia
|
```julia
|
||||||
# Simple text message
|
# Simple text message
|
||||||
@@ -496,9 +552,9 @@ end
|
|||||||
|
|
||||||
**Note**: AgentState is mutable and used internally by Agent
|
**Note**: AgentState is mutable and used internally by Agent
|
||||||
|
|
||||||
## Key Conversion Functions
|
## Message Transformation Pipeline
|
||||||
|
|
||||||
### convertToLlm()
|
### convertToLlm() - AgentMessage[] → Message[]
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
function convertToLlm(messages::Vector{AgentMessage})::Vector{Message}
|
function convertToLlm(messages::Vector{AgentMessage})::Vector{Message}
|
||||||
@@ -515,26 +571,53 @@ function convertToLlm(messages::Vector{AgentMessage})::Vector{Message}
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
**Purpose**: Transform AgentMessage[] to Message[] for LLM API
|
**Data Flow**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Vector{AgentMessage} (internal conversation history)
|
||||||
|
│
|
||||||
|
│ Type dispatch on convertToLlmMessage():
|
||||||
|
│
|
||||||
|
│ • UserMessage → UserMessage (pass-through)
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
│
|
||||||
|
│ Custom messages converted to UserMessage:
|
||||||
|
│ • BashExecutionMessage → UserMessage
|
||||||
|
│ (via bashExecutionToText() for display)
|
||||||
|
│ • CompactionSummaryMessage → UserMessage
|
||||||
|
│ (wrapped with COMPACTION_SUMMARY_PREFIX/SUFFIX)
|
||||||
|
│ • BranchSummaryMessage → UserMessage
|
||||||
|
│ (wrapped with BRANCH_SUMMARY_PREFIX/SUFFIX)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Vector{Message} (for LLM API)
|
||||||
|
- Excludes: BashExecutionMessage (if exclude_from_context)
|
||||||
|
- Includes: All standard messages + converted custom messages
|
||||||
|
```
|
||||||
|
|
||||||
**Example**:
|
**Example**:
|
||||||
```julia
|
```julia
|
||||||
# Input: AgentMessage[]
|
# Input: Vector{AgentMessage}
|
||||||
[
|
[
|
||||||
UserMessage(...),
|
UserMessage("user", [TextContent("Hello")], 1234567890),
|
||||||
AssistantMessage(...),
|
AssistantMessage("assistant", [
|
||||||
ToolResultMessage(...),
|
TextContent("Hi there!"),
|
||||||
BashExecutionMessage(...), # Will be converted to UserMessage
|
ToolCall("bash", "call_123", "bash", Dict("command" => "ls"), nothing)
|
||||||
CompactionSummaryMessage(...), # Will be converted to UserMessage
|
], "openai", "openai", "gpt-4", Usage(...), "done", nothing, 1234567891),
|
||||||
|
BashExecutionMessage("custom", "ls -la", "file1.md\nfile2.md\n", 0, false, false, nothing, 1234567892, false),
|
||||||
|
CompactionSummaryMessage("compactionSummary", "Previous conversation compacted", 1000, 1234567893),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Output: Message[]
|
# Output: Vector{Message}
|
||||||
[
|
[
|
||||||
UserMessage(...),
|
UserMessage("user", [TextContent("Hello")], 1234567890),
|
||||||
AssistantMessage(...),
|
AssistantMessage("assistant", [
|
||||||
ToolResultMessage(...),
|
TextContent("Hi there!"),
|
||||||
UserMessage(...), # Converted from BashExecutionMessage
|
ToolCall(...)
|
||||||
UserMessage(...), # Converted from CompactionSummaryMessage
|
], "openai", "openai", "gpt-4", Usage(...), "done", nothing, 1234567891),
|
||||||
|
UserMessage("user", [TextContent("Ran `ls -la`\n```\nfile1.md\nfile2.md\n```\n")], 1234567892),
|
||||||
|
UserMessage("user", [TextContent("<summary>Previous conversation compacted</summary>")], 1234567893),
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -571,6 +654,182 @@ function convertToLlmMessage(m::ToolResultMessage)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Complete Data Flow Examples
|
||||||
|
|
||||||
|
### Example 1: User Prompt → Assistant Response
|
||||||
|
|
||||||
|
```
|
||||||
|
User Input:
|
||||||
|
"Hello, what's in the current directory?"
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
prompt(agent, "Hello, what's in the current directory?")
|
||||||
|
│
|
||||||
|
└─► normalizePromptInput(String)
|
||||||
|
Input: "Hello, what's in the current directory?"
|
||||||
|
Output: [UserMessage("user", [TextContent("Hello, what's in the current directory?")], timestamp)]
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
AgentLoop execution:
|
||||||
|
│
|
||||||
|
├─► transform_context() (optional)
|
||||||
|
│ Input: [UserMessage(...)]
|
||||||
|
│ Output: [UserMessage(...)]
|
||||||
|
│
|
||||||
|
├─► convert_to_llm()
|
||||||
|
│ Input: [UserMessage(...)]
|
||||||
|
│ Output: [UserMessage(...)]
|
||||||
|
│
|
||||||
|
├─► stream_fn() - LLM API
|
||||||
|
│ Input: model, Context(...), config
|
||||||
|
│ Output: AssistantMessage with ToolCall[]
|
||||||
|
│ role: "assistant"
|
||||||
|
│ content: [
|
||||||
|
│ TextContent("I'll check the directory for you."),
|
||||||
|
│ ToolCall("tool", "tc_123", "bash", Dict("command" => "ls -la"), nothing)
|
||||||
|
│ ]
|
||||||
|
│ usage: Usage(input=100, output=20, ...)
|
||||||
|
│ stop_reason: "done"
|
||||||
|
│
|
||||||
|
├─► executeToolCalls()
|
||||||
|
│ Input: AssistantMessage with ToolCall[]
|
||||||
|
│ Output: ToolResultMessage[]
|
||||||
|
│ role: "toolResult"
|
||||||
|
│ tool_call_id: "tc_123"
|
||||||
|
│ tool_name: "bash"
|
||||||
|
│ content: [TextContent("file1.md\nfile2.md\n")]
|
||||||
|
│ is_error: false
|
||||||
|
│
|
||||||
|
└─► Append to context.messages
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
Final Conversation History:
|
||||||
|
[
|
||||||
|
UserMessage("user", [TextContent("Hello, what's in the current directory?")], ...),
|
||||||
|
AssistantMessage("assistant", [
|
||||||
|
TextContent("I'll check the directory for you."),
|
||||||
|
ToolCall("tool", "tc_123", "bash", Dict("command" => "ls -la"), nothing)
|
||||||
|
], "openai", "openai", "gpt-4", Usage(...), "done", nothing, ...),
|
||||||
|
ToolResultMessage("toolResult", "tc_123", "bash", [TextContent("file1.md\nfile2.md\n")], ..., false, ...),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Tool Call Execution → Tool Result
|
||||||
|
|
||||||
|
```
|
||||||
|
ToolCall from AssistantMessage
|
||||||
|
│
|
||||||
|
├─ type: "tool"
|
||||||
|
├─ id: "tc_123"
|
||||||
|
├─ name: "bash"
|
||||||
|
├─ arguments: Dict("command" => "ls -la")
|
||||||
|
└─ partial_json: nothing
|
||||||
|
↓
|
||||||
|
prepareToolCall(tool_call)
|
||||||
|
↓
|
||||||
|
Finds tool by name "bash"
|
||||||
|
↓
|
||||||
|
before_tool_call hook (optional)
|
||||||
|
Input: BeforeToolCallContext(...)
|
||||||
|
Output: BeforeToolCallResult(block=false) or nothing
|
||||||
|
↓
|
||||||
|
validateToolArguments(tool_call)
|
||||||
|
Input: Dict("command" => "ls -la")
|
||||||
|
Output: Dict("command" => "ls -la")
|
||||||
|
↓
|
||||||
|
Return: PreparedToolCall("prepared", tool_call, bash_tool, validated_args)
|
||||||
|
↓
|
||||||
|
executePreparedToolCall(prepared)
|
||||||
|
↓
|
||||||
|
tool.execute("tc_123", Dict("command" => "ls -la"), signal, on_update)
|
||||||
|
↓
|
||||||
|
Bash tool executes "ls -la" command
|
||||||
|
Returns: AgentToolResultMutable(
|
||||||
|
content: [TextContent("file1.md\nfile2.md\n")],
|
||||||
|
details: BashToolDetails(...),
|
||||||
|
usage: nothing,
|
||||||
|
added_tool_names: nothing,
|
||||||
|
terminate: nothing
|
||||||
|
)
|
||||||
|
↓
|
||||||
|
finalizeExecutedToolCall(executed)
|
||||||
|
↓
|
||||||
|
after_tool_call hook (optional)
|
||||||
|
Input: AfterToolCallContext(...)
|
||||||
|
Output: AfterToolCallResult(...) or nothing
|
||||||
|
↓
|
||||||
|
Return: FinalizedToolCallOutcome(
|
||||||
|
tool_call: ToolCall(...),
|
||||||
|
result: AgentToolResultMutable(...),
|
||||||
|
is_error: false
|
||||||
|
)
|
||||||
|
↓
|
||||||
|
createToolResultMessage(finalized)
|
||||||
|
↓
|
||||||
|
Return: ToolResultMessage(
|
||||||
|
role: "toolResult",
|
||||||
|
tool_call_id: "tc_123",
|
||||||
|
tool_name: "bash",
|
||||||
|
content: [TextContent("file1.md\nfile2.md\n")],
|
||||||
|
details: BashToolDetails(...),
|
||||||
|
usage: nothing,
|
||||||
|
added_tool_names: nothing,
|
||||||
|
is_error: false,
|
||||||
|
timestamp: Int64(...)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Custom Message Conversion
|
||||||
|
|
||||||
|
```
|
||||||
|
BashExecutionMessage (custom, for logging)
|
||||||
|
│
|
||||||
|
role: "custom"
|
||||||
|
command: "ls -la"
|
||||||
|
output: "file1.md\nfile2.md\n"
|
||||||
|
exit_code: 0
|
||||||
|
cancelled: false
|
||||||
|
truncated: false
|
||||||
|
full_output_path: nothing
|
||||||
|
timestamp: 1234567890
|
||||||
|
exclude_from_context: false
|
||||||
|
↓
|
||||||
|
convertToLlmMessage(BashExecutionMessage)
|
||||||
|
↓
|
||||||
|
bashExecutionToText(msg)
|
||||||
|
Output: "Ran `ls -la`\n```\nfile1.md\nfile2.md\n```\n"
|
||||||
|
↓
|
||||||
|
Return: UserMessage(
|
||||||
|
"user",
|
||||||
|
[TextContent("Ran `ls -la`\n```\nfile1.md\nfile2.md\n```\n")],
|
||||||
|
1234567890
|
||||||
|
)
|
||||||
|
↓
|
||||||
|
(Excluded if exclude_from_context = true)
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CompactionSummaryMessage (custom, for history compression)
|
||||||
|
│
|
||||||
|
role: "compactionSummary"
|
||||||
|
summary: "Previous 100 turns about Python programming"
|
||||||
|
tokens_before: 15000
|
||||||
|
timestamp: 1234567890
|
||||||
|
↓
|
||||||
|
convertToLlmMessage(CompactionSummaryMessage)
|
||||||
|
↓
|
||||||
|
Text = COMPACTION_SUMMARY_PREFIX + summary + COMPACTION_SUMMARY_SUFFIX
|
||||||
|
Result: "<summary>\nPrevious 100 turns about Python programming\n</summary>"
|
||||||
|
↓
|
||||||
|
Return: UserMessage(
|
||||||
|
"user",
|
||||||
|
[TextContent("<summary>...\nPrevious 100 turns...\n</summary>")],
|
||||||
|
1234567890
|
||||||
|
)
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
The type system in AgentCore.jl provides:
|
The type system in AgentCore.jl provides:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AgentCore.jl - Session Management Deep Dive
|
# AgentCore.jl - Session Management Deep Dive
|
||||||
|
|
||||||
## Session Architecture
|
## Session Architecture with Data Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
@@ -20,6 +20,12 @@
|
|||||||
│ ▼ ▼ ▼ ▼ ▼ │
|
│ ▼ ▼ ▼ ▼ ▼ │
|
||||||
│ Message Message Compaction Message BranchSummary │
|
│ Message Message Compaction Message BranchSummary │
|
||||||
│ │
|
│ │
|
||||||
|
│ Data Flow: │
|
||||||
|
│ AgentMessage[] (AgentState.messages) │
|
||||||
|
│ │ │
|
||||||
|
│ └─► appendMessage() → MessageEntry │
|
||||||
|
│ └─► storage.appendEntry() → JSONL file │
|
||||||
|
│ │
|
||||||
│ To navigate to E2 (fork point): │
|
│ To navigate to E2 (fork point): │
|
||||||
│ Session.moveTo(E2) │
|
│ Session.moveTo(E2) │
|
||||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||||
@@ -33,6 +39,38 @@
|
|||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Data Flow: AgentMessage → SessionTreeEntry
|
||||||
|
|
||||||
|
```
|
||||||
|
AgentState.messages::Vector{AgentMessage}
|
||||||
|
│
|
||||||
|
├─► For each message in messages:
|
||||||
|
│ │
|
||||||
|
│ ▼
|
||||||
|
│ ┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ │ appendMessage(session, AgentMessage) │
|
||||||
|
│ │ Input: message::AgentMessage │
|
||||||
|
│ │ Output: entry_id::String │
|
||||||
|
│ │ │
|
||||||
|
│ │ Steps: │
|
||||||
|
│ │ 1. Create MessageEntry: │
|
||||||
|
│ │ - type: "message" │
|
||||||
|
│ │ - id: createEntryId(storage) │
|
||||||
|
│ │ - parent_id: getLeafId(storage) │
|
||||||
|
│ │ - timestamp: create_timestamp() │
|
||||||
|
│ │ - message: copy(message) │
|
||||||
|
│ │ 2. storage.appendEntry(entry) │
|
||||||
|
│ │ - Write JSONL line to file │
|
||||||
|
│ │ - Update leaf_id │
|
||||||
|
│ │ 3. Return entry.id │
|
||||||
|
│ └──────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
└─► Entry stored in JSONL:
|
||||||
|
{"type":"message","id":"msg_1","parent_id":null,"timestamp":"...","message":{...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Entry Types
|
||||||
|
|
||||||
## Entry Types
|
## Entry Types
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
|
|||||||
+84
-53
@@ -1,6 +1,6 @@
|
|||||||
# AgentCore.jl - Tools Deep Dive
|
# AgentCore.jl - Tools Deep Dive
|
||||||
|
|
||||||
## Tool Architecture
|
## Tool Architecture with Data Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
│ - name: String (identifier) │
|
│ - name: String (identifier) │
|
||||||
│ - label: String (display name) │
|
│ - label: String (display name) │
|
||||||
│ - description: String (what it does) │
|
│ - description: String (what it does) │
|
||||||
│ - parameters: JSON schema │
|
│ - parameters::Any (JSON schema or type) │
|
||||||
│ - execute::Function (main logic) │
|
│ - execute::Function (main logic) │
|
||||||
│ - prepare_arguments::Union{Function, Nothing} │
|
│ - prepare_arguments::Union{Function, Nothing} │
|
||||||
│ - execution_mode::Union{ToolExecutionMode, Nothing} │
|
│ - execution_mode::Union{ToolExecutionMode, Nothing} │
|
||||||
@@ -21,77 +21,108 @@
|
|||||||
┌───────────────┼───────────────┐
|
┌───────────────┼───────────────┐
|
||||||
│ │ │
|
│ │ │
|
||||||
▼ ▼ ▼
|
▼ ▼ ▼
|
||||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
│ BashTool │ │ ReadTool │ │ WriteTool │
|
│ BashTool │ │ ReadTool │ │ WriteTool │
|
||||||
│ - bash() │ │ - read() │ │ - write() │
|
│ - bash() │ │ - read() │ │ - write() │
|
||||||
└─────────────┘ └─────────────┘ └─────────────┘
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
┌─────────────┐
|
┌─────────────┐
|
||||||
│ EditTool │
|
│ EditTool │
|
||||||
│ - edit() │
|
│ - edit() │
|
||||||
└─────────────┘
|
└─────────────┘
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ Tool Execution Flow │
|
│ Tool Execution Data Flow │
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
Assistant Message
|
Input: AssistantMessage (from LLM)
|
||||||
┌────────────────────────────────────────────────────────┐
|
content::Vector{MessageContent}
|
||||||
│ AssistantMessage: │
|
└─ Contains: TextContent[] and ToolCall[]
|
||||||
│ content: [ │
|
|
||||||
│ TextContent("I'll check the files..."), │
|
▼
|
||||||
│ ToolCall("bash", {command: "ls -la"}), │
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
│ ToolCall("read", {path: "README.md"}) │
|
│ extract ToolCalls │
|
||||||
│ ] │
|
│ filter(c -> c isa ToolCall, assistant_message.content) │
|
||||||
└────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
│ AgentLoop.executeToolCalls() │
|
│ ToolCall Type │
|
||||||
│ - Extract ToolCalls from message content │
|
│ • type::String ("tool") │
|
||||||
│ - Determine execution mode (sequential/parallel) │
|
│ • id::String (unique identifier) │
|
||||||
└────────────────────────────────────────────────────────┘
|
│ • name::String (tool name to execute) │
|
||||||
│
|
│ • arguments::Dict{String, Any} (JSON-like arguments) │
|
||||||
├─► executeToolCallsSequential()
|
│ • partial_json::Union{String, Nothing} │
|
||||||
│ (for tools that require order)
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
│
|
|
||||||
└─► executeToolCallsParallel()
|
|
||||||
(for independent tools)
|
|
||||||
|
|
||||||
│
|
│
|
||||||
├─► prepareToolCall()
|
├─► prepareToolCall()
|
||||||
│ - before_tool_call hook (optional)
|
│ Input: tool_call::ToolCall
|
||||||
│ - validate arguments
|
│ Output: Union{PreparedToolCall, ImmediateToolCallOutcome}
|
||||||
│ - prepare arguments (optional)
|
|
||||||
│
|
│
|
||||||
├─► execute()
|
│ Steps:
|
||||||
│ - Tool-specific logic
|
│ 1. Find tool by name in context.tools
|
||||||
│ - Return AgentToolResult
|
│ 2. before_tool_call hook (optional)
|
||||||
|
│ Input: BeforeToolCallContext
|
||||||
|
│ Output: BeforeToolCallResult (block, reason)
|
||||||
|
│ 3. prepareToolCallArguments() (optional)
|
||||||
|
│ Input: tool_call.arguments::Dict{String, Any}
|
||||||
|
│ Output: prepared_arguments::Any
|
||||||
|
│ 4. validateToolArguments()
|
||||||
|
│ Input: prepared_tool_call.arguments
|
||||||
|
│ Output: validated_args::Any
|
||||||
|
│ 5. Return: PreparedToolCall(kind, tool_call, tool, args)
|
||||||
|
│
|
||||||
|
├─► executePreparedToolCall() (if prepared)
|
||||||
|
│ Input: PreparedToolCall
|
||||||
|
│ Output: ExecutedToolCallOutcome
|
||||||
|
│
|
||||||
|
│ tool.execute(tool_call.id, args, signal, on_update)
|
||||||
|
│ Input: tool_call_id::String
|
||||||
|
│ args::Any
|
||||||
|
│ signal::Union{Any, Nothing}
|
||||||
|
│ on_update::Function (streaming updates)
|
||||||
|
│ Output: AgentToolResultMutable
|
||||||
|
│ • content::Vector{MessageContent}
|
||||||
|
│ • details::Any
|
||||||
|
│ • usage::Union{Usage, Nothing}
|
||||||
|
│ • terminate::Union{Bool, Nothing}
|
||||||
│
|
│
|
||||||
├─► finalizeExecutedToolCall()
|
├─► finalizeExecutedToolCall()
|
||||||
│ - after_tool_call hook (optional)
|
│ Input: ExecutedToolCallOutcome
|
||||||
|
│ Output: FinalizedToolCallOutcome
|
||||||
|
│
|
||||||
|
│ Steps:
|
||||||
|
│ 1. after_tool_call hook (optional)
|
||||||
|
│ Input: AfterToolCallContext
|
||||||
|
│ Output: AfterToolCallResult (patches)
|
||||||
|
│ 2. Apply patches to result
|
||||||
|
│ 3. Return: FinalizedToolCallOutcome(tool_call, result, is_error)
|
||||||
│
|
│
|
||||||
└─► createToolResultMessage()
|
└─► createToolResultMessage()
|
||||||
- Emit ToolResultMessage
|
Input: FinalizedToolCallOutcome
|
||||||
|
Output: ToolResultMessage
|
||||||
|
• role: "toolResult"
|
||||||
|
• tool_call_id::String (matches ToolCall.id)
|
||||||
|
• tool_name::String (matches ToolCall.name)
|
||||||
|
• content::Vector{MessageContent}
|
||||||
|
• details::Any
|
||||||
|
• usage::Union{Usage, Nothing}
|
||||||
|
• added_tool_names::Union{Vector{String}, Nothing}
|
||||||
|
• is_error::Bool
|
||||||
|
• timestamp::Timestamp (Int64)
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
│ ToolResultMessage │
|
│ ToolResultMessage[] (one per ToolCall) │
|
||||||
│ - tool_call_id: "ref to original ToolCall" │
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
│ - tool_name: "bash" │
|
|
||||||
│ - content: [TextContent("file1.md\nfile2.md\n")] │
|
|
||||||
│ - is_error: false │
|
|
||||||
└────────────────────────────────────────────────────────┘
|
|
||||||
│
|
│
|
||||||
▼
|
├─► Append to context.messages (AgentState.messages)
|
||||||
┌────────────────────────────────────────────────────────┐
|
└─► Next turn: LLM sees tool results as input
|
||||||
│ AgentState.messages.append(tool_result) │
|
|
||||||
│ - Next turn: LLM sees tool results │
|
|
||||||
└────────────────────────────────────────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Built-in Tools
|
## Built-in Tools
|
||||||
|
|
||||||
|
## Built-in Tools
|
||||||
|
|
||||||
### 1. BashTool
|
### 1. BashTool
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
|
|||||||
+186
-12
@@ -165,32 +165,206 @@ AgentStartEvent
|
|||||||
└─ AgentEndEvent
|
└─ AgentEndEvent
|
||||||
```
|
```
|
||||||
|
|
||||||
## Data Flow
|
## Complete Data Flow with Type Transformations
|
||||||
|
|
||||||
### Message Transformation
|
This documentation shows how data is transformed through the agent lifecycle.
|
||||||
|
|
||||||
|
### Message Type Hierarchy
|
||||||
|
|
||||||
```
|
```
|
||||||
AgentMessage[] (internal)
|
Message (for LLM API)
|
||||||
|
├── UserMessage (role: "user")
|
||||||
|
│ └── content::Vector{MessageContent}
|
||||||
|
│ ├── TextContent (text::String)
|
||||||
|
│ └── ImageContent (data::String, mime_type::String)
|
||||||
|
├── AssistantMessage (role: "assistant")
|
||||||
|
│ ├── content::Vector{MessageContent}
|
||||||
|
│ │ ├── TextContent
|
||||||
|
│ │ └── ToolCall (id, name, arguments::Dict{String, Any})
|
||||||
|
│ ├── usage::Usage
|
||||||
|
│ ├── stop_reason::String
|
||||||
|
│ └── timestamp::Timestamp
|
||||||
|
└── ToolResultMessage (role: "toolResult")
|
||||||
|
├── tool_call_id::String
|
||||||
|
├── tool_name::String
|
||||||
|
├── content::Vector{MessageContent}
|
||||||
|
├── details::Any
|
||||||
|
├── usage::Union{Usage, Nothing}
|
||||||
|
├── is_error::Bool
|
||||||
|
└── timestamp::Timestamp
|
||||||
|
|
||||||
|
AgentMessage (internal, extends Message)
|
||||||
|
├── UserMessage (same as above)
|
||||||
|
├── AssistantMessage (same as above)
|
||||||
|
├── ToolResultMessage (same as above)
|
||||||
|
├── BashExecutionMessage (custom)
|
||||||
|
│ ├── role, command, output, exit_code
|
||||||
|
│ ├── cancelled, truncated, exclude_from_context
|
||||||
|
│ └── timestamp
|
||||||
|
├── CompactionSummaryMessage (custom)
|
||||||
|
│ ├── summary, tokens_before, timestamp
|
||||||
|
│ └── converted to UserMessage for LLM
|
||||||
|
└── BranchSummaryMessage (custom)
|
||||||
|
├── summary, from_id, timestamp
|
||||||
|
└── converted to UserMessage for LLM
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complete Conversation Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Step 1: User Input (Vector{AgentMessage}) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
prompt(agent, "Hello!")
|
||||||
│
|
│
|
||||||
├─ transform_context() (optional)
|
└─► normalizePromptInput()
|
||||||
▼
|
Input: "Hello!"::String
|
||||||
AgentMessage[] (transformed)
|
Output: [UserMessage("user", [TextContent("Hello!")], timestamp)]
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Step 2: AgentLoop Processing │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
runAgentLoop()
|
||||||
|
│
|
||||||
|
├─► transform_context() (optional hook)
|
||||||
|
│ Input: [UserMessage(...)]::Vector{AgentMessage}
|
||||||
|
│ Output: [UserMessage(...)]::Vector{AgentMessage}
|
||||||
|
│
|
||||||
|
├─► convert_to_llm()
|
||||||
|
│ Input: [UserMessage(...)]::Vector{AgentMessage}
|
||||||
|
│ Output: [UserMessage(...)]::Vector{Message}
|
||||||
|
│
|
||||||
|
├─► stream_fn() - LLM API call
|
||||||
|
│ Input: model, Context(...), config
|
||||||
|
│ Output: AssistantMessage with ToolCall[]
|
||||||
|
│
|
||||||
|
├─► executeToolCalls()
|
||||||
|
│ Input: AssistantMessage (with ToolCall[])
|
||||||
|
│ Output: ToolResultMessage[]
|
||||||
|
│
|
||||||
|
└─► Emit events and append to context.messages
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Step 3: Final Conversation State │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
context.messages::Vector{AgentMessage}
|
||||||
|
├─ UserMessage("user", [TextContent("Hello!")], ...)
|
||||||
|
├─ AssistantMessage("assistant", [
|
||||||
|
│ TextContent("Hi there!"),
|
||||||
|
│ ToolCall("bash", {...})
|
||||||
|
│ ], ...)
|
||||||
|
└─ ToolResultMessage("toolResult", "bash", [TextContent("...")], ...)
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Step 4: AgentEndEvent (final output) │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
AgentEndEvent(messages::Vector{AgentMessage})
|
||||||
|
└─ Contains full conversation history
|
||||||
|
User Input (String / AgentMessage / Vector{AgentMessage})
|
||||||
|
│
|
||||||
|
├─► normalizePromptInput()
|
||||||
|
│ Input: input::Union{String, AgentMessage, Vector{AgentMessage}}
|
||||||
|
│ Output: Vector{AgentMessage}
|
||||||
|
│ • String → UserMessage("user", [TextContent(input)], timestamp)
|
||||||
|
│ • AgentMessage → [input]
|
||||||
|
│ • Vector{AgentMessage} → input (pass-through)
|
||||||
|
│
|
||||||
|
├─► prompt(agent, messages)
|
||||||
|
│ └─► runPromptMessages()
|
||||||
│
|
│
|
||||||
├─ convert_to_llm()
|
|
||||||
▼
|
▼
|
||||||
Message[] (LLM API)
|
AgentLoop Execution:
|
||||||
|
│
|
||||||
|
├─► transform_context() (optional hook)
|
||||||
|
│ Input: context.messages::Vector{AgentMessage}
|
||||||
|
│ Output: messages::Vector{AgentMessage} (transformed)
|
||||||
|
│
|
||||||
|
├─► convert_to_llm()
|
||||||
|
│ Input: messages::Vector{AgentMessage}
|
||||||
|
│ Output: llm_messages::Vector{Message}
|
||||||
|
│
|
||||||
|
│ AgentMessage → Message mapping:
|
||||||
|
│ • UserMessage → UserMessage (pass-through)
|
||||||
|
│ • AssistantMessage → AssistantMessage (pass-through)
|
||||||
|
│ • ToolResultMessage → ToolResultMessage (pass-through)
|
||||||
|
│ • BashExecutionMessage → UserMessage (text conversion)
|
||||||
|
│ • CompactionSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│ • BranchSummaryMessage → UserMessage (text wrapped)
|
||||||
|
│
|
||||||
|
├─► LLM API Call (stream_fn)
|
||||||
|
│ Input: model, Context(system_prompt, llm_messages, tools), config
|
||||||
|
│ Output: Stream{AssistantMessageEvent}
|
||||||
|
│
|
||||||
|
├─► AssistantMessage (returned from LLM)
|
||||||
|
│ content::Vector{MessageContent}
|
||||||
|
│ └─ Contains: TextContent[] and/or ToolCall[]
|
||||||
|
│
|
||||||
|
├─► executeToolCalls() (if ToolCall[] in content)
|
||||||
|
│ │
|
||||||
|
│ ├─► prepareToolCall() for each ToolCall
|
||||||
|
│ │ Input: tool_call::ToolCall
|
||||||
|
│ │ Output: PreparedToolCall or ImmediateToolCallOutcome
|
||||||
|
│ │
|
||||||
|
│ ├─► executePreparedToolCall() (if prepared)
|
||||||
|
│ │ Input: PreparedToolCall
|
||||||
|
│ │ Output: ExecutedToolCallOutcome
|
||||||
|
│ │ tool.execute() returns AgentToolResultMutable
|
||||||
|
│ │
|
||||||
|
│ ├─► finalizeExecutedToolCall()
|
||||||
|
│ │ Input: ExecutedToolCallOutcome
|
||||||
|
│ │ Output: FinalizedToolCallOutcome
|
||||||
|
│ │
|
||||||
|
│ └─► createToolResultMessage()
|
||||||
|
│ Input: FinalizedToolCallOutcome
|
||||||
|
│ Output: ToolResultMessage
|
||||||
|
│ • role: "toolResult"
|
||||||
|
│ • tool_call_id, tool_name
|
||||||
|
│ • content::Vector{MessageContent}
|
||||||
|
│ • details, usage, added_tool_names
|
||||||
|
│ • is_error, timestamp
|
||||||
|
│
|
||||||
|
└─► Append to context.messages and new_messages
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Vector{AgentMessage} (final conversation history)
|
||||||
|
Contains: [UserMessage, AssistantMessage, ToolResultMessage, ...]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tool Execution Flow
|
### Tool Execution Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
ToolCall (in assistant message)
|
ToolCall (in AssistantMessage.content)
|
||||||
|
│
|
||||||
|
├─ before_tool_call hook (optional)
|
||||||
|
│ Input: BeforeToolCallContext
|
||||||
|
│ Output: BeforeToolCallResult (block, reason) or nothing
|
||||||
│
|
│
|
||||||
├─ before_tool_call hook
|
|
||||||
├─ prepareToolCall()
|
├─ prepareToolCall()
|
||||||
├─ execute()
|
│ Input: tool_call::ToolCall
|
||||||
├─ after_tool_call hook
|
│ Output: Union{PreparedToolCall, ImmediateToolCallOutcome}
|
||||||
|
│ • Validates tool exists
|
||||||
|
│ • Runs before_tool_call hook
|
||||||
|
│ • Runs prepare_arguments hook (optional)
|
||||||
|
│ • Runs validateToolArguments (optional)
|
||||||
|
│
|
||||||
|
├─ executePreparedToolCall() (if prepared)
|
||||||
|
│ Input: PreparedToolCall
|
||||||
|
│ Output: ExecutedToolCallOutcome
|
||||||
|
│ tool.execute() returns AgentToolResultMutable
|
||||||
|
│
|
||||||
|
├─ finalizeExecutedToolCall()
|
||||||
|
│ Input: ExecutedToolCallOutcome
|
||||||
|
│ Output: FinalizedToolCallOutcome
|
||||||
|
│ Runs after_tool_call hook (optional)
|
||||||
|
│
|
||||||
└─ createToolResultMessage()
|
└─ createToolResultMessage()
|
||||||
|
Input: FinalizedToolCallOutcome
|
||||||
|
Output: ToolResultMessage
|
||||||
|
• role: "toolResult"
|
||||||
|
• tool_call_id, tool_name
|
||||||
|
• content::Vector{MessageContent}
|
||||||
|
• details, usage, added_tool_names
|
||||||
|
• is_error, timestamp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|||||||
Reference in New Issue
Block a user