751 lines
61 KiB
Markdown
751 lines
61 KiB
Markdown
# AgentCore.jl - Architecture Overview
|
|
|
|
## Top-Down Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ AgentCore.jl Layers │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Level 1: AgentHarness (Session Management & Persistence) │
|
|
│ - Session persistence with JSONL storage │
|
|
│ - Resource management (skills, prompt templates) │
|
|
│ - Extension hooks system │
|
|
│ - Branch navigation and compaction │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ orchestrates
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Level 2: Agent (State Management & Event Streaming) │
|
|
│ - Conversation state (messages, tools, system prompt) │
|
|
│ - Event streaming and lifecycle management │
|
|
│ - Steering and follow-up message queues │
|
|
│ - Abort handling │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ delegates to
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Level 3: AgentLoop (Core LLM Interaction Loop) │
|
|
│ - Stateful LLM interactions │
|
|
│ - Tool execution (parallel or sequential) │
|
|
│ - Event emission lifecycle │
|
|
│ - Steering/follow-up message handling │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ transforms to
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Level 4: Session (Conversation History Management) │
|
|
│ - Tree-based conversation history │
|
|
│ - Branch support with compaction │
|
|
│ - Message and metadata persistence │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Process Flow
|
|
|
|
### 1. Agent Lifecycle
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Agent Lifecycle │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
|
|
User Code
|
|
│
|
|
│ 1. Create Agent
|
|
▼
|
|
┌──────────────┐
|
|
│ Agent() │ ──► Initialize state, queues, listeners
|
|
└──────────────┘
|
|
│
|
|
│ 2. Subscribe to events
|
|
▼
|
|
┌──────────────────┐
|
|
│ subscribe() │ ──► Register event handlers
|
|
└──────────────────┘
|
|
│
|
|
│ 3. Run prompt
|
|
▼
|
|
┌──────────────────┐
|
|
│ prompt() │ ──► Validate input, normalize messages
|
|
└──────────────────┘
|
|
│
|
|
│ 4. Start AgentLoop
|
|
▼
|
|
┌──────────────────┐
|
|
│ runPromptMessages│ ──► Create ActiveRun, spawn loop
|
|
└──────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ AgentLoop (runs in separate thread) │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────┐ │
|
|
│ │ 1. Emit AgentStartEvent │ │
|
|
│ │ 2. Emit TurnStartEvent │ │
|
|
│ │ 3. Process prompts (emit MessageStart/End) │ │
|
|
│ │ 4.┌────────────────────────────────────────────────┐ │ │
|
|
│ │ │ while true: │ │ │
|
|
│ │ │ │ Process steering/follow-up messages │ │ │
|
|
│ │ │ │ Stream assistant response (LLM call) │ │ │
|
|
│ │ │ │ Execute tool calls (parallel/sequential) │ │ │
|
|
│ │ │ │ Emit TurnEndEvent │ │ │
|
|
│ │ │ │ Check if should stop │ │ │
|
|
│ │ │ │ Get next steering messages │ │ │
|
|
│ │ └───┴────────────────────────────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ 5. Event streaming
|
|
▼
|
|
┌──────────────────┐
|
|
│ Event Handlers │ ──► User-defined listeners receive events
|
|
└──────────────────┘
|
|
│
|
|
│ 6. Wait for completion
|
|
▼
|
|
┌──────────────────┐
|
|
│ waitForIdle() │ ──► Resolve when all events processed
|
|
└──────────────────┘
|
|
```
|
|
|
|
### 2. AgentLoop Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ AgentLoop Process Flow │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌────────────────────────────────────────────────────────────────────┐
|
|
│ AgentLoop Entrypoint │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ agentLoop(prompts, context, config, signal, stream_fn) │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ runAgentLoop(prompts, context, config, emit, signal) │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ runLoop() - Main Event Loop │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
└──────────────────────────────┼─────────────────────────────────────┘
|
|
│
|
|
│ Loop Iteration
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────────┐
|
|
│ Main Processing Loop │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 1. Get Steering/Follow-up Messages │ │
|
|
│ │ ┌────────────────────┐ ┌──────────────────────┐ │ │
|
|
│ │ │ steering_queue │ │ follow_up_queue │ │ │
|
|
│ │ │ (after assistant) │ │ (after stop) │ │ │
|
|
│ │ └────────────────────┘ └──────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 2. Stream Assistant Response │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ transform_context() │ │ │
|
|
│ │ │ convert_to_llm(messages) -> Message[] │ │ │
|
|
│ │ │ stream_fn(model, context, config) -> Response │ │ │
|
|
│ │ │ - Text deltas │ │ │
|
|
│ │ │ - Tool call deltas │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Emit: MessageStartEvent, MessageUpdateEvent, │ │ │
|
|
│ │ │ MessageEndEvent │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 3. Execute Tool Calls │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ extract ToolCall from assistant content │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ if EXECUTION_SEQUENTIAL || has_sequential_tool: │ │ │
|
|
│ │ │ executeToolCallsSequential() │ │ │
|
|
│ │ │ else: │ │ │
|
|
│ │ │ executeToolCallsParallel() │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ For each tool call: │ │ │
|
|
│ │ │ 1. before_tool_call hook │ │ │
|
|
│ │ │ 2. prepareToolCall() │ │ │
|
|
│ │ │ 3. execute() │ │ │
|
|
│ │ │ 4. after_tool_call hook │ │ │
|
|
│ │ │ 5. Emit ToolExecutionStart/Update/EndEvent │ │ │
|
|
│ │ │ 6. Emit ToolResultMessage │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 4. Prepare Next Turn │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ prepare_next_turn(context) -> next_turn_snapshot │ │ │
|
|
│ │ │ - Optional: Update model/thinking_level │ │ │
|
|
│ │ │ - Optional: Update context │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 5. Check Termination Conditions │ │
|
|
│ │ ┌────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ should_stop_after_turn(context) -> bool │ │ │
|
|
│ │ │ - Max turns reached? │ │ │
|
|
│ │ │ - Tool returned terminate=true? │ │ │
|
|
│ │ │ - Steering queue empty and follow-up empty? │ │ │
|
|
│ │ └────────────────────────────────────────────────────┘ │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ 6. Emit TurnEndEvent (message, tool_results) │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
│ │ Loop continues until termination condition met │ │
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
└──────────────────────────────┼─────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────────┐
|
|
│ AgentEndEvent with final messages │
|
|
└────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 3. Tool Execution Flow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Tool Execution Flow │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ Assistant Message with Tool Calls │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ AssistantMessage: │ │
|
|
│ │ content: [ │ │
|
|
│ │ TextContent("I'll help you"), │ │
|
|
│ │ ToolCall(id="tc1", name="bash", args={...}), │ │
|
|
│ │ ToolCall(id="tc2", name="read", args={...}) │ │
|
|
│ │ ] │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
└───────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ executeToolCalls()
|
|
▼
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ Determine Execution Mode │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ config.tool_execution == EXECUTION_SEQUENTIAL? │ │
|
|
│ │ OR any tool has execution_mode == EXECUTION_SEQUENTIAL? │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────┴───────────────┐ │
|
|
│ ▼ ▼ │
|
|
│ ┌────────────────────────┐ ┌────────────────────────┐ │
|
|
│ │ executeSequential() │ │ executeParallel() │ │
|
|
│ └────────────────────────┘ └────────────────────────┘ │
|
|
│ │ │ │
|
|
└──────────────┼───────────────────────────────┼────────────────────┘
|
|
│ │
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────────────┐ ┌──────────────────────┐
|
|
│ Sequential Execution │ │ Parallel Execution │
|
|
│ │ │ │
|
|
│ for tool_call in: │ │ for tool_call in: │
|
|
│ prepareToolCall() │ │ prepareToolCall() │
|
|
│ execute() │ │ execute() (async) │
|
|
│ finalize() │ │ │
|
|
│ │ │ wait all results │
|
|
│ │ └──────────────────────┘
|
|
└──────────────────────┘
|
|
│
|
|
▼
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ For Each Tool Call │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ 1. before_tool_call hook (optional) │ │
|
|
│ │ - Can block execution │ │
|
|
│ │ 2. prepareToolCall() │ │
|
|
│ │ - validateToolArguments() │ │
|
|
│ │ - prepareToolCallArguments() (optional) │ │
|
|
│ │ 3. Execute Tool: │ │
|
|
│ │ tool.execute(tool_call_id, args, signal, on_update) │ │
|
|
│ │ 4. after_tool_call hook (optional) │ │
|
|
│ │ - Can modify result content │ │
|
|
│ │ 5. Emit events: │ │
|
|
│ │ - ToolExecutionStartEvent │ │
|
|
│ │ - ToolExecutionUpdateEvent (optional) │ │
|
|
│ │ - ToolExecutionEndEvent │ │
|
|
│ │ 6. Create ToolResultMessage │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
└───────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ Tool Result Messages │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ ToolResultMessage: │ │
|
|
│ │ role: "toolResult" │ │
|
|
│ │ tool_call_id: "tc1" │ │
|
|
│ │ tool_name: "bash" │ │
|
|
│ │ content: [TextContent("command output")] │ │
|
|
│ │ is_error: false │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
└───────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 4. Session & Tree Structure
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Session Tree Structure │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
|
|
Session = Linked List of Entries (tree structure)
|
|
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ Branch Navigation │
|
|
│ │
|
|
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
|
│ │ E1 │────▶│ E2 │────▶│ E3 │────▶│ E4 │────▶│ E5 │ (leaf) │
|
|
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
|
|
│ │ │ │ │ │ │
|
|
│ ▼ ▼ ▼ ▼ ▼ │
|
|
│ Message Message Compaction Message BranchSummary │
|
|
│ │
|
|
│ E3 is a Compaction Entry: │
|
|
│ - Summary of E1, E2 │
|
|
│ - first_kept_entry_id: reference to first retained message │
|
|
│ - tokens_before: context size before compaction │
|
|
│ │
|
|
│ E5 is a BranchSummary Entry: │
|
|
│ - Summary of branch from from_id │
|
|
│ - Represents a fork point in conversation history │
|
|
│ │
|
|
└───────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ Session.moveTo()
|
|
▼
|
|
┌───────────────────────────────────────────────────────────────────┐
|
|
│ Forking & Branching │
|
|
│ │
|
|
│ Current branch: │
|
|
│ ┌─────┐ ┌─────┐ ┌─────┐ │
|
|
│ │ E1 │────▶│ E2 │────▶│ E3 │ │
|
|
│ └─────┘ └─────┘ └─────┘ │
|
|
│ │ │
|
|
│ │ moveTo(E2) │
|
|
│ ▼ │
|
|
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
|
│ │ E1 │────▶│ E2 │────▶│ E3' │────▶│ E4' │ (new branch) │
|
|
│ └─────┘ └─────┘ └─────┘ └─────┘ │
|
|
│ │ │
|
|
│ │ create BranchSummary │
|
|
│ ▼ │
|
|
│ ┌─────┐ │
|
|
│ │ E5 │ (branch summary) │
|
|
│ └─────┘ │
|
|
│ │
|
|
└───────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Component Relationships
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Component Relationships │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
User Code
|
|
│
|
|
├── Creates ──► Agent
|
|
│ │
|
|
│ ├── Uses ──► AgentLoop
|
|
│ │ │
|
|
│ │ ├── Uses ──► StreamFn (LLM API)
|
|
│ │ │
|
|
│ │ └── Uses ──► Session
|
|
│ │
|
|
│ ├── Manages ──► AgentState
|
|
│ │
|
|
│ ├── Queues ──► SteeringQueue
|
|
│ │
|
|
│ └── Queues ──► FollowUpQueue
|
|
│
|
|
└── Interacts With ──► AgentHarness (optional, higher level)
|
|
│
|
|
├── Manages ──► SessionRepo
|
|
│
|
|
├── Manages ──► Skills
|
|
│
|
|
└── Manages ──► PromptTemplates
|
|
```
|
|
|
|
## Data Flow with Type Transformations
|
|
|
|
### Complete User Input → Conversation History Flow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ 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 │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ transform_context() (optional hook) │
|
|
│ │
|
|
│ Input: Vector{AgentMessage} │
|
|
│ Output: Vector{AgentMessage} (transformed) │
|
|
│ - Can truncate, filter, or modify messages │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ convertToLlm() - Type Transformation Pipeline │
|
|
│ │
|
|
│ 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) │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ Context for LLM API │
|
|
│ - system_prompt: String │
|
|
│ - messages: Vector{Message} │
|
|
│ - tools: Vector{AgentTool} │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ LLM API Call (stream_fn) │
|
|
│ │
|
|
│ Input: model, context, config │
|
|
│ Output: Stream{AssistantMessageEvent} │
|
|
│ • StartEvent: partial AssistantMessage │
|
|
│ • TextStartEvent/TextDeltaEvent/TextEndEvent │
|
|
│ • ToolCallStartEvent/ToolCallDeltaEvent/ToolCallEndEvent │
|
|
│ • DoneEvent: final AssistantMessage with usage │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ 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)
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ executeToolCalls() - Tool Processing │
|
|
│ │
|
|
│ Extract: filter(c -> c isa ToolCall, assistant.content) │
|
|
│ Output: ExecutedToolCallBatch │
|
|
│ • messages: Vector{ToolResultMessage} │
|
|
│ • terminate: Bool │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ ToolResultMessage (for each ToolCall) │
|
|
│ │
|
|
│ • 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)
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ Updated AgentState.messages (AgentMessage[]) │
|
|
│ │
|
|
│ Conversation History: │
|
|
│ [UserMessage, AssistantMessage, ToolResultMessage, ...] │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Level 3: Session Storage (optional, for persistence) │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
AgentState.messages (Vector{AgentMessage})
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ 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) │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ Persisted Data (JSON format) │
|
|
│ - Each entry has: id, parent_id, timestamp, type │
|
|
│ - MessageEntry contains full AgentMessage │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 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
|
|
|
|
The AgentCore.jl architecture follows a clean separation of concerns:
|
|
|
|
1. **AgentHarness** - Highest level, handles persistence and resources
|
|
2. **Agent** - State management and event streaming
|
|
3. **AgentLoop** - Core LLM interaction loop
|
|
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.
|