Tutorial: Single Agent Workflow
Letβs build a complete workflow with one agent doing a coding task.
What Weβll Build
A simple system that:
- Creates a coding task
- Assigns it to an agent
- Waits for completion
- Reports the result
Setup
mkdir single-agent-demo && cd single-agent-demo
tt init --name demo
The Code
Create main.rs:
use tinytown::{Town, Task, Result};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
// Connect to town
let town = Town::connect(".").await?;
// Create an agent
let agent = town.spawn_agent("coder", "claude").await?;
println!("π€ Spawned agent: {}", agent.id());
// Create a task
let task = Task::new(
"Create a Rust function that calculates fibonacci numbers recursively"
);
println!("π Created task: {}", task.id);
// Assign to agent
let task_id = agent.assign(task).await?;
println!("β
Assigned task {} to coder", task_id);
// Check status periodically
loop {
tokio::time::sleep(Duration::from_secs(5)).await;
if let Some(state) = agent.state().await? {
println!(" Agent state: {:?}", state.state);
match state.state {
tinytown::AgentState::Idle => {
println!("π Agent completed work!");
break;
}
tinytown::AgentState::Error => {
println!("β Agent encountered error");
break;
}
_ => continue,
}
}
}
// Get task result
if let Some(task) = town.channel().get_task(task_id).await? {
println!("\nπ Task result:");
println!(" State: {:?}", task.state);
if let Some(result) = task.result {
println!(" Output: {}", result);
}
}
Ok(())
}
Running It
# In terminal 1: Keep the town running
tt start
# In terminal 2: Run your code
cargo run
What Happens
- Town connects to the existing town (and its Redis)
- Agent spawns with state
StartingβIdle - Task creates with state
Pending - Assignment sends a
TaskAssignmessage to agentβs inbox - Agent receives the message (in a real setup, Claude would process it)
- Polling checks agent state every 5 seconds
- Completion when agent returns to
Idle
The Message Flow
Your Code Redis Agent
β β β
β spawn_agent() β β
β ββββββββββββββββββββββββββΊβ β
β β SET tt:<town>:agent:xxx β
β β ββββββββββββββββββββββββββββββββββ
β β β
β assign(task) β β
β ββββββββββββββββββββββββββΊβ β
β β SET tt:<town>:task:yyy β
β β RPUSH tt:<town>:inbox:xxx β
β β ββββββββββββββββββββββββββββββββββ
β β β
β β BLPOP tt:<town>:inbox:xxx β
β ββββββββββββββββββββββββββββββββββββ
β β β
β state() β β
β ββββββββββββββββββββββββββΊβ β
β β GET tt:<town>:agent:xxx β
βββββββββββββββββββββββββββ β β
Simulating the Agent
In a real workflow, Claude (or another AI) receives the task. For testing, you can simulate completion:
# In redis-cli (replace <town> with your town name)
redis-cli -s ./redis.sock
# Get the inbox message
LPOP tt:<town>:inbox:550e8400-e29b-41d4-a716-446655440000
# Update agent state to idle
# (In practice, the agent process does this)
Key Takeaways
- Towns manage Redis β You donβt need to start it manually
- Agents are stateful β Their state persists in Redis
- Tasks are tracked β Full lifecycle from pending to complete
- Messages are reliable β Redis lists ensure delivery
Next Steps
- Multi-Agent Coordination β Coordinate multiple agents
- Task Pipelines β Chain tasks together