---
adr_number: 10
title: Actor and Agent Architecture
status_history:
- - '2026-02-16'
  - Proposed
  - Jeffrey Phillips Freeman
- - '2026-02-16'
  - Accepted
  - Jeffrey Phillips Freeman
tier: 2
authors:
- Jeffrey Phillips Freeman
superseded_by: null
related_adrs:
- number: 2
  title: Namespace System
  relationship: Actors and agents are identified via namespaced names
- number: 6
  title: Plan Lifecycle
  relationship: Actors drive plan phase transitions and execute plan steps
- number: 11
  title: Tool System
  relationship: Tools serve as atomic execution nodes within actor graphs
- number: 12
  title: Skill System
  relationship: Skills provide the tool collections that actors are equipped with
- number: 22
  title: LangChain/LangGraph Integration
  relationship: Actors are implemented as LangGraph StateGraph instances
- number: 30
  title: Skill Abstraction Definition
  relationship: Skills are the unit of capability assignment to actors
- number: 31
  title: Actor Abstraction Definition
  relationship: Formalizes the canonical definition of what an actor is
- number: 32
  title: Jinja2 YAML Template Preprocessing
  relationship: Defines the two-phase Jinja2 + env var preprocessing pipeline for actor YAML files
- number: 33
  title: Decision Recording Protocol
  relationship: Actors call `record_decision` tool during Strategize and Execute to record decisions in the tree
- number: 27
  title: Language Server Protocol (LSP) Integration
  relationship: Actors acquire language intelligence through LSP servers bound via the `lsp` configuration field
acceptance:
  votes_for:
  - voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
    comment: Actor-as-graph with hierarchical composition gives us flexible orchestration without tight coupling
  votes_against: []
  abstentions: []
---
## Context

CleverAgents must orchestrate multiple AI-driven roles — strategy planning, code execution, cost estimation, invariant reconciliation, code review, and more. Some of these roles are single LLM agents; others are complex multi-step workflows involving multiple LLMs, deterministic tool nodes, and sub-graphs. The system needs an abstraction that unifies all of these behind a common interface, supports hierarchical composition, and is defined declaratively via configuration rather than code.

## Decision Drivers

- Must orchestrate multiple AI-driven roles (strategy planning, code execution, cost estimation, invariant reconciliation, code review) under a single abstraction
- Some roles are single LLM agents; others are complex multi-step workflows with multiple LLMs, deterministic tool nodes, and sub-graphs
- Actors must be defined declaratively via YAML configuration, not code, to enable non-developer customization
- Need hierarchical composition so complex behaviors are built from simple, reusable named components
- Must support namespaced identification and resolution of actors across deployment boundaries
- The "agent" concept (LLM with tools and reasoning) must be preserved as a specialization, not the only orchestration primitive

## Decision

CleverAgents uses the **actor** as its fundamental orchestration abstraction. An actor generalizes "agent" into "anything conversational." Every custom actor is a **LangGraph graph** defined via YAML configuration — even a simple single-LLM actor is a graph with one node. Actors are namespaced, composable, and referenceable by name. An **agent** is a specialized actor with a conversational interface, tool-calling capability, and optionally memory and planning heuristics.

## Design

### Actor Definition

Actors are defined in YAML configuration files and managed via `agents actor` CLI commands. Key definition fields:

- **`name`**: Namespaced identifier (e.g., `local/code-reviewer`).
- **`provider`** and **`model`**: LLM provider and model for the actor's primary node.
- **`system_prompt`**: Jinja2 template for the actor's system instructions. Supports `{{ context.* }}` variable expansion.
- **`skill_access_policy`**: Controls which skills (and their tools) the actor can use.
- **`graph_descriptor`**: Defines the actor's internal graph — nodes, edges, conditional routing.
- **`memory_policy`**: Controls context retention between invocations.
- **`context_view_policy`**: ACMS settings — preferred strategies, default breadth/depth, depth gradient, skeleton budget ratio, temporal scope, auto-refresh.
- **`limits`**: Token limits, tool call limits per step, retry policies.
- **`cost_policy`**: Budget constraints and cost tracking.
- **`lsp`**: LSP server binding — explicit server names, language-based resolution, or auto-discovery from project resources. Provides language intelligence (diagnostics, type info, completions, references) to actor nodes.

### Actor as Graph

Every actor is a LangGraph `StateGraph`. The graph contains:

