A modular multi-agent framework for building AI development workflows with Ollama
Project description
AgentChain 🔗
A minimal, zero-dependency Python library for building multi-agent workflows.
Fluent API • Custom Agents • Per-Agent Models • Event Hooks • PyPI Ready
Installation
pip install agentchain
Or install from source:
git clone https://github.com/yourusername/agentchain
cd agentchain
pip install -e .
Quick Start
from agentchain import AgentChain
AgentChain() \
.requirements("Build a calculator app with add, subtract, multiply, divide") \
.model("deepseek-coder-v2:16b") \
.output("./calculator") \
.run()
That's it. AgentChain coordinates multiple AI agents to plan and implement your project.
Core Concepts
The Default Workflow
AgentChain ships with 3 agents that form a complete development pipeline:
| Agent | Role | Responsibility |
|---|---|---|
| Contractor | Orchestrator | Coordinates the workflow, handles retries, verifies completion |
| Planner | Planning | Breaks requirements into concrete, ordered tasks |
| Coder | Implementation | Writes code to files based on task specifications |
Fluent API
Configure everything with method chaining:
AgentChain()
.requirements("Your project description") # What to build
.model("llama3:8b") # Default model for all agents
.configure("planner", model="llama3:70b") # Planner gets a bigger model
.configure("coder", model="deepseek-coder-v2:16b") # Coder is specialized
.output("./my_project") # Where to write files
.run()
Configuration
Per-Agent Models
Use the right model for each task:
AgentChain()
.requirements("Build a REST API")
.configure("planner", model="llama3:70b") # Planning: bigger model
.configure("coder", model="deepseek-coder-v2:16b") # Coding: code-focused model
.output("./api")
.run()
Agent Parameters
Pass custom parameters to specific agents:
AgentChain()
.requirements("Build a game")
.configure("planner",
model="llama3:70b",
max_tasks=20, # Custom parameter
include_tests=True # Custom parameter
)
.configure("coder",
model="deepseek-coder-v2:16b",
style="verbose", # Custom parameter
language="python" # Custom parameter
)
.output("./game")
.run()
LLM Configuration
Configure the LLM endpoint:
from agentchain import ChainConfig
config = ChainConfig(
llm_base_url="http://localhost:11434", # Ollama default
default_model="llama3:8b",
)
AgentChain(config)
.requirements("Build something")
.output("./output")
.run()
Custom Agents
Function-Based Agents
The simplest way to add an agent:
from agentchain import AgentChain, agent, AgentContext, AgentResult
@agent(name="reviewer", role="Code Reviewer")
def review_code(context: AgentContext, llm) -> AgentResult:
code = context.get("code", "")
response = llm.generate(
"Review this code for issues:\n\n" + code,
model="llama3:8b"
)
return AgentResult.ok({"review": response})
# Use it
AgentChain()
.requirements("Build a CLI tool")
.register("reviewer", review_code) # Add to this chain
.output("./cli")
.run()
Class-Based Agents
For more complex agents with state:
from agentchain import Agent, agent, AgentContext, AgentResult
@agent(name="tester", role="Test Writer")
class TesterAgent(Agent):
def execute(self, context: AgentContext) -> AgentResult:
code_files = context.get("files", [])
for file in code_files:
test_code = self._llm.generate(
f"Write tests for:\n{file['content']}",
model=self._config.get("model", "llama3:8b")
)
# Write test file...
return AgentResult.ok({"tests_created": len(code_files)})
Custom Orchestrator
Replace the entire workflow:
from agentchain import Orchestrator, orchestrator, AgentContext, AgentResult
@orchestrator(name="my_workflow", role="Custom Pipeline")
class MyOrchestrator(Orchestrator):
def execute(self, context: AgentContext) -> AgentResult:
# Phase 1: Your planning logic
plan_result = self.invoke_agent("planner", context)
# Phase 2: Your implementation logic
for task in plan_result.data.get("tasks", []):
self.invoke_agent("coder", AgentContext(
task=task["description"],
inputs=task
))
# Phase 3: Your custom phase
if self.get_agent("tester"):
self.invoke_agent("tester", context)
return AgentResult.ok({"status": "complete"})
# Use it
AgentChain()
.requirements("Build something")
.orchestrator(MyOrchestrator)
.output("./output")
.run()
Events & Callbacks
Monitor progress with event hooks:
from agentchain import AgentChain, Event
def on_progress(event_type, data):
print(f"[{event_type}] {data.get('message', '')}")
def on_task_complete(event_type, data):
task = data.get("task", {})
print(f"✓ Completed: {task.get('name')}")
AgentChain()
.requirements("Build an app")
.on(Event.PROGRESS, on_progress)
.on(Event.TASK_COMPLETE, on_task_complete)
.on(Event.AGENT_ERROR, lambda e, d: print(f"Error: {d}"))
.output("./app")
.run()
Available Events
| Event | Trigger |
|---|---|
Event.CHAIN_START |
Chain execution begins |
Event.CHAIN_COMPLETE |
Chain execution finishes |
Event.AGENT_START |
An agent starts executing |
Event.AGENT_COMPLETE |
An agent finishes |
Event.AGENT_ERROR |
An agent encounters an error |
Event.TASK_START |
A task begins |
Event.TASK_COMPLETE |
A task completes |
Event.TASK_FAILED |
A task fails |
Event.PROGRESS |
General progress update |
CLI Usage
AgentChain includes a command-line interface:
# Basic usage
agentchain "Build a todo app with Flask" --output ./todo-app
# With specific model
agentchain "Build a calculator" --model deepseek-coder-v2:16b --output ./calc
# Verbose output
agentchain "Build a game" --output ./game --verbose
API Reference
AgentChain
The main entry point for building chains.
class AgentChain:
def requirements(self, text: str) -> AgentChain
def model(self, name: str) -> AgentChain
def configure(self, agent_name: str, **kwargs) -> AgentChain
def output(self, directory: str) -> AgentChain
def orchestrator(self, cls: Type[Orchestrator]) -> AgentChain
def register(self, name: str, agent: Agent | Callable) -> AgentChain
def on(self, event: Event, handler: Callable) -> AgentChain
def run(self) -> AgentResult
AgentContext
Data passed to agents:
class AgentContext:
task: str # Current task description
inputs: dict # Input data from previous agents
workspace: str # Output directory path
def get(self, key: str, default=None) -> Any
AgentResult
Return value from agents:
class AgentResult:
success: bool # Whether execution succeeded
data: dict # Output data
error: str | None # Error message if failed
@classmethod
def ok(cls, data: dict) -> AgentResult
@classmethod
def fail(cls, error: str) -> AgentResult
Requirements
- Python: 3.9+
- LLM Backend: Ollama running at
localhost:11434(configurable) - Dependencies: None (stdlib only!)
Examples
See the examples/ directory:
- minimal.py - Basic usage
- multi_model.py - Different models per agent
- custom_agent.py - Creating custom agents
- custom_orchestrator.py - Custom workflow
License
MIT License - see LICENSE
Contributing
Contributions welcome! Please read our contributing guidelines first.
# Development install
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black src/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llama2a-0.1.0.tar.gz.
File metadata
- Download URL: llama2a-0.1.0.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bed6f7ba1b3b43f4b2dfdb14a0254fe2793706efcbf7b71e2930753a35ee7aff
|
|
| MD5 |
23f3e143f17ed46fcb512690648a147d
|
|
| BLAKE2b-256 |
fa117411e8eb7d162ef2361c512b7261707cc40bcf36bbc1c9f1127f6fb683ec
|
File details
Details for the file llama2a-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llama2a-0.1.0-py3-none-any.whl
- Upload date:
- Size: 5.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f0772e9d4aec49a4ec696ad7dd18872f217f4567defe728614a8579aded9531
|
|
| MD5 |
e0fe834566d8520f9ba8d69b9331eba1
|
|
| BLAKE2b-256 |
760924f8bda66c2c4f30e0afa05ea050d57521fa2b3518e6ac5b7d957c9367fc
|