# AgentCore.jl - Learning Guide ## How to Use This Documentation ### Top-Down Learning Approach This documentation is organized in a **top-down** order, starting from high-level concepts and drilling down into implementation details. Follow this sequence: 1. **Architecture Overview** - Understand the big picture 2. **Agent Component** - Learn about state management and event streaming 3. **AgentLoop Component** - Understand the core LLM interaction loop 4. **Types & Messages** - Learn the data structures 5. **Session Management** - Understand conversation history 6. **Tools** - Learn about tool execution ### Learning Style - **Visual learners**: Study the ASCII diagrams - **Hands-on learners**: Code examples provided for each section - **Conceptual learners**: Read summaries and overviews first ## Quick Start ### Minimal Example ```julia using AgentCore # Create agent agent = Agent(Dict( :systemPrompt => "You are a helpful assistant.", :model => Model(...), :tools => [bash_tool], )) # Run conversation prompt(agent, "Hello!") # Wait for completion wait_for_idle(agent) ``` ### Understanding the Flow ``` User Code │ ├─► Create Agent │ ├─ Initialize state │ ├─ Set up queues │ └─ Register hooks │ ├─► prompt("Hello") │ ├─ Validate input │ └─ Start AgentLoop │ ├─► AgentLoop (runs in thread) │ ├─ Stream LLM response │ ├─ Execute tools │ └─ Emit events │ └─► Event handlers receive events ├─ MessageEndEvent ├─ ToolExecutionEndEvent └─ AgentEndEvent ``` ## Core Concepts ### Agent **What it is**: High-level interface for LLM interactions **What it does**: - Manages conversation state - Handles event streaming - Queues steering/follow-up messages - Provides hooks for customization **Key methods**: - `prompt()` - Start new conversation - `continue!()` - Continue existing conversation - `steer()` - Queue message for next turn - `followUp()` - Queue message after stop - `subscribe()` - Listen to events ### AgentLoop **What it is**: Core LLM interaction loop **What it does**: - Calls LLM API with streaming - Executes tool calls (parallel or sequential) - Emits lifecycle events - Handles steering/follow-up messages **Key functions**: - `agentLoop()` - Start new conversation - `agentLoopContinue()` - Continue conversation - `runAgentLoop()` - Internal loop execution - `streamAssistantResponse()` - LLM API call - `executeToolCalls()` - Tool execution ### Session **What it is**: Conversation history management **What it does**: - Persists messages to storage - Supports branching - Implements compaction - Manages conversation tree **Key methods**: - `appendMessage()` - Add message - `appendCompaction()` - Compress history - `moveTo()` - Navigate branches - `buildSessionContext()` - Build context for LLM ### Tools **What it is**: Functions agents can call **What they do**: - Execute external operations - Return results to agent - Support streaming updates - Implement hooks **Built-in tools**: - `bash` - Execute shell commands - `read` - Read files - `write` - Write files - `edit` - Edit files ## Event System ### Event Types ``` AgentEvent ├─ AgentStartEvent / AgentEndEvent ├─ TurnStartEvent / TurnEndEvent ├─ MessageStartEvent / MessageEndEvent ├─ MessageUpdateEvent ├─ ToolExecutionStartEvent / ToolExecutionEndEvent └─ ToolExecutionUpdateEvent ``` ### Event Flow ``` AgentStartEvent │ ├─ TurnStartEvent │ ├─ MessageStartEvent (user) │ ├─ MessageEndEvent (user) │ ├─ MessageStartEvent (assistant) │ ├─ MessageUpdateEvent (streaming) │ ├─ MessageEndEvent (assistant) │ ├─ ToolExecutionStartEvent │ ├─ ToolExecutionEndEvent │ └─ TurnEndEvent │ └─ AgentEndEvent ``` ## Data Flow ### Message Transformation ``` AgentMessage[] (internal) │ ├─ transform_context() (optional) ▼ AgentMessage[] (transformed) │ ├─ convert_to_llm() ▼ Message[] (LLM API) ``` ### Tool Execution Flow ``` ToolCall (in assistant message) │ ├─ before_tool_call hook ├─ prepareToolCall() ├─ execute() ├─ after_tool_call hook └─ createToolResultMessage() ``` ## Best Practices ### 1. Use Hooks for Customization ```julia # Before tool call before_hook = (context, signal) -> begin println("Executing: $(context.tool_call.name)") return nothing end # After tool call after_hook = (context, signal) -> begin if context.is_error println("Tool failed: $(context.tool_call.name)") end return nothing end ``` ### 2. Monitor Events ```julia subscribe(agent) do event, signal if event isa MessageEndEvent println("Message: $(event.message)") elseif event isa ToolExecutionEndEvent println("Tool completed: $(event.tool_name)") end end ``` ### 3. Use Steering for Redirection ```julia # Agent is going wrong direction steer(agent, UserMessage("Actually, let's do X instead")) ``` ### 4. Use Follow-Up for Continuation ```julia # Agent thinks it's done, but user wants more followUp(agent, UserMessage("Wait, there's one more thing")) ``` ## Common Patterns ### Pattern 1: Conversation with Memory ```julia # Use Session to persist conversation storage = JsonlSessionStorage(...) session = Session(storage) # Add messages to session appendMessage(session, user_message) appendMessage(session, assistant_message) # Build context from session context = buildSessionContext(session) ``` ### Pattern 2: Long Conversations ```julia # Compact periodically to stay within context limits if token_count > MAX_TOKENS * 0.8 compact_id = appendCompaction( session, summary, first_kept_id, token_count, ) end ``` ### Pattern 3: Branching Conversations ```julia # User wants to explore alternative session.moveTo(branch_point_id) # Create new branch appendBranchSummary(session, "Exploring alternative approach") appendMessage(session, new_user_message) ``` ### Pattern 4: Custom Tools ```julia # Create custom tool custom_tool = AgentTool( "custom", "custom", "Does custom thing", ..., execute_function, nothing, EXECUTION_PARALLEL, ) # Add to agent agent = Agent(Dict(:tools => [custom_tool])) ``` ## Debugging ### Check Active Run ```julia if !isnothing(agent.active_run) println("Agent is busy") else println("Agent is idle") end ``` ### Clear Queues ```julia clearAllQueues(agent) ``` ### Reset State ```julia reset!(agent) ``` ## Performance Tips 1. **Use parallel execution** for independent tools 2. **Compact periodically** for long conversations 3. **Use thinking_level wisely** (higher = slower but better) 4. **Batch tool calls** when possible 5. **Cache LLM responses** when appropriate ## Troubleshooting ### Agent stuck in loop ```julia # Check if agent is still processing if hasQueuedMessages(agent) # Clear queues clearAllQueues(agent) end ``` ### Too many tokens ```julia # Compact session compact_id = appendCompaction( session, summary, first_kept_id, token_count, ) ``` ### Tool execution failed ```julia # Check tool result if result.is_error println("Tool failed: $(result.error)") end ``` ## Next Steps 1. Read **Architecture Overview** for deep understanding 2. Explore **Agent Component** for state management 3. Study **AgentLoop** for core logic 4. Learn **Types & Messages** for data structures 5. Master **Session Management** for persistence 6. Build **Tools** for custom functionality ## Resources - Original TypeScript implementation: `@earendil-works/pi-agent-core` - AgentCore.jl source code: `src/` - Examples: `examples/` ## Community For questions and discussions: - GitHub Issues: `/issues` - Documentation: `docs/`