# YiemAgent: Agile Agent Framework ## 1. Business Context & Success Metrics ### Business Goal YiemAgent is a Julia-based AI agent framework that enables building chatbot assistants with tool-calling capabilities, model-agnostic LLM integration, and runtime tool discovery via MCP (Model Context Protocol). It provides an asynchronous agent loop with channel-based message passing, supporting both single-turn and multi-turn conversations with automatic tool call resolution. ### User Stories - **US-001**: As a developer, I want to create an AI agent with a simple constructor so that I can quickly prototype chatbot functionality in Julia - **US-002**: As a developer, I want the agent to discover tools automatically from an MCP server so that I don't need to manually register tool APIs - **US-003**: As a developer, I want the agent to handle tool calls automatically in a loop so that the LLM can use tools without external orchestration - **US-004**: As a developer, I want to intercept tool calls with before/after hooks so that I can add user approval, logging, or result sanitization - **US-005**: As a developer, I want the framework to support multiple LLM models and providers through a pluggable `llmCall` interface so that I can swap models without changing agent logic ### KPIs & Targets - **KPI-001**: Agent loop processes messages with <500ms overhead for sequential tool calls (validates US-003) - **KPI-002**: MCP tool discovery completes within 2 seconds for 50+ tools (validates US-002) - **KPI-003**: 100% of message types have OpenAI format conversion (validates US-001) ## 2. Technical Boundaries ### In Scope - Julia module implementing an AI agent with asynchronous message processing - Channel-based input (user→agent), follow-up (async queue), and output (agent→user) interfaces - LLM model abstraction with tool call extraction from multiple response formats (OpenAI content blocks, LMStudio/vLLM format) - Tool registry with MCP server integration (tools/list, tools/call via JSON-RPC 2.0) - Agent loop with before/after tool call hooks for lifecycle management - Message type system with OpenAI format conversion utilities - Abort signal and termination flag support via tool implementations ### Out of Scope - GUI/CLI interface (exposed via channels only; UI layer is external) - Persistent chat history or long-term memory (TODO comments in code) - Multi-agent orchestration (single agent instance per `yiemAgent` struct) - Embedding generation or retrieval-augmented generation (RAG) - Natural language-to-code generation or execution sandboxes ### Dependencies - **Julia Standard Library**: `Dates`, `UUIDs`, `Base.Threads` - **External Packages**: `JSON`, `DataStructures`, `HTTP`, `PrettyPrinting`, `Serialization`, `DataFrames` - **Internal Package**: `GeneralUtils` (project-specific utility library) - **Optional**: `NATS` (MCP server communication), `LibPQ` (PostgreSQL) ### Deployment Constraints - **NFR-001**: Library designed for embedded use in Docker containers (MicroK8s cluster, per `NFR-501` from template) - **NFR-002**: Must support Julia 1.9+ for module include/export semantics compatibility - **NFR-003**: Thread-safe channel operations; single-agent instance processes messages sequentially on background `@spawn` task ## 3. Functional Requirements (FR) ### FR-001: Agent Message Processing The agent shall process user messages through an asynchronous loop that: - Accepts messages via `inputChannel` (capacity 16) and `followUpChannel` (capacity 32) - Prioritizes `inputChannel` messages over `followUpChannel` messages - Feeds responses to `outputChannel` (capacity 16) in OpenAI message format - Exits cleanly on `:shutdown` signal **Traceability**: US-001, US-003 ### FR-002: LLM Model Agnosticism The agent shall accept a pluggable `llmCall` function that: - Receives formatted messages and returns an LLM response dict - The response dict may contain `message.tool_calls` (LMStudio format) or `content` blocks (OpenAI format) - The framework extracts tool calls regardless of response format **Traceability**: US-005 ### FR-003: Tool Call Execution Loop The agent shall automatically execute tool calls returned by the LLM: - Extract tool calls via `_extractToolCalls()` which supports both `message.tool_calls` and `content[{type="tool_calls"}]` formats - Build `agentContext` via `prepareContext()` (pluggable hook) - Format messages via `formatMsgForLLM()` to OpenAI format - Extract tool calls → loop: execute tool calls → append results to history → call LLM again - Break loop when LLM response has no tool calls - Support `terminate` flag: if `toolResultBatch.terminate == true`, break loop and return final response **Traceability**: US-003 ### FR-004: Tool Discovery and Registration The agent shall: - Query MCP server via `mcpserver("tools/list")` (JSON-RPC 2.0) to discover tools - Support pagination via `nextCursor` field - Wrap each MCP tool definition as an `agentTool` with `execute` hook that calls `mcpserver("tools/call", name, args, sessionId)` - Register a builtin `listTools` tool that triggers runtime discovery and lists all available tools - Skip tools already registered (idempotent) **Traceability**: US-002 ### FR-005: Tool Call Lifecycle Hooks The agent shall support two lifecycle hooks: - `beforeToolCall(beforeToolCallContext, signal)` — can block execution by returning `beforeToolCallResult(true, reason)` - `afterToolCall(afterToolCallContext)` — can transform result by returning modified `agentToolResult` - Errors from hooks are caught and converted to `immediateOutcome`/error tool result messages (never throw) **Traceability**: US-004 ### FR-006: Message Type Conversion The agent shall: - Convert internal message types (`userMessage`, `assistantMessage`, `assistantMessageToolCall`, `toolResultMessage`) to OpenAI format via `_userMessageToOpenAI()`, `_assistantMessageToOpenAI()`, `_assistantMessageToolCallToOpenAI()`, `_toolResultMessageToOpenAI()` - Convert internal `messageContent` types (`textContent`, `imageContent`, `reasoningContent`) to OpenAI content blocks via `_messageContentToBlocks()` - Convert `agentTool` definitions to OpenAI function tool format via `_toolsToOpenAI()` **Traceability**: US-001 ## 4. Non-Functional Requirements (NFRs) ### 4.1 Performance & Scalability - **NFR-101**: Agent loop message processing overhead <500ms per sequential tool call (measured KPI: KPI-001) - **NFR-102**: MCP tool discovery for 50+ tools completes within 2 seconds (measured KPI: KPI-002) - **NFR-103**: Channel buffers prevent unbounded memory growth — `inputChannel` (16), `followUpChannel` (32), `outputChannel` (16) ### 4.2 Availability & Reliability - **NFR-201**: Agent loop catches all errors via `try/catch` in `_agentLoop()` and calls `eventSink(errMsg)` before exit (measured by event sink logging) - **NFR-202**: `prepareToolCall()` never throws errors — validation failures return `immediateOutcome` with error tool result ### 4.3 Privacy & Security - **Data Classification**: Tool arguments and results may contain PII (user messages); MCP tool results treated as untrusted input - **No built-in auth/authz**: Responsibility of `mcpServer` implementation and `beforeToolCall` hook - **Input validation**: JSON schema validation via `validateToolArguments()` checks required fields only (tools need custom validation via `validateRequiredArgs` hook) ### 4.4 Observability & Telemetry - **Required Logs**: `agentLoop` events (via `eventSink`) emit state machine steps (`_agentLoop 1` through `_agentLoop 6`), `_processMessage` steps (1 through 18), `prepareToolCall` steps (1 through 10) - **Critical Metrics**: - `agent._state.messages.length` — conversation history size (logged at each step) - `toolExecutionStart`/`toolExecutionEnd` events (via `toolExecStartEvent`, `toolExecEndEvent` structs) - **Retention**: No built-in log retention; agent is a library, does not manage external logging ## 5. Acceptance Conditions - [ ] **FR-001**: Agent processes messages through channel system with priority ordering (inputChannel > followUpChannel) - [ ] **FR-002**: `llmCall` is pluggable; response formats supported include `message.tool_calls` dict and `content` array blocks - [ ] **FR-003**: Agent loops on tool calls until LLM returns no tool calls or terminate flag is set - [ ] **FR-004**: MCP tools are discovered via `tools/list` with pagination; all registered tools are callable via `tools/call` - [ ] **FR-005**: `beforeToolCall` hook can block; `afterToolCall` hook can transform result; errors return error tool result - [ ] **FR-006**: All message types (userMessage, assistantMessage, assistantMessageToolCall, toolResultMessage) convert to OpenAI format - [ ] **NFR-101**: Message processing overhead <500ms per sequential tool call - [ ] **NFR-102**: MCP discovery for 50+ tools completes <2 seconds - [ ] **NFR-201**: Agent loop catches errors and exits gracefully without panics