# Actor YAML Schema Reference

## Overview

Actors are the v3 replacement for v2's reactive stream configurations, providing a simpler, more declarative way to define AI agent behaviors. An actor defines what an agent can do, how it processes information, and how it interacts with tools and other actors.

**Module:** `cleveragents.actor.schema`  
**Schema Version:** 1.0

## Table of Contents

- [Actor Types](#actor-types)
- [Field Definitions](#field-definitions)
- [Tool Node Semantics](#tool-node-semantics)
- [Graph Constraints](#graph-constraints)
- [Validation Rules](#validation-rules)
- [Error Messages](#error-messages)
- [Examples](#examples)

---

## Actor Types

Actors come in three types, each serving different purposes:

### LLM Actor

**Type:** `llm`  
**Purpose:** Single LLM agent with system prompt and optional tools  
**Use Case:** Simple conversational agents, code reviewers, assistants

**Required Fields:**
- `name` - Namespaced actor name
- `type: llm`
- `model` - LLM model identifier (e.g., "gpt-4", "claude-3-opus")

**Optional Fields:**
- `system_prompt` - System prompt defining agent behavior
- `tools` - List of tool references or inline definitions
- `context_view` - Role-based context filtering
- `memory` - Conversation history settings
- `context` - File inclusion settings

**Example:**
```yaml
name: assistants/code_reviewer
type: llm
model: gpt-4
system_prompt: "You are an expert code reviewer..."
```

### TOOL Actor

**Type:** `tool`  
**Purpose:** Collection of callable tools without LLM interaction  
**Use Case:** Utility tool bundles, shared tool libraries

**Required Fields:**
- `name` - Namespaced actor name
- `type: tool`
- `tools` - At least one tool (reference or inline definition)

**Optional Fields:**
- None (tools-only actors don't need LLM configuration)

**Example:**
```yaml
name: utilities/file_ops
type: tool
tools:
  - files/read_file
  - files/write_file
```

### GRAPH Actor

**Type:** `graph`  
**Purpose:** Multi-node workflow with conditional routing  
**Use Case:** Complex workflows, multi-step processes, agentic loops

**Required Fields:**
- `name` - Namespaced actor name
- `type: graph`
- `model` - LLM model for agent nodes
- `route` - Complete graph topology definition

**Optional Fields:**
- `system_prompt` - Default prompt for graph-level agents
- `tools` - Tools available to all agent nodes
- `context_view` - Context filtering for the entire graph
- `memory` - Memory settings for the graph
- `context` - Context settings

**Example:**
```yaml
name: workflows/test_driven_dev
type: graph
model: gpt-4
route:
  nodes: [...]
  edges: [...]
  entry_node: planner
  exit_nodes: [review]
```

---

## Field Definitions

### Top-Level Fields

#### `name` (required)

**Type:** `str`  
**Format:** `namespace/actor_name`  
**Description:** Unique identifier for the actor in namespaced format

**Validation:**
- Must contain exactly one forward slash (`/`)
- Namespace and name must not be empty
- Should use snake_case for both parts

**Valid:**
- `assistants/code_reviewer`
- `workflows/test_driven_dev`
- `utilities/file_operations`

**Invalid:**
- `code_reviewer` (missing namespace)
- `assistants/code/reviewer` (too many slashes)
- `assistants/` (empty name)

#### `type` (required)

**Type:** `ActorType` enum  
**Values:** `llm`, `tool`, `graph`  
**Description:** Determines actor execution behavior and compilation target

#### `description` (required)

**Type:** `str`  
**Description:** Human-readable description of what the actor does

**Best Practices:**
- Start with a verb ("Reviews code...", "Processes documents...")
- Keep under 200 characters
- Be specific about the actor's purpose

#### `version` (optional)

**Type:** `str`  
**Default:** `"1.0"`  
**Description:** Schema version for backward compatibility

#### `model` (conditional)

**Type:** `str`  
**Required for:** `llm`, `graph` actors  
**Optional for:** `tool` actors (not used)  
**Description:** LLM model identifier

**Common Values:**
- OpenAI: `gpt-4`, `gpt-4-turbo`, `gpt-3.5-turbo`
- Anthropic: `claude-3-opus`, `claude-3-sonnet`, `claude-3-haiku`
- Open source: `mistral-large`, `llama-3-70b`

#### `system_prompt` (optional)

**Type:** `str`  
**Description:** System prompt defining agent behavior and guidelines

**Best Practices:**
- Use multi-line YAML strings (`|`) for readability
- Include role definition ("You are an expert...")
- List specific tasks and constraints
- Provide output format guidance

#### `tools` (optional/required)

**Type:** `list[str | ToolDefinition]`  
**Required for:** `tool` actors  
**Optional for:** `llm`, `graph` actors  
**Description:** Tools available to the actor

**Tool Reference Format:**
```yaml
tools:
  - namespace/tool_name  # Reference to existing tool
```

**Inline Tool Definition:**
```yaml
tools:
  - name: utils/count_lines
    description: Count lines in a file
    parameters:
      - name: file_path
        type: str
        required: true
    code: |
      def count_lines(file_path: str) -> int:
          with open(file_path, 'r') as f:
              return len(f.readlines())
```

---

### Tool Definition Fields

#### ToolParameter

**Fields:**

- `name` (str, required): Parameter name (must be valid Python identifier)
- `type` (str, required): Python type annotation (e.g., `str`, `int`, `list[str]`)
- `description` (str, required): Parameter description
- `required` (bool, default=True): Whether parameter is required
- `default` (any, optional): Default value for optional parameters

**Example:**
```yaml
parameters:
  - name: input_file
    type: str
    description: Path to input file
    required: true
  - name: encoding
    type: str
    description: File encoding
    required: false
    default: utf-8
```

#### ToolDefinition

**Fields:**

- `name` (str, required): Tool name in `namespace/name` format
- `description` (str, required): What the tool does
- `parameters` (list, optional): List of ToolParameter objects
- `code` (str, required): Python code implementing the tool

**Validation:**
- Tool name must follow `namespace/name` format
- Code must be valid Python
- Parameter names must be valid identifiers

---

### Memory Configuration

#### MemoryConfig

**Fields:**

- `enabled` (bool, default=True): Enable conversation memory
- `max_messages` (int, optional): Maximum messages to retain (None = unlimited)
- `max_tokens` (int, optional): Maximum tokens in history (None = unlimited)
- `summarize_old` (bool, default=False): Summarize old messages to save tokens

**Example:**
```yaml
memory:
  enabled: true
  max_messages: 50
  max_tokens: 4000
  summarize_old: false
```

**Use Cases:**
- Short conversations: `max_messages: 10-20`
- Medium conversations: `max_messages: 50`, `max_tokens: 4000-8000`
- Long workflows: `summarize_old: true`, `max_tokens: 16000`

---

### Context Configuration

#### ContextConfigSchema

**Fields:**

- `include_files` (list[str]): Specific files to include in context
- `include_dirs` (list[str]): Directories to include in context
- `exclude_patterns` (list[str]): Glob patterns to exclude
- `max_context_tokens` (int, optional): Maximum context window size

**Example:**
```yaml
context:
  include_files:
    - README.md
    - pyproject.toml
  include_dirs:
    - src/
    - tests/
  exclude_patterns:
    - "**/__pycache__/**"
    - "*.pyc"
    - "**/.git/**"
  max_context_tokens: 8000
```

#### ContextView

**Type:** enum  
**Values:** `strategist`, `executor`, `reviewer`, `full`  
**Description:** Role-based context filtering

**Context Views:**

- **strategist**: High-level planning view
  - Project structure
  - Goals and constraints
  - Architecture decisions
  - No implementation details

- **executor**: Implementation view
  - Source code
  - File contents
  - Specific tasks
  - Implementation details

- **reviewer**: Validation view
  - Changes and diffs
  - Test results
  - Code quality metrics
  - Review feedback

- **full**: Complete view (use sparingly)
  - All available context
  - Can exceed token limits
  - Only for critical decision points

---

## Tool Node Semantics

### Tool References

Tool references point to existing tools in the tool registry. Use references when:

- The tool is shared across multiple actors
- The tool is complex and tested separately
- The tool requires specific dependencies

**Format:** `namespace/tool_name`

**Example:**
```yaml
tools:
  - files/read_file
  - testing/run_pytest
  - analysis/extract_entities
```

### Inline Tool Definitions

Inline tools are defined directly in the actor YAML. Use inline tools when:

- The tool is actor-specific
- The tool is simple (< 50 lines of code)
- The tool doesn't require external dependencies

**Best Practices:**

1. Keep inline tools simple and focused
2. Include proper error handling
3. Add docstrings to the function
4. Use type hints for all parameters
5. Validate inputs before processing

**Example:**
```yaml
tools:
  - name: utils/count_words
    description: Count words in a text file
    parameters:
      - name: file_path
        type: str
        description: Path to text file
        required: true
    code: |
      def count_words(file_path: str) -> dict:
          """Count words in a file and return statistics."""
          try:
              with open(file_path, 'r', encoding='utf-8') as f:
                  content = f.read()
                  words = content.split()
                  return {
                      "word_count": len(words),
                      "char_count": len(content),
                      "line_count": content.count('\n') + 1
                  }
          except FileNotFoundError:
              return {"error": f"File not found: {file_path}"}
          except Exception as e:
              return {"error": str(e)}
```

---

## Graph Constraints

### Graph Topology

A graph actor's `route` field defines the complete workflow topology.

#### RouteDefinition

**Fields:**

- `nodes` (list[NodeDefinition], required): All nodes in the graph
- `edges` (list[EdgeDefinition], required): Connections between nodes
- `entry_node` (str, required): Starting node ID
- `exit_nodes` (list[str], required): Terminal node IDs

### Node Types

#### AGENT Node

**Type:** `agent`  
**Purpose:** LLM-powered decision making and content generation  
**Configuration:**

```yaml
- id: planner
  type: agent
  name: Task Planner
  description: Plans tasks based on requirements
  config:
    model: gpt-4  # Can override graph-level model
    prompt: "You are a task planning expert..."
    tools:
      - files/read_file
      - analysis/extract_requirements
```

#### TOOL Node

**Type:** `tool`  
**Purpose:** Execute specific tools or functions  
**Configuration:**

```yaml
- id: run_tests
  type: tool
  name: Test Runner
  description: Executes pytest test suite
  config:
    tool_name: testing/run_pytest
    parameters:
      verbose: true
      coverage: true
```

#### CONDITIONAL Node

**Type:** `conditional`  
**Purpose:** Route execution based on conditions  
**Configuration:**

```yaml
- id: check_results
  type: conditional
  name: Result Checker
  description: Routes based on test results
  config:
    conditions:
      - check: "state.get('tests_passed') == True"
        route_to: code_review
      - check: "state.get('tests_passed') == False"
        route_to: fix_errors
```

**Conditional Expressions:**

- Access state with `state.get('key')`
- Use Python comparison operators (`==`, `!=`, `<`, `>`, `<=`, `>=`)
- Combine with boolean operators (`and`, `or`, `not`)
- Conditions are evaluated in order; first match wins

#### SUBGRAPH Node

**Type:** `subgraph`  
**Purpose:** Embed another actor as a nested workflow  
**Configuration:**

```yaml
- id: code_review
  type: subgraph
  name: Code Reviewer
  description: Runs full code review workflow
  config:
    actor_path: actors/code_reviewer.yaml
```

**Subgraph Rules:**

- Subgraph actors can be of any type (llm, tool, graph)
- State is passed to and from the subgraph
- Subgraphs enable workflow composition and reuse
- Maximum nesting depth: 5 levels

### Edge Definition

**Fields:**

- `from_node` (str, required): Source node ID
- `to_node` (str, required): Target node ID
- `condition` (str, optional): Conditional routing expression
- `priority` (int, default=0): Edge priority (higher = evaluated first)

**Example:**
```yaml
edges:
  # Unconditional edge
  - from_node: planner
    to_node: executor

  # Conditional edge
  - from_node: executor
    to_node: retry
    condition: "state.get('retry_count', 0) < 3"
    priority: 10

  # Default edge (lower priority)
  - from_node: executor
    to_node: escalate
    priority: 1
```

---

## Validation Rules

### Graph Validation

The schema automatically validates graph topology when loading YAML files.

#### 1. Unique Node IDs

**Rule:** All node IDs must be unique within a graph.

**Valid:**
```yaml
nodes:
  - id: planner
  - id: executor
  - id: reviewer
```

**Invalid:**
```yaml
nodes:
  - id: planner
  - id: executor
  - id: planner  # ❌ Duplicate ID
```

**Error:**
```
ValueError: Duplicate node IDs found: {'planner'}
```

#### 2. Entry Node Exists

**Rule:** The `entry_node` must reference a valid node ID.

**Valid:**
```yaml
nodes:
  - id: start
  - id: process
entry_node: start  # ✅ 'start' exists
```

**Invalid:**
```yaml
nodes:
  - id: start
  - id: process
entry_node: init  # ❌ 'init' doesn't exist
```

**Error:**
```
ValueError: Entry node 'init' not found in nodes
```

#### 3. Exit Nodes Exist

**Rule:** All `exit_nodes` must reference valid node IDs.

**Valid:**
```yaml
nodes:
  - id: start
  - id: end1
  - id: end2
exit_nodes:
  - end1
  - end2
```

**Invalid:**
```yaml
nodes:
  - id: start
  - id: end1
exit_nodes:
  - end1
  - finish  # ❌ 'finish' doesn't exist
```

**Error:**
```
ValueError: Exit node 'finish' not found in nodes
```

#### 4. Edge References Valid

**Rule:** All edge `from_node` and `to_node` must reference valid node IDs.

**Valid:**
```yaml
nodes:
  - id: a
  - id: b
edges:
  - from_node: a
    to_node: b
```

**Invalid:**
```yaml
nodes:
  - id: a
  - id: b
edges:
  - from_node: a
    to_node: c  # ❌ 'c' doesn't exist
```

**Error:**
```
ValueError: Edge to_node 'c' not found in nodes
```

#### 5. No Cycles

**Rule:** Graphs must be acyclic (no circular dependencies).

**Valid (Linear):**
```yaml
edges:
  - from_node: a
    to_node: b
  - from_node: b
    to_node: c
```

**Valid (Branching):**
```yaml
edges:
  - from_node: a
    to_node: b
  - from_node: a
    to_node: c
  - from_node: b
    to_node: d
  - from_node: c
    to_node: d
```

**Invalid (Cycle):**
```yaml
edges:
  - from_node: a
    to_node: b
  - from_node: b
    to_node: c
  - from_node: c
    to_node: a  # ❌ Cycle: a → b → c → a
```

**Error:**
```
ValueError: Graph contains cycles involving nodes: ['a']
```

**Note:** Conditional edges with retry logic can appear cyclic but are validated at runtime.

#### 6. All Nodes Reachable

**Rule:** All nodes must be reachable from the entry node.

**Valid:**
```yaml
entry_node: start
edges:
  - from_node: start
    to_node: process
  - from_node: process
    to_node: end
```

**Invalid:**
```yaml
entry_node: start
nodes:
  - id: start
  - id: process
  - id: orphan  # ❌ Not reachable from 'start'
edges:
  - from_node: start
    to_node: process
```

**Error:**
```
ValueError: Unreachable nodes detected: {'orphan'}
```

---

## Error Messages

### Common Validation Errors

#### NameError: Invalid Actor Name

**Cause:** Actor name doesn't follow `namespace/name` format

**Example:**
```yaml
name: code_reviewer  # ❌ Missing namespace
```

**Error:**
```
ValueError: Actor name must be namespaced (namespace/name): code_reviewer
```

**Fix:**
```yaml
name: assistants/code_reviewer  # ✅
```

#### TypeError: Missing Required Field

**Cause:** LLM or GRAPH actor missing `model` field

**Example:**
```yaml
name: assistants/helper
type: llm
# ❌ Missing model field
```

**Error:**
```
ValueError: LLM actors require 'model' field
```

**Fix:**
```yaml
name: assistants/helper
type: llm
model: gpt-4  # ✅
```

#### ValueError: Empty Tools List

**Cause:** TOOL actor with no tools defined

**Example:**
```yaml
name: utilities/helpers
type: tool
tools: []  # ❌ Empty list
```

**Error:**
```
ValueError: TOOL actors require at least one tool
```

**Fix:**
```yaml
name: utilities/helpers
type: tool
tools:
  - files/read_file  # ✅
```

#### ValueError: Missing Route

**Cause:** GRAPH actor without route definition

**Example:**
```yaml
name: workflows/process
type: graph
model: gpt-4
# ❌ Missing route field
```

**Error:**
```
ValueError: GRAPH actors require 'route' field
```

**Fix:**
```yaml
name: workflows/process
type: graph
model: gpt-4
route:  # ✅
  nodes: [...]
  edges: [...]
  entry_node: start
  exit_nodes: [end]
```

---

## Examples

### Example 1: Simple LLM Actor

```yaml
name: assistants/code_reviewer
type: llm
description: Reviews Python code for best practices
version: "1.0"

model: gpt-4
system_prompt: |
  You are an expert Python code reviewer.
  Review code for:
  - PEP 8 compliance
  - Design patterns
  - Potential bugs
  - Performance issues

context_view: reviewer
memory:
  enabled: true
  max_messages: 20
```

### Example 2: LLM with Inline Tools

```yaml
name: assistants/file_analyzer
type: llm
description: Analyzes files and generates reports
version: "1.0"

model: gpt-4-turbo
system_prompt: "Analyze files and provide insights..."

tools:
  - files/read_file  # Tool reference

  - name: utils/count_lines  # Inline tool
    description: Count lines in a file
    parameters:
      - name: file_path
        type: str
        required: true
    code: |
      def count_lines(file_path: str) -> int:
          with open(file_path, 'r') as f:
              return len(f.readlines())
```

### Example 3: Simple Graph Workflow

```yaml
name: workflows/doc_processor
type: graph
description: Processes documents through multiple stages
version: "1.0"

model: gpt-3.5-turbo

route:
  nodes:
    - id: extract
      type: tool
      name: Text Extractor
      description: Extracts text from documents
      config:
        tool_name: documents/extract_text

    - id: analyze
      type: agent
      name: Analyzer
      description: Analyzes content
      config:
        model: gpt-3.5-turbo
        prompt: "Analyze the document content..."

    - id: summarize
      type: agent
      name: Summarizer
      description: Creates summary
      config:
        model: gpt-3.5-turbo
        prompt: "Summarize in 3-5 bullet points..."

  edges:
    - from_node: extract
      to_node: analyze
    - from_node: analyze
      to_node: summarize

  entry_node: extract
  exit_nodes:
    - summarize
```

### Example 4: Complex Graph with Conditionals

```yaml
name: workflows/ci_pipeline
type: graph
description: CI/CD pipeline with test and deploy stages
version: "1.0"

model: gpt-4

route:
  nodes:
    - id: build
      type: tool
      name: Builder
      description: Builds the project
      config:
        tool_name: ci/build

    - id: test
      type: tool
      name: Test Runner
      description: Runs test suite
      config:
        tool_name: ci/run_tests

    - id: check_tests
      type: conditional
      name: Test Result Checker
      description: Check if tests passed
      config:
        conditions:
          - check: "state.get('tests_passed') == True"
            route_to: deploy
          - check: "state.get('tests_passed') == False"
            route_to: notify_failure

    - id: deploy
      type: tool
      name: Deployer
      description: Deploys to production
      config:
        tool_name: ci/deploy

    - id: notify_failure
      type: tool
      name: Notifier
      description: Sends failure notification
      config:
        tool_name: notifications/send_alert

  edges:
    - from_node: build
      to_node: test
    - from_node: test
      to_node: check_tests
    # Conditional routing handled by check_tests node

  entry_node: build
  exit_nodes:
    - deploy
    - notify_failure
```

---

## Usage

### Loading Actor Configuration

```python
from cleveragents.actor.schema import ActorConfigSchema

# Load from YAML file
actor = ActorConfigSchema.from_yaml_file("actors/code_reviewer.yaml")

# Access fields
print(actor.name)  # assistants/code_reviewer
print(actor.type)  # ActorType.LLM
print(actor.model)  # gpt-4

# Validate graph (automatic on load)
if actor.type == ActorType.GRAPH:
    # Validation already done:
    # - All nodes have unique IDs
    # - Entry/exit nodes exist
    # - All edges reference valid nodes
    # - No cycles detected
    # - All nodes reachable
    pass
```

### Saving Actor Configuration

```python
# Create actor programmatically
actor = ActorConfigSchema(
    name="assistants/helper",
    type=ActorType.LLM,
    description="A helpful assistant",
    model="gpt-4",
    system_prompt="You are a helpful assistant."
)

# Save to YAML file
actor.to_yaml_file("actors/helper.yaml")
```

---

## See Also

- [ADR-010: Actor and Agent Architecture](../adr/ADR-010-actor-and-agent-architecture.md)
- [ADR-011: Tool System](../adr/ADR-011-tool-system.md)
- [Tool Schema Reference](../schema/tool.schema.yaml)
- [Action Schema Reference](../schema/action.schema.yaml)
