# AI Agent Architecture

## Overview

A modular, extensible AI agent system built around a core loop of **perceive → reason → act**. The agent receives input, plans using an LLM, executes tools, and returns results.

---

## High-Level Architecture

```
┌─────────────────────────────────────────────────────┐
│                    User / Caller                     │
└──────────────────────┬──────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────┐
│                   Agent Core                         │
│  ┌───────────┐  ┌───────────┐  ┌────────────────┐  │
│  │  Message   │  │  Agent    │  │  Conversation  │  │
│  │  Router    │→ │  Loop     │→ │  Memory        │  │
│  └───────────┘  └─────┬─────┘  └────────────────┘  │
│                       │                              │
│                       ▼                              │
│              ┌────────────────┐                      │
│              │  LLM Provider  │                      │
│              │  (Reasoning)   │                      │
│              └────────┬───────┘                      │
│                       │                              │
│                       ▼                              │
│              ┌────────────────┐                      │
│              │ Tool Executor  │                      │
│              └────────┬───────┘                      │
│                       │                              │
│         ┌─────────────┼─────────────┐               │
│         ▼             ▼             ▼               │
│    ┌─────────┐  ┌──────────┐  ┌──────────┐        │
│    │ Tool A  │  │ Tool B   │  │ Tool C   │        │
│    └─────────┘  └──────────┘  └──────────┘        │
└─────────────────────────────────────────────────────┘
```

---

## Core Components

### 1. Agent Core (`agent/core.py`)

The main orchestrator. Manages the agent loop lifecycle.

```
class Agent:
    - config: AgentConfig
    - llm: LLMProvider
    - tools: ToolRegistry
    - memory: ConversationMemory

    + run(input: str) -> AgentResult
    + step() -> StepResult
    + reset()
```

**Agent Loop (the heart of the system):**

```
while not done:
    1. Build prompt from system instructions + memory + user input
    2. Call LLM for next action (reason)
    3. If LLM returns final answer → return to user
    4. If LLM returns tool call → execute tool → append result to memory → loop
```

### 2. LLM Provider (`agent/llm.py`)

Abstraction over LLM APIs. Supports swapping models.

```
class LLMProvider (Protocol):
    + chat(messages: list[Message]) -> LLMResponse

class AnthropicProvider(LLMProvider):    # Claude API
class OpenAIProvider(LLMProvider):       # OpenAI API
class OllamaProvider(LLMProvider):       # Local models
```

**LLMResponse** contains either:
- `text`: a final text response
- `tool_calls`: list of `ToolCall(name, arguments)`

### 3. Tool System (`agent/tools/`)

Tools are the agent's capabilities — how it interacts with the world.

```
class Tool (Protocol):
    name: str
    description: str
    parameters: JSONSchema

    + execute(**kwargs) -> ToolResult
```

**Built-in tools to start with:**

| Tool | Purpose |
|------|---------|
| `web_search` | Search the internet |
| `read_file` | Read local files |
| `write_file` | Write/create files |
| `run_command` | Execute shell commands |
| `http_request` | Make HTTP API calls |

**Tool Registry:**
```
class ToolRegistry:
    + register(tool: Tool)
    + get(name: str) -> Tool
    + list() -> list[ToolSchema]   # for LLM prompt injection
    + execute(name: str, **kwargs) -> ToolResult
```

### 4. Conversation Memory (`agent/memory.py`)

Tracks the full message history for the current session.

```
class ConversationMemory:
    - messages: list[Message]

    + add_user(content: str)
    + add_assistant(content: str)
    + add_tool_result(tool_name: str, result: str)
    + get_messages() -> list[Message]
    + truncate(max_tokens: int)       # context window management
```

**Message types:**
- `system` — agent instructions & tool definitions
- `user` — user input
- `assistant` — LLM output (text or tool calls)
- `tool_result` — output from executed tools

### 5. Configuration (`agent/config.py`)

```
class AgentConfig:
    model: str              # e.g. "claude-sonnet-4-6"
    system_prompt: str      # agent persona & instructions
    max_steps: int          # loop safety limit (default: 20)
    max_tokens: int         # per-response token limit
    temperature: float      # 0.0 - 1.0
    tools: list[str]        # enabled tool names
```

---

## Project Structure

```
itagent/
├── agent/
│   ├── __init__.py
│   ├── core.py            # Agent class & agent loop
│   ├── config.py          # AgentConfig dataclass
│   ├── llm.py             # LLM provider abstraction
│   ├── memory.py          # Conversation memory
│   └── tools/
│       ├── __init__.py    # ToolRegistry
│       ├── base.py        # Tool protocol / base class
│       ├── web_search.py
│       ├── read_file.py
│       ├── write_file.py
│       ├── run_command.py
│       └── http_request.py
├── prompts/
│   └── system.txt         # Default system prompt
├── main.py                # CLI entrypoint
├── pyproject.toml
├── requirements.txt
└── ARCHITECTURE.md         # This file
```

---

## Data Flow

```
User Input
    │
    ▼
┌─────────────────────────────────────────────┐
│ 1. Build Messages                           │
│    [system_prompt + tool_schemas + history   │
│     + user_input]                            │
└──────────────────────┬──────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────┐
│ 2. LLM Call                                 │
│    Input:  messages                         │
│    Output: text OR tool_calls               │
└──────────────────────┬──────────────────────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
        [Final Text]      [Tool Calls]
              │                 │
              ▼                 ▼
        Return to user    Execute each tool
                                │
                                ▼
                          Append results
                          to memory
                                │
                                ▼
                          Go to Step 1
```

---

## Key Design Decisions

### Why a simple loop instead of a graph/DAG?
A linear ReAct loop (Reason → Act → Observe) covers 90% of agent use cases. It's easy to debug, trace, and extend. Graduate to a graph (like LangGraph) only when you need parallel branches or complex state machines.

### Why protocol-based tool definitions?
Tools are defined as simple classes with a `execute()` method and JSON schema for parameters. This makes it trivial to add new tools — just implement the protocol and register.

### Why no framework dependency (LangChain, etc.)?
Building from primitives gives full control over the agent loop, token management, error handling, and retry logic. Frameworks add abstraction layers that make debugging harder for a base agent.

---

## Extension Points

These are **not built now** — they are future directions once the base works.

| Extension | Description |
|-----------|-------------|
| **Persistent memory** | Store facts across sessions (vector DB or file-based) |
| **Multi-agent** | Orchestrator agent delegates to specialist sub-agents |
| **Streaming** | Stream LLM responses token-by-token to the user |
| **Guardrails** | Input/output validation, content filtering |
| **Observability** | Structured logging, tracing each agent step |
| **Auth & sandboxing** | Tool permission system, sandboxed execution |

---

## Getting Started (Build Order)

1. **`config.py`** — Define `AgentConfig` dataclass
2. **`llm.py`** — Implement one LLM provider (start with Anthropic)
3. **`tools/base.py`** — Define `Tool` protocol and `ToolRegistry`
4. **`memory.py`** — Implement `ConversationMemory`
5. **`core.py`** — Wire up the agent loop
6. **`main.py`** — CLI entrypoint to test interactively
7. **Add tools one by one** — `web_search`, `read_file`, etc.

---

## Tech Stack

| Component | Choice |
|-----------|--------|
| Language | Python 3.12+ |
| LLM SDK | `anthropic` (primary), `openai` (optional) |
| HTTP | `httpx` |
| CLI | `click` or `typer` |
| Config | `pydantic` for validation |
| Package manager | `uv` or `pip` |
