Local CLI Testing

Test your agents locally during development without the control plane

While building your agent, you can test skills and reasoners locally using CLI commands. This provides fast feedback during development without needing to run the control plane or make HTTP requests.

Quick Start

Given a simple agent:

from agentfield import Agent

app = Agent(node_id="hello-world")

@app.skill()
def get_greeting(name: str) -> dict:
    return {"message": f"Hello, {name}!"}

@app.reasoner
async def say_hello(name: str) -> dict:
    greeting = get_greeting(name)
    return greeting

if __name__ == "__main__":
    app.serve()

You can test it locally with CLI commands:

# Execute a function
python main.py call say_hello --name Alice

# List all available functions
python main.py list

# Interactive shell
python main.py shell

CLI mode runs your agent locally without the control plane. This means no workflow tracking, no DIDs, no observability - just fast function execution for testing.

CLI Commands

call - Execute Functions

Execute any registered skill or reasoner with automatic argument parsing.

Basic Usage:

python main.py call <function_name> --arg1 value1 --arg2 value2

Examples:

# Simple string argument
python main.py call say_hello --name Alice

# Multiple arguments
python main.py call process_ticket --text "Bug report" --priority high

# Numeric arguments
python main.py call calculate_metrics --threshold 0.85 --max_items 100

# Boolean arguments
python main.py call analyze_data --include_metadata true --verbose false

Complex Arguments (JSON):

For dict, list, or complex objects, pass JSON strings:

# Dictionary argument
python main.py call process_data --config '{"mode": "fast", "retries": 3}'

# List argument
python main.py call batch_process --items '[1, 2, 3, 4, 5]'

# Nested structures
python main.py call analyze --data '{"user": {"id": 123, "tags": ["premium"]}}'

Output:

Results are printed as formatted JSON:

{
  "message": "Hello, Alice!"
}

list - Discover Functions

List all available skills and reasoners with their signatures and descriptions.

python main.py list

Output:

📋 Agent: hello-world

Skills (deterministic):
  • get_greeting(name: str) -> dict
    No description

Reasoners (AI-powered):
  • say_hello(name: str) -> dict
    No description

Add docstrings to your functions to see helpful descriptions in the list output.

With Docstrings:

@app.reasoner
async def analyze_sentiment(text: str) -> dict:
    """Analyze the sentiment of the given text using AI."""
    return await app.ai(
        system="Analyze sentiment as positive, negative, or neutral",
        user=text
    )
$ python main.py list

Reasoners (AI-powered):
 analyze_sentiment(text: str) -> dict
    Analyze the sentiment of the given text using AI.

shell - Interactive Mode

Launch an interactive Python shell with all functions pre-loaded in the namespace.

python main.py shell

Output:

🚀 Agent Shell: hello-world
Available functions: ['agent', 'get_greeting', 'say_hello']

Tip: Use 'await say_hello(name="Alice")' for async functions

In [1]:

Usage in Shell:

# Call skills directly
In [1]: get_greeting(name="Bob")
Out[1]: {'message': 'Hello, Bob!'}

# Call async reasoners with await
In [2]: await say_hello(name="Charlie")
Out[2]: {'message': 'Hello, Charlie!'}

# Access the agent instance
In [3]: agent.node_id
Out[3]: 'hello-world'

The shell mode requires IPython. Install it with: pip install ipython

CLI vs Server Mode

The same Python file automatically detects which mode to run:

# CLI commands trigger local testing mode
python main.py call say_hello --name Alice
python main.py list
python main.py shell

Characteristics:

  • ✅ Fast local execution
  • ✅ No setup required
  • ✅ Immediate feedback
  • ❌ No control plane connection
  • ❌ No workflow tracking
  • ❌ No DIDs or verifiable credentials
  • ❌ No observability or monitoring

Use for: Development, testing, debugging

# No CLI commands = server mode
python main.py

Characteristics:

  • ✅ Full control plane integration
  • ✅ Workflow tracking and DAGs
  • ✅ DIDs and verifiable credentials
  • ✅ Observability and monitoring
  • ✅ Cross-agent communication
  • ✅ Production-ready

Use for: Production deployment, distributed systems

Common Testing Workflows

