Local Development
Zero infrastructure. Just code.
Local Development
Start building agents in seconds with embedded BoltDB
Traditional development requires PostgreSQL running, Redis for state, environment variables configured.
Agentfield development requires one command: af server.
The CLI includes an embedded BoltDB database that auto-initializes. Write agent code, test multi-agent workflows, and iterate fast—without infrastructure setup.
Quick Start
Start Control Plane
af serverWhat happens:
- Control plane starts on
http://localhost:8080 - BoltDB initializes in
~/.agentfield/agentfield.bolt - Web UI accessible at
http://localhost:8080 - Auto-runs database migrations
- Ready for agent connections
Output:
2025-01-15 10:23:45 INFO Control plane started
2025-01-15 10:23:45 INFO Storage: BoltDB (local mode)
2025-01-15 10:23:45 INFO HTTP server listening on :8080
2025-01-15 10:23:45 INFO Web UI: http://localhost:8080Create Your First Agent
# In another terminal
af init my-agent --language python
cd my-agentGenerated structure:
my-agent/
├── agentfield.yaml # Agent configuration
├── main.py # Entry point
├── requirements.txt # Dependencies
└── README.mdRun the Agent
af runWhat happens:
- Agent registers with control plane at
localhost:8080 - Auto-detects callback URL (your machine's IP)
- Hot reload enabled (edit code, see changes instantly)
- Logs stream to terminal
Output:
2025-01-15 10:24:12 INFO Agent 'my-agent' starting...
2025-01-15 10:24:12 INFO Registered with control plane
2025-01-15 10:24:12 INFO Callback URL: http://192.168.1.100:8001
2025-01-15 10:24:12 INFO Hot reload: enabledTest Your Agent
# Call your agent's reasoner
curl -X POST http://localhost:8080/api/v1/execute/my-agent.process \
-H "Content-Type: application/json" \
-d '{"input": {"text": "Hello from local dev!"}}'Response:
{
"execution_id": "exec_abc123",
"status": "completed",
"result": {
"message": "Processed: Hello from local dev!"
}
}What's Included
Embedded Database
BoltDB auto-initializes in ~/.agentfield/agentfield.bolt. Stores executions, memory, agent registry—everything.
Limitation: Single writer, local only. Move to PostgreSQL for team collaboration or production testing.
Hot Reload
Python agents reload automatically when you save code:
app = Agent(node_id="my-agent", dev_mode=True)
@app.reasoner()
async def process(text: str) -> dict:
# Edit this, save → instant reload
return {"result": f"Processed: {text}"}Web UI
Access the control plane UI at http://localhost:8080:
Features:
- Workflow DAGs: Visualize execution flows
- Agent Registry: See active/inactive agents
- Execution History: Browse past runs
- Memory Inspector: View shared state
- Metrics Dashboard: Queue depth, throughput, errors
Development workflows:
- Write agent code
- Call via
curlor UI - View execution in real-time
- Debug with DAG visualization
- Iterate
Development Patterns
Single Agent Development
Most common: one control plane, one agent.
# Terminal 1: Control plane
af server
# Terminal 2: Your agent
cd my-agent
af runUse case: Building a new agent, rapid prototyping
Multi-Agent Development
Test agent coordination locally:
# Terminal 1: Control plane
af server
# Terminal 2: Support agent
cd agents/support
af run
# Terminal 3: Analytics agent
cd agents/analytics
af run
# Terminal 4: Email agent
cd agents/email
af runAll agents share:
- Same BoltDB database
- Memory fabric (via control plane)
- Workflow execution context
Test cross-agent calls:
# In support agent
@app.reasoner()
async def triage_ticket(ticket: dict) -> dict:
# Call analytics agent
sentiment = await app.call(
"analytics-agent.analyze_sentiment",
text=ticket["message"]
)
# Call email agent if urgent
if sentiment["urgency"] == "high":
await app.call(
"email-agent.notify_team",
ticket=ticket
)
return {"status": "triaged", "sentiment": sentiment}Control plane handles routing even on localhost.
Environment Variables
Minimal config needed for local development:
# Optional: Override defaults
export AGENTFIELD_SERVER=http://localhost:8080 # Default
export AGENT_PORT=8001 # Default
export AGENT_CALLBACK_URL=auto # Auto-detected
export AGENTFIELD_LOG_LEVEL=DEBUG # Verbose logging
# LLM provider (only if using app.ai())
export OPENAI_API_KEY=your-keyMost times you need zero env vars. Defaults work.
# Optional: Override defaults
export AGENTFIELD_SERVER=http://localhost:8080
export AGENT_PORT=8001
export AGENT_CALLBACK_URL=auto
# LLM provider
export OPENAI_API_KEY=your-keyTesting Workflows
Test Sync Execution
curl -X POST http://localhost:8080/api/v1/execute/my-agent.process \
-H "Content-Type: application/json" \
-d '{"input": {"text": "test"}}'Returns immediately with result (unless execution takes > 30s).
Test Async Execution
curl -X POST http://localhost:8080/api/v1/execute/async/my-agent.long_task \
-H "Content-Type: application/json" \
-d '{
"input": {"task": "generate_report"},
"webhook": {
"url": "http://localhost:3000/webhooks/agentfield",
"secret": "dev_secret_123"
}
}'Returns immediately with 202 Accepted and execution_id.
Poll for status:
curl http://localhost:8080/api/v1/executions/exec_abc123Webhook fires when complete (if you have a local server listening).
Test Memory Operations
# Agent 1: Write to memory
@app.reasoner()
async def store_data(key: str, value: str):
await app.memory.set(key, value)
return {"stored": key}
# Agent 2: Read from memory
@app.reasoner()
async def retrieve_data(key: str):
value = await app.memory.get(key)
return {"key": key, "value": value}Memory is shared across all agents connected to the control plane.
Debugging
View Logs
Control plane logs:
af server --log-level debugAgent logs:
# Python
af run --log-level debug
# Or set environment variable
export AGENTFIELD_LOG_LEVEL=DEBUG
af runInspect Workflow DAG
After executing an agent:
- Open Web UI:
http://localhost:8080 - Navigate to Executions
- Click on execution ID
- View DAG showing agent call chain
Example DAG:
support-agent.triage
├─> analytics-agent.analyze_sentiment
└─> email-agent.notify_teamDebug Memory State
Via Web UI:
- Go to Memory tab
- Filter by scope: global, actor, session, workflow
- View key-value pairs in real-time
Via API:
curl http://localhost:8080/api/v1/memory?scope=globalTest Failure Scenarios
Kill agent mid-execution:
# Start long-running task
curl -X POST http://localhost:8080/api/v1/execute/async/my-agent.slow_task \
-d '{"input": {}}'
# Kill agent process
pkill -f "af run"Control plane detects missed heartbeat:
- Marks agent as
inactiveafter 2 minutes - Queued executions fail with "agent unavailable"
- Restart agent → auto-registers → resumes
Moving to PostgreSQL
When you need team collaboration or production patterns, switch to PostgreSQL:
export AGENTFIELD_POSTGRES_URL=postgres://localhost:5432/agentfield
af server # Migrations run automaticallyYour agent code doesn't change. Same code works with BoltDB or Postgres.
Development Tips
Use .env Files
# my-agent/.env
AGENTFIELD_SERVER=http://localhost:8080
OPENAI_API_KEY=sk-...
AGENT_PORT=8001Load automatically:
# main.py
from dotenv import load_dotenv
load_dotenv()
from agentfield import Agent
app = Agent("my-agent")Mock External APIs
When developing agents that call external services:
# main.py
import os
MOCK_MODE = os.getenv("MOCK_MODE", "false") == "true"
@app.skill()
async def fetch_weather(city: str):
if MOCK_MODE:
return {"temp": 72, "condition": "sunny"} # Mock response
# Real API call
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.weather.com/{city}")
return response.json()Enable mocking:
export MOCK_MODE=true
af runQuick Reset
Clear all data and start fresh:
# Stop control plane
pkill -f "af server"
# Delete BoltDB file
rm ~/.agentfield/agentfield.bolt
# Restart
af serverUse case: Testing migrations, resetting state, debugging persistence issues.
Related Documentation
- Build Your First Agent - Deep dive into agent development
- Docker Deployment - Containerized development with PostgreSQL
- Deployment Overview - Production patterns and architecture
- Environment Variables - Complete configuration reference
Local development is friction-free. One command starts infrastructure. Focus on agent logic, not ops.