9.9 KiB
adr_number, title, status_history, tier, authors, superseded_by, related_adrs, acceptance
| adr_number | title | status_history | tier | authors | superseded_by | related_adrs | acceptance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | Actor and Agent Architecture |
|
2 |
|
null |
|
|
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).providerandmodel: 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_actorandexecution_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.