1. Rapid Iteration

Test changes immediately without restarting servers:

# Edit your code
vim main.py

# Test the change
python main.py call my_function --arg value

# Iterate
vim main.py
python main.py call my_function --arg value

2. Debugging with Shell

Use the interactive shell to explore behavior:

python main.py shell
# Test different inputs
In [1]: await analyze_sentiment(text="I love this!")
Out[1]: {'sentiment': 'positive', 'confidence': 0.95}

In [2]: await analyze_sentiment(text="This is terrible")
Out[2]: {'sentiment': 'negative', 'confidence': 0.92}

# Inspect intermediate results
In [3]: data = get_user_data(user_id=123)
In [4]: data
Out[4]: {'id': 123, 'name': 'Alice', 'email': 'alice@example.com'}

3. Testing Before Deployment

Validate your agent works before deploying:

# Test all critical functions
python main.py call process_order --order_id 12345
python main.py call calculate_total --items '[{"price": 10}, {"price": 20}]'
python main.py call send_notification --user_id 123 --message "Test"

# List to verify all functions are registered
python main.py list

# Deploy when ready
python main.py  # Starts server mode

Type Hint Support

CLI commands automatically parse arguments based on function type hints:

from pydantic import BaseModel

class UserInput(BaseModel):
    name: str
    age: int
    tags: list[str]

@app.skill()
def process_user(user: UserInput) -> dict:
    return {
        "processed": True,
        "name": user.name,
        "age": user.age,
        "tag_count": len(user.tags)
    }
# Pydantic models accept JSON
python main.py call process_user --user '{
  "name": "Alice",
  "age": 30,
  "tags": ["premium", "verified"]
}'

Supported Types:

  • str - String values
  • int - Integer values
  • float - Floating point values
  • bool - Boolean values (true/false)
  • dict - JSON objects
  • list - JSON arrays
  • Pydantic models - JSON objects validated against schema

Limitations

CLI mode is designed for development and testing only. It does not provide:

No Control Plane Features:

  • No workflow tracking or DAG visualization
  • No execution IDs or workflow IDs
  • No DIDs (Decentralized Identifiers)
  • No verifiable credentials
  • No observability or monitoring
  • No cross-agent communication via app.call()

No Production Features:

  • No HTTP endpoints
  • No webhook callbacks
  • No async execution with polling
  • No SSE (Server-Sent Events)
  • No authentication or authorization

Always use server mode for production deployments. CLI mode is strictly for local testing during development.

When to Use Each Mode

Use CLI Mode When:

  • 🔧 Developing new skills or reasoners
  • 🐛 Debugging function behavior
  • ✅ Testing changes before deployment
  • 🚀 Validating agent logic locally
  • 📝 Exploring function signatures

Use Server Mode When:

  • 🌐 Deploying to production
  • 📊 Need workflow tracking and observability
  • 🔗 Building distributed multi-agent systems
  • 🔐 Require DIDs and verifiable credentials
  • 📈 Need monitoring and analytics

Example: Complete Development Workflow

# main.py
from agentfield import Agent
from pydantic import BaseModel

app = Agent(node_id="support-agent")

class TicketAnalysis(BaseModel):
    category: str
    priority: str
    estimated_time: int

@app.reasoner
async def analyze_ticket(ticket_text: str) -> TicketAnalysis:
    """Analyze support ticket and categorize it."""
    return await app.ai(
        system="Analyze support tickets",
        user=ticket_text,
        schema=TicketAnalysis
    )

if __name__ == "__main__":
    app.serve()

Development Phase:

# Test during development
python main.py call analyze_ticket --ticket_text "Login button not working"

# Output:
# {
#   "category": "technical",
#   "priority": "high",
#   "estimated_time": 30
# }

# Iterate and test again
python main.py call analyze_ticket --ticket_text "How do I reset my password?"

# Verify all functions
python main.py list

Production Deployment:

# Deploy with control plane
python main.py

# Now accessible via HTTP with full observability
curl -X POST http://localhost:8080/api/v1/execute/support-agent.analyze_ticket \
  -H "Content-Type: application/json" \
  -d '{"input": {"ticket_text": "Login button not working"}}'