- **Actor nodes**: References to other actors by name, enabling hierarchical composition.
- **Tool nodes**: Deterministic, non-LLM steps that either reference a named registered tool or define an anonymous inline tool.
- **Edges**: Connections between nodes, including conditional edges for routing based on state.

Even a minimal single-LLM actor is expressed as a graph with one LLM node and tool edges. This uniformity means the system has exactly one execution model for all actors.

### Actor Arguments

All actors can receive arguments when invoked. Arguments are injected into the actor's context and are usable in Jinja2 templates within system prompts and node configurations.

### Actor Composition

Actors reference other actors **by name**. An actor graph node can point to another registered actor, enabling hierarchical composition:

- A strategy actor might delegate to a specialized resource-analysis actor.
- An execution actor might route to different implementation actors based on the programming language.
- A review actor might compose multiple specialized checkers (security, style, correctness).

Load order matters — referenced actors must be registered before the referencing actor.

### Actor vs. Agent

- **Agent**: An actor that is specifically an LLM with tools and reasoning behaviors. Has a conversational interface and tool-calling capability. May have memory, planning heuristics, and a role identity.
- **Actor**: May be an agent, or a composite workflow, a multi-step graph, a wrapper around a third-party system, or any combination.

Every agent is an actor. Not every actor is an agent.

### Actor Roles in Plan Lifecycle

| Role | Phase | Description |
|------|-------|-------------|
| **Strategy actor** | Strategize | Planner/architect. Analyzes context, produces decision tree. Required. |
| **Execution actor** | Execute | Builder/implementer. Executes strategy, invokes tools. Required. |
| **Estimation actor** | Pre-execute | Cost/risk estimator. Optional. |
| **Invariant actor** | Strategize entry | Reconciles invariant conflicts across scopes. Optional (fallback chain: plan → action → project → global config). |

### Agent Behavior Configuration

Agent behavior is configurable without code changes: prompt templates, tool sets, safety constraints, style constraints (verbosity, code style), and reliability controls (self-checks, validations).

## Constraints

- Every custom actor must be defined as a LangGraph graph in YAML. There is no imperative actor definition API.
- Actor names follow the namespace system (`[[server:]namespace/]name`).
- Actor references in graph nodes must point to registered actors. Forward references are not resolved — load order matters.
- Built-in provider namespaces (`openai/`, `anthropic/`, etc.) are reserved for built-in LLM actors and cannot be used for custom actor names.
- An action must specify at least two actors: `strategy_actor` and `execution_actor`. Estimation and invariant actors are optional.

## Consequences

### Positive
- A single abstraction (actor-as-graph) handles everything from simple single-LLM agents to complex multi-step workflows.
- YAML-based definition enables non-developers to create and modify actors without writing code.
- Hierarchical composition enables building complex behaviors from simple, reusable components.
- The actor/agent distinction preserves the familiar "agent" concept while enabling non-agent automation.

### Negative
- Expressing all actors as graphs adds complexity for simple single-LLM use cases.
- YAML graph definitions can become difficult to read and maintain for complex multi-node actors with many conditional edges.
- Load-order dependency for actor references complicates registration and updates.

### Risks
- Circular actor references (A references B which references A) must be detected and rejected.
- Deep actor composition hierarchies may have unpredictable latency and cost characteristics.
- The YAML graph DSL may prove insufficient for complex orchestration patterns, requiring escape hatches to code.

## Alternatives Considered

**Separate abstractions for agents and workflows** — Would require two execution engines, two configuration formats, and two sets of management commands. The actor-as-graph approach provides a single unified model.

**Code-defined actors (Python classes)** — More flexible but prevents non-developer customization. YAML definitions are chosen to align with the declarative configuration philosophy of the rest of the system.

## Compliance

- **Graph validation**: Actor registration validates that the graph is well-formed — no dangling references, no circular dependencies, all required nodes present.
- **Schema validation**: Pydantic models validate actor YAML configuration against the expected schema.
- **Composition tests**: Integration tests verify that hierarchically composed actors correctly delegate to sub-actors and aggregate results.
- **Role tests**: BDD scenarios verify that strategy, execution, estimation, and invariant actors fulfill their roles correctly within the plan lifecycle.
- **Code review**: New actor definitions are reviewed for correct graph structure, appropriate skill access policies, and reasonable limit configurations.
