Traditional AI stops at a response. Actuate keeps going until quality is measured, not assumed.
Generate once. There is no measured signal telling you whether the output actually met the bar — only the model finishing talking.
Iterate until a measurable setpoint is met, or until stability guards call it: converged, exhausted, oscillating, or timed out.
If you want stability, gain, and convergence around model output — not just a graph of tools.
A DAG of tools calls for a workflow engine. A conversation calls for a chat product. Actuate is for teams that need oscillation detection and a measured path to “good enough,” every run.
Classical control theory maps onto Actuate one-to-one.
Signals are values on the wire and are never mutated. Events are facts in the log and are never rewritten. Iterations, status, and convergence progress are projections of the event stream — not a second mutable database.
Frozen constraints. A clear boundary between what decides and what executes.
A graph is how topology is represented — NetworkX plus port-typed edges — and how the console runs multi-agent labs. It is not the aggregate root.
| Layer | Responsibility | Not responsible for |
|---|---|---|
| actuate.domain | Types — signals, events, topology, policy, registry | I/O |
| actuate.engine | Walk topology, invoke capabilities, append events | Control law, SQL, HTTP |
| Controller | Error + objective + history → control decision | Graph traversal |
| Scheduler | Sequential / future parallel dispatch | What to run |
| RunStore | Durable workspace / system / spec / event log | Binary blobs |
| EventSink | Trace, persist, WebSocket, MLflow | Orchestration |
| CapabilityRegistry | Discover and instantiate plugins | Execution |
A Python package to embed, and an operator console to run.
Install with the extras you need. Import ExecutionEngine, GraphRunner, plants, sensors, and stores directly — this is how you embed Actuate inside another service or script.
pip install -e ".[plants,persistence,ui]"
FastAPI + React. The operator UI for the same package, run with python -m actuate.api and ui/frontend. Same capabilities, optional extras.
actuate # API server actuate-bootstrap # seed Postgres
import asyncio
from actuate.domain.policy import LoopPolicy, SetPoint, StabilityGuard
from actuate.domain.specification import create_specification
from actuate.domain.templates import standard_closed_loop
from actuate.engine import ExecutionEngine
from actuate.plugins import register_builtins
async def main() -> None:
spec = create_specification(
control_system_id="demo",
version_number=1,
topology=standard_closed_loop(
plant_name="stub",
sensor_name="rule",
sensor_params={"required_phrases": ["MUST-INCLUDE"]},
),
policies=LoopPolicy(
set_point=SetPoint(target=0.95),
stability=StabilityGuard(max_iterations=6),
),
)
run = await ExecutionEngine().run(
spec,
registry=register_builtins(),
initial_prompt="Write a short answer.",
)
print(run.id, run)
asyncio.run(main())you do not run chat-completions yourself — pass model, api_key, and an optional api_base into the plant. Actuate calls the provider.
Four steps from clone to a running control console.
Console API auth is on by default. Live LLM calls need a saved provider key under Settings.
Clone
Grab the source. Swap the org for your fork if you haven't published a canonical repo yet.
git clone https://github.com/actuate-ai/actuate.git cd actuate
Postgres
Actuate's production RunStore is Postgres, and only Postgres. Bring it up with Docker Compose.
docker compose up -d postgres # unix export DATABASE_URL=postgresql+psycopg://actuate:actuate@localhost:5432/actuate
Install & boot the API
Startup bootstraps Postgres idempotently — a default workspace, four control systems, specs, and provider base URLs. Existing API keys are never overwritten.
pip install -e ".[ui,persistence,plants,dsl,dev]" python -m actuate.api
Open the console
Start the React frontend and open localhost:5173. Ctrl/Cmd + K opens the command palette.
cd ui/frontend npm install npm run dev
Twelve screens for treating named runs as operations, not chat history.
Dense KPIs, charts, status/provider mix, template sizes, searchable activity
Multi-agent graph (default) or control loop; named runs
Loop I/O; selected iteration opens as the detail panel
Tools + I/O logs; rerun a node as a revision; export JSON pack
Named loop + graph history with in-place search
Drag-drop specialists, labs, cursor zoom, pan, minimap
Cohort stats plus compare two named runs
Trajectories in Postgres when DATABASE_URL is set
Provider catalog + full agent system prompts
Capability registry
Score, tokens, latency, status mix from activity
Persist keys + custom bases to Postgres
LangGraph is a workflow runtime. Actuate is a control system with an optional graph inside it.
Use LangGraph for LangChain's graph SDK. Use Actuate for measure → correct → converge around generation, with a console that treats named runs as operations.
Bring any provider.
| Provider | How |
|---|---|
| Stub | Tests only (allow_stub). Not a console default |
| OpenAI, Anthropic, Gemini, Groq, OpenRouter | LiteLLM + env / Settings keys |
| NVIDIA NIM | https://integrate.api.nvidia.com/v1 + NVIDIA_API_KEY |
| Ollama | Local http://localhost:11434 |
| Custom | Your URL + key + model, OpenAI chat-completions compatible |
What a graph specialist can reach for.
| Tool | What it does |
|---|---|
| web_search | DuckDuckGo instant-answer search |
| http_get | GET a public https URL — private / loopback / metadata blocked, ~80KB cap |
| recall_memory | Similar past converged trajectories |
| calculator | Arithmetic |
| utc_now | UTC timestamp |
| list_connections | This node's parents/children |
| handoff | Structured packet for downstream nodes |
One tree, clearly separated concerns.
actuate/
domain/ ControlSystem, Specification, Topology, Signals, Events, ExecutionSession
engine/ ExecutionEngine, controllers, fusion, scheduler, NetworkX helper
plants/ StubGenerator, LiteLLMAdapter (OpenAI, Anthropic, Gemini, Groq,
OpenRouter, NVIDIA NIM, Ollama, custom OpenAI-compatible)
sensors/ RuleEvaluator, LLMJudgeEvaluator, SimilarityEvaluator
actuators/ PromptCorrector, OutputCorrector, ContextCorrector, StrategyCorrector
retry/ Exponential, diversity, temperature sweep, model switch, perturbation
memory/ Vector store + cosine retriever + learning graph
persistence/ InMemoryRunStore, SqlRunStore (Postgres), bootstrap seed
telemetry/ Tracing / persistence / MLflow event sinks
dsl/ YAML → Specification
plugins/ Built-in capability registration
graphs/ Specialist catalog, long prompts, DAG runner, agent tools
api/ FastAPI + WebSocket control plane + console auth
ui/frontend/ React control console (Vite)
docs/architecture/ architecture.md
tests/ Engine, graphs, tools, API, bootstrap
docker-compose.yml Postgres 16Five rules the architecture won't bend on.
Defects are welcome as issues. The architecture document itself is frozen — implementation and capabilities are where change belongs.
Live calls take prompt and completion tokens from the provider via LiteLLM. If an endpoint returns zeros, Actuate falls back to a length-based heuristic so budgets still move. Graph totals sum every specialist and judge call, including tool rounds. This is not a billing-grade tokenizer.
Built and maintained by one engineer, in the open.
Ezhilan Nagarajan
Software engineer building an automated loan-review pipeline for correspondent business loans at Rocket India, and maintainer of actuate-ai in his own time. Focused on full-stack, cloud-native, event-driven, and AI-powered systems.
Star it if the control-systems framing is useful. Fork it to try a different controller.
Open issues for defects. The architecture document is frozen — implementation is where change belongs.
git clone https://github.com/actuate-ai/actuate.git
git clone https://github.com/<you>/actuate.git git remote add upstream https://github.com/actuate-ai/actuate.git