# Agent

The `Agent` class is the foundation for creating custom agents in Ceylon AI. It provides a base class that you can extend to implement your own agent logic with support for message handling, action registration, and context management.

## Class Signature

```python
class Agent(PyAgent):
    def __init__(self, name: str = "agent") -> None:
        ...
```

## Description

`Agent` is a Python wrapper around `PyAgent` that simplifies agent creation and provides a fluent API for registering actions and handling messages. It supports both synchronous and asynchronous message handlers.

## Constructor

### `__init__(name: str = "agent")`

Creates a new Agent instance.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `name` | `str` | `"agent"` | The name identifier for the agent |

**Returns:** Agent instance

**Example:**
```python
from ceylonai_next import Agent

# Create an agent with default name
agent = Agent()

# Create an agent with custom name
agent = Agent("my-assistant")
```

## Methods

### `name() -> str`

Returns the name of the agent.

**Parameters:** None

**Returns:** `str` - The agent's name

**Example:**
```python
agent = Agent("my-assistant")
print(agent.name())  # Output: my-assistant
```

---

### `action(name: str | None = None, description: str = "") -> Callable`

Decorator to register a function as an action that the agent can execute.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `name` | `str \| None` | `None` | Custom action name (uses function name if not provided) |
| `description` | `str` | `""` | Human-readable description of the action |

**Returns:** `Callable` - The decorated function

**Example:**
```python
from ceylonai_next import Agent

agent = Agent("calculator")

@agent.action(name="add", description="Add two numbers together")
def add_numbers(a: int, b: int) -> int:
    return a + b

@agent.action(description="Multiply two numbers")
def multiply(x: float, y: float) -> float:
    """Multiply two numbers"""
    return x * y
```

---

### `on_message(message: str | bytes, context: PyAgentContext | None = None) -> str | None`

Handle incoming messages. Override this method in subclasses to implement custom message processing logic.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `message` | `str \| bytes` | Required | The incoming message content |
| `context` | `PyAgentContext \| None` | `None` | Optional agent context for message processing |

**Returns:** `str | None` - Response string or None

**Notes:**
- This method can be synchronous or asynchronous (use `async def`)
- The response is automatically stored in `_last_response`

**Example:**
```python
from ceylonai_next import Agent

class EchoAgent(Agent):
    def on_message(self, message, context=None):
        # Process synchronously
        processed = str(message).upper()
        return f"Echo: {processed}"

class AsyncAgent(Agent):
    async def on_message(self, message, context=None):
        # Process asynchronously
        await some_async_operation(message)
        return f"Processed: {message}"

# Usage
echo = EchoAgent("echo")
response = echo.send_message("hello")  # Returns: "Echo: HELLO"
```

---

### `send_message(message: str) -> str`

Send a message to the agent and receive a response.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `message` | `str` | The message to send to the agent |

**Returns:** `str` - The agent's response

**Raises:**
- `RuntimeError` - If the agent's `on_message` handler is not properly implemented

**Example:**
```python
from ceylonai_next import Agent

class ReplyAgent(Agent):
    def on_message(self, message, context=None):
        return f"Got it: {message}"

agent = ReplyAgent("replier")
response = agent.send_message("Hello")
print(response)  # Output: Got it: Hello
```

---

### `last_response() -> str | None`

Retrieve the last response generated by the agent.

**Parameters:** None

**Returns:** `str | None` - The last response, or None if no messages have been sent

**Example:**
```python
from ceylonai_next import Agent

class MyAgent(Agent):
    def on_message(self, message, context=None):
        return f"Response to: {message}"

agent = MyAgent()

# Send first message
agent.send_message("First message")
print(agent.last_response())  # Output: Response to: First message

# Send second message
agent.send_message("Second message")
print(agent.last_response())  # Output: Response to: Second message
```

---

## Complete Examples

### Example 1: Simple Echo Agent

```python
from ceylonai_next import Agent, PyLocalMesh

class EchoAgent(Agent):
    """Simple agent that echoes messages back"""

    def __init__(self, name):
        super().__init__(name)
        self.message_count = 0

    def on_message(self, message, context=None):
        self.message_count += 1
        return f"[{self.message_count}] Echo: {message}"

# Create and use agent
agent = EchoAgent("echo-bot")
print(agent.send_message("Hello"))  # Output: [1] Echo: Hello
print(agent.send_message("World"))  # Output: [2] Echo: World
print(agent.last_response())         # Output: [2] Echo: World
```

### Example 2: Agent with Actions

```python
from ceylonai_next import Agent
import json

class CalculatorAgent(Agent):
    """Agent with calculation actions"""

    def __init__(self):
        super().__init__("calculator")

    @Agent.action(description="Add two numbers")
    def add(self, a: int, b: int) -> int:
        return a + b

    @Agent.action(description="Subtract two numbers")
    def subtract(self, a: int, b: int) -> int:
        return a - b

    def on_message(self, message, context=None):
        return f"Calculator ready to process: {message}"

# Create and use
agent = CalculatorAgent()
response = agent.send_message("Please add 5 and 3")
print(response)  # Output: Calculator ready to process: Please add 5 and 3
```

### Example 3: Agent in a Mesh

```python
from ceylonai_next import Agent, PyLocalMesh
import time

class SimpleAgent(Agent):
    def __init__(self, name):
        super().__init__(name)
        self.received = []

    def on_message(self, message, context=None):
        self.received.append(message)
        return f"Received and logged: {message}"

# Create mesh and agent
mesh = PyLocalMesh("demo_mesh")
agent = SimpleAgent("worker")
mesh.add_agent(agent)
mesh.start()

# Send messages via mesh
mesh.send_to("worker", "Task 1")
mesh.send_to("worker", "Task 2")
time.sleep(0.5)

print(f"Processed: {agent.received}")  # Output: Processed: ['Task 1', 'Task 2']
```

### Example 4: Agent with State Management

```python
from ceylonai_next import Agent
from typing import Dict, Any

class StatefulAgent(Agent):
    """Agent that maintains conversation state"""

    def __init__(self, name):
        super().__init__(name)
        self.state: Dict[str, Any] = {
            "messages": [],
            "context": {}
        }

    def on_message(self, message, context=None):
        # Store message
        self.state["messages"].append(message)

        # Process based on message type
        if message.startswith("SET:"):
            key, value = message.replace("SET:", "").split("=")
            self.state["context"][key] = value
            return f"Set {key} = {value}"

        if message.startswith("GET:"):
            key = message.replace("GET:", "").strip()
            value = self.state["context"].get(key, "NOT FOUND")
            return f"{key} = {value}"

        return f"Message #{len(self.state['messages'])}: {message}"

# Usage
agent = StatefulAgent("stateful")
print(agent.send_message("SET:username=alice"))  # Output: Set username = alice
print(agent.send_message("GET:username"))         # Output: username = alice
print(agent.last_response())                      # Output: username = alice
```

## Related APIs

- **[LlmAgent](./llm-agent.md)** - LLM-powered agent with integrated language model support
- **[PyAction](../actions/action.md)** - Action class for defining agent capabilities
- **[LocalMesh](./mesh.md)** - Multi-agent mesh for inter-agent communication
- **[PyLocalMesh](./mesh.md)** - Rust-backed local mesh implementation

## See Also

- [Agent Message Handling Guide](../../guide/agent-messages.md)
- [Creating Custom Actions](../../guide/actions.md)
- [Multi-Agent Mesh Communication](../../guide/mesh.md)
- [Complete Agent Example](../../examples/simple-agent.md)
