Boundary fix: - Delete src/cleveractors/acms/index.py (ACMSIndex, FileTraversalEngine, IndexEntry, FileType, TierLevel) — these are CLI/storage concerns that belong in cleveragents-core per ADR-001; the file already exists there at cleveragents/acms/index.py - Clean src/cleveractors/acms/__init__.py: remove all index.py imports and re-exports; __all__ now derives purely from uko.__all__ ProviderRegistryPort (ADR-005): - Add src/cleveractors/ports/provider_registry.py: ProviderRegistryPort Protocol with get(provider, model) -> Agent | None; structural typing, no host imports, mirrors the shape of ToolRegistryPort - Update src/cleveractors/ports/__init__.py: export ProviderRegistryPort alongside ToolRegistryPort; update module docstring Wire provider resolution into the node executor: - compiler.py: _map_node now accepts actor_provider/actor_model defaults and merges them into AGENT node metadata (setdefault so per-node config values still win); compile_actor passes config.provider/config.model - nodes.py: Node.__init__ gains optional provider_registry parameter; _execute_agent resolution order is now: (1) pre-resolved agents dict, (2) ProviderRegistryPort.get(provider, model) from node metadata, (3) graceful synthetic fallback — the ValueError guard for missing config.agent is removed since the registry is a valid alternative path Documentation: - Port ADR-003 (actor abstraction definition) from cleveragents-core ADR-031 - Port ADR-004 (Jinja2 YAML template preprocessing) from cleveragents-core ADR-032 - Add ADR-005 (Provider Registry Protocol) for the new provider_registry port - Port five reference docs: actors_schema.md, actor_compiler.md, actor_config.md, actor_hierarchy.md, actors_examples.md - Port API reference: api/actor.md - Add provider field to all YAML examples in actors_examples.md, actor_hierarchy.md, and actor_config.md - Update error messages in actor_config.md to match actual validator output - Add graph-level provider/model propagation docs to actor_compiler.md - Add ADR-005 cross-references to ADR-001, ADR-002, and actors_schema.md - Fix ADR-005 status section to reflect that implementation is in this PR - Fix ADR-004 to reference actual test files (smoke.feature, not phantom ones) - Remove fabricated reserved-namespace constraint from ADR-003 Constraints - Fix broken LICENSE link in docs/index.md for MkDocs rendering - Add provider field to ActorConfigSchema table in api/actor.md - Add internal modules section to docs/specification.md (ticket item 7) - Fix markdown formatting in actors_schema.md provider field definition - Add ADR-005 cross-reference to actors_schema.md provider field section BDD coverage (7 scenarios, 30 steps): - ProviderRegistryPort happy-path resolution - Graceful fallback when provider registry returns None - Graceful fallback when no provider registry is supplied - Pre-resolved agents dict takes precedence over provider registry - Compile a minimal graph actor, reject missing provider, render template Post-review fixes applied in amend: - actors_examples.md and actor_hierarchy.md: added provider to all examples - ADR-003: removed fabricated reserved-namespace constraint - ADR-004: corrected phantom test file references - ADR-005: updated status to reflect implementation is included - Added 'no registry' and 'agents dict precedence' BDD scenarios - Added provider to api/actor.md ActorConfigSchema fields table - ADR-001 and ADR-002: added ADR-005 cross-references - actor_config.md: fixed error message to match validator - actor_compiler.md: documented graph-level provider/model propagation - index.md: fixed broken LICENSE link - actors_schema.md: added ADR-005 cross-reference ISSUES CLOSED: #4
20 KiB
Actor YAML Schema Reference
Overview
An actor defines what an agent can do, how it processes information, and how it interacts with tools and other actors. This document is the complete schema reference for the actor YAML format parsed, validated, and compiled by this library.
Module: cleveractors.actor.schema
Schema Version: 1.0
Table of Contents
- Actor Types
- Field Definitions
- Tool Node Semantics
- Graph Constraints
- Validation Rules
- Error Messages
- Examples
- Usage
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 nametype: llmprovider- LLM provider name (e.g.,openai,anthropic); resolved at runtime viaProviderRegistryPortmodel- LLM model identifier (e.g.,gpt-4,claude-3-opus)
Optional Fields:
system_prompt- System prompt defining agent behaviourtools- List of tool references or inline definitionscontext_view- Role-based context filteringmemory- Conversation history settingscontext- File inclusion settings
Example:
name: assistants/code_reviewer
type: llm
provider: openai
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 nametype: tooltools- At least one tool (reference or inline definition)
Example:
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 nametype: graphprovider- LLM provider name (e.g.,openai,anthropic)model- LLM model for agent nodesroute- Complete graph topology definition
Optional Fields:
system_prompt- Default prompt for graph-level agentstools- Tools available to all agent nodescontext_view- Context filtering for the entire graphmemory- Memory settings for the graphcontext- Context settings
Example:
name: workflows/test_driven_dev
type: graph
provider: openai
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_reviewerworkflows/test_driven_devutilities/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 behaviour 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 compatibility tracking
provider (conditional)
Type: str
Required for: llm, graph actors
Optional for: tool actors (not used)
Description: LLM provider name resolved at runtime by the host via
cleveractors.ports.provider_registry.ProviderRegistryPort.
See ADR-005 for the runtime
resolution contract.
Common Values:
- OpenAI:
openai - Anthropic:
anthropic - Google:
google
Validation:
LLM and GRAPH actors require 'provider' fieldif missing forllmorgraphactors.
model (conditional)
Type: str
Required for: llm, graph actors
Optional for: tool actors (not used)
Description: LLM model identifier passed to the host's ProviderRegistryPort
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 behaviour and guidelines.
Supports Jinja2 template syntax — see
ADR-004 for details.
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 for tool actors)
Type: list[str | ToolDefinition]
Required for: tool actors
Optional for: llm, graph actors
Description: Tools available to the actor
Tool Reference Format:
tools:
- namespace/tool_name # Reference to a named tool
Inline Tool Definition:
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 descriptionrequired(bool, default=True): Whether parameter is requireddefault(any, optional): Default value for optional parameters
Example:
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 innamespace/nameformatdescription(str, required): What the tool doesparameters(list, optional): List of ToolParameter objectscode(str, required): Python code implementing the tool
Validation:
- Tool name must follow
namespace/nameformat - Code must be valid Python
- Parameter names must be valid identifiers
Memory Configuration
MemoryConfig
Fields:
enabled(bool, default=True): Enable conversation memorymax_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:
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 contextinclude_dirs(list[str]): Directories to include in contextexclude_patterns(list[str]): Glob patterns to excludemax_context_tokens(int, optional): Maximum context window size
Example:
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 hint; consumed by the host's
context assembly layer.
Context Views:
-
strategist: High-level planning view
- Project structure, goals, 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
-
full: Complete view (use sparingly)
- All available context; can exceed token limits
Tool Node Semantics
Tool References
Tool references point to named tools that the host resolves via its tool registry at runtime. 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:
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:
- Keep inline tools simple and focused
- Include proper error handling
- Add docstrings to the function
- Use type hints for all parameters
- Validate inputs before processing
Example:
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 graphedges(list[EdgeDefinition], required): Connections between nodesentry_node(str, required): Starting node IDexit_nodes(list[str], required): Terminal node IDs
Node Types
AGENT Node
Type: agent
Purpose: LLM-powered decision making and content generation
Configuration:
- 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:
- 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:
- 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:
- id: code_review
type: subgraph
name: Code Reviewer
description: Runs full code review workflow
actor_ref: local/code-reviewer
Subgraph Rules:
- Reference the actor by its namespaced name via
actor_ref - State is passed to and from the subgraph
- Subgraphs enable workflow composition and reuse
- Circular subgraph references are rejected at compile time
Edge Definition
Fields:
from_node(str, required): Source node IDto_node(str, required): Target node IDcondition(str, optional): Conditional routing expressionpriority(int, default=0): Edge priority (higher = evaluated first)
Example:
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:
nodes:
- id: planner
- id: executor
- id: reviewer
Invalid:
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.
Invalid:
nodes:
- id: start
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.
Invalid:
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.
Invalid:
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).
Invalid:
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']
6. All Nodes Reachable
Rule: All nodes must be reachable from the entry node.
Invalid:
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
name: code_reviewer # ❌ Missing namespace
Error:
ValueError: Actor name must be namespaced (namespace/name): code_reviewer
Fix:
name: assistants/code_reviewer # ✅
TypeError: Missing Required Field
Cause: LLM or GRAPH actor missing model field
Error:
ValueError: LLM actors require 'model' field
Cause: LLM or GRAPH actor missing provider field
Error:
ValueError: LLM and GRAPH actors require 'provider' field
Fix:
provider: openai
ValueError: Empty Tools List
Cause: TOOL actor with no tools defined
Error:
ValueError: TOOL actors require at least one tool
ValueError: Missing Route
Cause: GRAPH actor without route definition
Error:
ValueError: GRAPH actors require 'route' field
Examples
Example 1: Simple LLM Actor
name: assistants/code_reviewer
type: llm
description: Reviews Python code for best practices
version: "1.0"
provider: openai
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
name: assistants/file_analyzer
type: llm
description: Analyzes files and generates reports
version: "1.0"
provider: openai
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
name: workflows/doc_processor
type: graph
description: Processes documents through multiple stages
version: "1.0"
provider: openai
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
name: workflows/ci_pipeline
type: graph
description: CI/CD pipeline with test and deploy stages
version: "1.0"
provider: openai
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
from cleveractors.actor.schema import ActorConfigSchema
from cleveractors.actor.yaml_loader import load_yaml_text
# Load from file
with open("actors/code_reviewer.yaml") as f:
raw = load_yaml_text(f.read())
actor = ActorConfigSchema.model_validate(raw)
# Access fields
print(actor.name) # assistants/code_reviewer
print(actor.type) # ActorType.LLM
print(actor.model) # gpt-4
# Validation is automatic on load:
# - All nodes have unique IDs
# - Entry/exit nodes exist
# - All edges reference valid nodes
# - No cycles detected
# - All nodes reachable
Compiling an Actor
from cleveractors.actor.compiler import compile_actor
compiled = compile_actor(actor)
print(compiled.metadata.node_ids) # list of all node IDs
print(compiled.metadata.lsp_bindings) # extracted LSP bindings
See Also
- ADR-003: Actor Abstraction Definition
- Actor Configuration Guide — practical authoring guide
- Actor Hierarchy Extensions — LSP bindings and tool sources
- Actor Compiler — compilation pipeline