A stateful, embodied AI agent pipeline on a cluster of legacy computers, powered by Ansible, Nomad, Consul, and Pipecat.
Requirements • Structure • Setup • Bootstrap • Expanding • Agent Arch • Deployment
It uses Ansible for automated provisioning, Nomad for cluster orchestration, and a state-of-the-art AI stack to create a responsive, streaming, and embodied voice agent. For a detailed technical description of the system's layers, see the Holistic Project Architecture document or explore the comprehensive Thematic Manuals Index.
A brief overview of the key directories in this repository:
nomad, consul, pipecatapp).app.py, memory.py, and the tools directory.default_agent_loop.yaml).playbook.yaml, heal_cluster.yaml).all.yaml and models.yaml.SECURITY WARNING: Before provisioning any machines, it is strongly recommended to isolate your cluster network. Operating an autonomous AI agent on your primary home network presents significant risks. See the Network Isolation Guide for instructions on setting up a secure DMZ/VLAN.
Setting up a new cluster involves two main methods: a one-time manual setup for the first node, and a fully automated setup for all subsequent nodes.
The first node in your cluster requires a manual OS installation. This node will later be configured by Ansible to act as the PXE/iPXE boot server for all other nodes.
git clone <repo_url>initial-setup directory and edit
the setup.conf file. You must provide the machine's desired HOSTNAME, a
static IP address, and the CONTROL_NODE_IP (which should be the static IP
of this same machine, as it will become the control node).sudo bash setup.shAfter rebooting, this node is ready for Ansible provisioning (see Section 4).
It should be designated as both a controller_node and your pxe_server in
the Ansible inventory.
Once your first node has been provisioned by Ansible and the pxe_server role has been applied to it, you can automatically install Debian on all other bare-metal machines in your cluster.
This system uses an advanced iPXE-over-HTTP method that is significantly faster and more reliable than traditional PXE. For detailed instructions on how to apply the Ansible role and prepare the client machines for network booting, see the iPXE Boot Server Setup Guide.
For development, testing, or bootstrapping the very first node of a new cluster, you can use the provided bootstrap script. This is the recommended method for getting started.
On your control node, install Git: sudo apt install git -y
Clone this repository.
Run the Bootstrap Script: This script is a powerful wrapper around Ansible that handles all necessary steps to configure the local machine as a fully-functional, standalone agent, a cluster controller, or a new worker node.
Basic Usage:
./bootstrap.sh
sudo password, as the script needs
administrative privileges to install and configure software.Common Flags for Customizing the Bootstrap Process:
You can control the behavior of the bootstrap script with the following flags:
--role <role>: Specify the role for the node.
all (default): Full setup for a standalone control node.controller: Sets up only the core infrastructure services (Consul, Nomad, etc.).worker: Configures the node as a worker and requires --controller-ip.--controller-ip <ip>: The IP address of the main controller node. Required when --role is worker.--user <user>: Specify the target user for Ansible (default: pipecatapp).--tags <tag1,tag2>: Run only specific parts of the Ansible playbook (e.g., --tags nomad would only run the Nomad configuration tasks).--external-model-server: Skips the download and build steps for large
language models. This is ideal for development or if you are using a remote
model server.--purge-jobs: Stops and purges all running Nomad jobs before starting the
bootstrap process, ensuring a clean deployment.--leave-services-running: Do not clean up Nomad and Consul data on
startup (useful for restarts without state loss).--system-cleanup: Use with caution. Aggressively cleans Docker
resources, Apt cache, and logs to free up disk space on the host machine.--clean: Use with caution. This will permanently delete all untracked
files in the repository (git clean -fdx), restoring it to a pristine
state.--debug: Enables verbose Ansible logging (-vvvv) and saves the full
output to playbook_output.log.--verbose [level]: Set verbosity level (0-4). Default 0, or 3 if flag is
used without value.--continue: If a previous bootstrap run failed, this flag will resume the
process from the last successfully completed playbook, saving significant
time.--benchmark: Run benchmark tests during deployment.--deploy-docker: Deploy the pipecat application using Docker (Default).--run-local: Deploy the pipecat application using local raw_exec
(useful for debugging without Docker rebuilds).--container: Run the entire infrastructure inside a single large
container (experimental).--home-assistant-debug: Enable debug mode for Home Assistant integrations.--watch <target>: Pause for inspection after the specified target (task/role) completes.This single node is now ready to be used as a standalone conversational AI agent. It can also serve as the primary "seed" node for a larger cluster. To expand your cluster, see the advanced guide below.
If you are setting up a multi-node cluster, you will need to work with the Ansible inventory directly.
Configure Initial Inventory (inventory.yaml): Edit the inventory.yaml file to define your initial controller nodes. While new worker nodes will be added to the cluster automatically, you must define the initial seed nodes for the control plane here.
controller_nodes. This group must contain at least one node that will act as the primary control node and Nomad server.worker_nodes. This group will be populated automatically as new nodes join the cluster.Run the Main Playbook: Run the following command from the root of this repository. This will configure the initial control node(s) and prepare the cluster for auto-expansion.
ansible-playbook -i inventory.yaml playbook.yaml --ask-become-pass
--ask-become-pass: This flag is important. It will prompt you for your sudo password, which Ansible needs to perform administrative tasks.This cluster is designed for resilience and scalability. As your needs grow, you may need to add more controller nodes to the control plane for higher availability. This process is fully automated.
To promote an existing worker node to a controller:
Ensure the worker is part of the cluster: The node you wish to promote must already be a provisioned worker and visible in nomad node status.
Run the promotion playbook:
ansible-playbook playbooks/promote_controller.yaml
Enter the hostname: You will be prompted to enter the exact hostname of the worker node you want to promote (e.g., worker1).
The playbook will handle everything:
inventory.yaml file to move the node from the workers group to the controller_nodes group.consul and nomad configuration roles to re-provision it as a server.TwinService & Workflow EngineThe core of this application is the TwinService, which now orchestrates the agent's behavior using a flexible Workflow Engine. Instead of a hardcoded logic loop, the agent's thought process is defined in declarative YAML files (e.g., pipecatapp/workflows/default_agent_loop.yaml).
The agent uses a graph-based workflow engine where nodes represent steps in the thought process (e.g., "Summarize", "Reason", "Execute Tool").
The agent is capable of using a wide variety of tools to interact with the world. While the default workflow is a simple conversational loop, advanced workflows can leverage the following tools:
These features are integrated directly into the TwinService or prompt system:
route_to_expert.The following tools are available in the codebase (pipecatapp/tools/):
ansible): Runs Ansible playbooks to manage the cluster.autoresearch): Performs an iterative, autonomous code optimization loop.archivist): Performs deep research on the agent's long-term memory.claude_clone): A tool for interacting with a Claude-like model.code_runner): Executes Python code in a secure, sandboxed environment.container_registry): Search for container images and tags in the Docker Registry.council): Convenes a council of AI experts to deliberate on a query.dependency_scanner): Scans Python packages for vulnerabilities using the OSV database.desktop_control): Provides full control over the desktop environment (screenshots, mouse/keyboard).experiment): Orchestrates A/B testing or parallel experiments for code generation.file_editor): Reads, writes, and patches files in the codebase.final_answer): A tool to provide a final answer to the user.git): Interacts with Git repositories.ha): Controls smart home devices via Home Assistant.llxprt_code): A specialized tool for code-related tasks.mcp): Provides agent introspection and self-control.mtac): Programmatically triggers and monitors containerized ML model fine-tuning loops.open_workers): Manages and interacts with open worker agents.openclaw): Send messages via OpenClaw Gateway to various channels (WhatsApp, Telegram, etc.).opencode): Interface for the OpenCode tool.orchestrator): Dispatches high-priority, complex jobs to the cluster.ouroboros): Manages and navigates the circular Ouroboros webring.planner): Plans complex tasks and executes them.power): Controls the cluster's power management policies.project_mapper): Scans the codebase to generate a project structure map.prompt_improver): A tool for improving prompts.rag): Searches the project's documentation to answer questions.search): Search the codebase for text patterns or file names.set_operational_mode): Dynamic operational mode selector and behavioral guideline loader (via SkillLibrary).shell): Executes shell commands (uses a persistent tmux session).smol_agent_computer): A tool for creating small, specialized agents.spec_loader): Clones external Git repositories (docs, specs) and ingests them into the agent's context.ssh): Executes commands on remote machines.submit_solution): Allows a Worker Agent to submit a code solution or artifact to be parsed by the ExperimentTool/Judge.summarizer): Summarizes conversation history.swarm): Spawns multiple worker agents to perform tasks in parallel.term_everything): Provides a terminal interface for interacting with the system.ternlight): Executes high-speed ternary embedding semantic lookups on documentation.vr): Tools for Virtual Reality interactions.web_browser): Enables web navigation and content interaction.Note on Implementation History: Previous versions of the agent relied on a hardcoded "Router" agent with a static list of tools. The current system has evolved to a dynamic, workflow-driven architecture (
TwinService+WorkflowRunner), enabling more complex and varied agent behaviors.
The agent is designed to function as a "Mixture of Experts." The primary
pipecat agent acts as a router, classifying the user's query and routing it to
a specialized backend expert if appropriate.
TwinService (or a SimpleLLMNode in the workflow)
classifies the user's query. If it determines the query is best handled by a
specialist (e.g., a 'coding' expert), it routes the request to that expert
service via Consul.deploy_expert.yaml Ansible playbook. For detailed instructions, see the
Advanced AI Service Deployment
section below.The personality and instructions for the main router agent are defined in ansible/roles/pipecatapp/files/prompts/router.txt. You can edit this file to customize the behavior of the main agent. Expert agents are configured via the group_vars/models.yaml file, where you can define the models they use.
There are two primary ways to interact with the conversational agent: the web interface and the Gemini CLI extension.
Navigate to the IP address of any node in your cluster on port 8000 (e.g., http://192.168.1.101:8000). The web UI provides real-time conversation logs, a request-approval interface, and the ability to save and load the agent's memory state.
For command-line users, a Gemini CLI extension is provided to send messages directly to the agent.
Install the Gemini CLI:
npm install -g @google/gemini-cli
Navigate to the extension directory:
cd pipecat-agent-extension
Install dependencies and build the extension:
npm install
npm run build
Link the extension to your Gemini CLI installation:
gemini extensions link .
Once the extension is linked, you can use the custom /pipecat:send command to send a message to the agent:
gemini /pipecat:send "Your message here"
Example:
gemini /pipecat:send "Can you write a python script to list files in a directory?"
The agent will process this message as if you had typed it in the web UI.
In addition to the agent's interface, you can access the dashboards for the underlying infrastructure services.
URL: http://<node_ip>:8500
Login: Access requires the SecretID (management token).
Retrieving the Token: Run this command on your controller node:
sudo cat /etc/consul.d/management_token
http://<node_ip>:4646The system is designed to be self-bootstrapping. The bootstrap.sh script (or the main playbook.yaml) handles the deployment of the core AI services on the primary control node. This includes a default instance of the llama-expert job and the pipecat voice agent.
If a job has been stopped, or you just want to verify that everything is running as it should be, you now use your new, lightweight playbook. It will skip all the system setup and only manage the Nomad jobs.
ansible-playbook playbooks/heal_cluster.yaml
If you make a change to a job file or need to restart the services from a clean state, it's best to purge the old jobs before running the start script again.
nomad job stop -purge llamacpp-rpc
nomad job stop -purge pipecat-app
The true power of this architecture is the ability to deploy multiple,
specialized AI experts that the main pipecat agent can route queries to. With
the new unified llama-expert.nomad job template, deploying a new expert is
handled through a dedicated Ansible playbook.
Define a Model List for Your Expert:
First, open group_vars/models.yaml and create a new list of models for your expert. For example, to create a creative-writing expert, you could add:
creative_writing_models:
- name: "phi-3-mini-instruct"
# ... other model details
Deploy the Expert with Ansible:
Use the playbooks/deploy_expert.yaml playbook to render the Nomad job with your custom parameters and launch it. You pass variables on the command line using the -e flag.
Example: Deploying a creative-writing expert to the creative namespace:
ansible-playbook playbooks/deploy_expert.yaml \
-e "job_name=creative-expert service_name=llama-api-creative namespace=creative model_list={{ creative_writing_models }} worker_count=2"
The TwinService in the pipecatapp will automatically discover any service
registered in Consul with the llama-api- prefix and make it available for
routing.
To support running large models on legacy hardware with limited RAM, the system supports Split Inference.
expert job can be configured to offload computation to rpc-server providers running on worker nodes.rpc-provider services via Consul and passes them to the llama-server using the --rpc argument. This allows the model's layers to be split across multiple machines, aggregating their memory and compute power.For high-throughput, low-latency execution of DeepSeek V4 models (Flash and PRO) on legacy CPU and GPU hardware, the cluster deploys the DwarfStar (ds4) inference engine natively.
ds4 Ansible role and Nomad templates (ds4-server.nomad.j2), it enables distributed GGUF model serving integrated into the Mixture of Experts namespace-isolated mesh.To optimize resource usage on legacy hardware, this project includes an intelligent power management system.
power_agent.py, uses an eBPF program (traffic_monitor.c) to monitor network traffic to specific services at the kernel level with minimal overhead.power.set_idle_threshold tool.This project includes a web-based dashboard for real-time display and debugging. To access it, navigate to the IP address of any node in your cluster on port 8000 (e.g., http://192.168.1.101:8000). The UI provides:
To protect node environments from deployment corruptions and accidental modifications, the cluster integrates Btrfs-level snapshot backups.
btrfs_snapshot role takes automated snapshots of critical folders (such as /opt/pipecatapp, /etc/consul.d, /etc/nomad.d) prior to running major playbooks.recover_os.py script executes rsync/rollback routines from the read-only pre-deployment snapshots to restore system state instantly.The cluster supports managing encrypted secrets (like API keys, database passwords) directly within the Git repository using SOPS and age encryption.
Ansible automatically decrypts variables from *.sops.yaml files during playbook runs, ensuring that sensitive data is secure at rest while enabling GitOps workflows.
Setup Age Key:
age-keygen -o ~/.config/sops/age/keys.txt
Update SOPS Config:
Update .sops.yaml at the root of the project to include the public key generated above.
Manage Secrets:
Create encrypted variable files in group_vars/ or host_vars/ (e.g. group_vars/secrets.sops.yaml).
sops group_vars/secrets.sops.yaml
The Pipecat cluster utilizes a Trusted Platform Module (TPM) to provide a unique, immutable identity for each node.
Instead of storing traditional SSH private keys (id_rsa) in plaintext on the filesystem, the tpm_ssh Ansible role generates an SSH key and securely seals it inside the physical TPM chip.
tpm-ssh-agent systemd service. It unlocks the TPM key on boot using a securely stored, randomly generated PIN (located at /etc/tpm_ssh_pins with 0600 root-only access), and provides a persistent SSH_AUTH_SOCK.The cluster employs an Identity-Based Debugging flow to manage the dynamic Tailscale (Headscale) mesh network, separating automated machine identities from human administrative access.
Rather than distributing static Tailscale Auth Keys, human operators must use a hardware security key (e.g., YubiKey) to enroll their external machines into the cluster. This relies on an SSH challenge to the controller node.
scripts/enroll-admin.sh helper script on your external machine.headscale.local.mesh Tailnet over HTTPS (routed via Traefik).To eliminate the dependency on external services (such as GitHub) for fetching authorized_keys, the cluster implements a purely offline key distribution model using the Consul Key-Value (KV) store.
http://127.0.0.1:8500/v1/kv/ssh-keys/<hostname>)./usr/local/bin/update-ssh-authorized-keys.sh every 5 minutes. This script fetches all public keys from the Consul KV and idempotently updates the local ~/.ssh/authorized_keys file.nomad node statusnomad job statusnomad alloc logs <allocation_id> or use the Mission Control Web UI.A dedicated health check job exists to verify the status of all running LLM experts. This provides a quick way to ensure the entire cluster is operational.
ansible-playbook playbooks/run_health_check.yamlnomad job logs health-checktests/scripts/ directory.This project uses a suite of linters to ensure code quality and consistency. For detailed instructions on how to install the development dependencies and run the checks, please see the Linting Documentation.
To run all linters, use the following command:
npm run lint
llama-expert.nomad job is configured via Ansible variables in group_vars/models.yaml. You can define different model lists for different experts.RealtimeSTT sensitivity can be tuned in app.py for better performance in noisy environments.pipecatapp.nomad job file.This project includes two types of benchmarks.
Measures the end-to-end latency of a live conversation. Enable it by setting BENCHMARK_MODE = "true" in the env section of the pipecatapp.nomad job file. Results are printed to the job logs.
Uses llama-bench to measure the raw inference speed (tokens/sec) of the deployed LLM backend. Run the benchmark.nomad job to test the performance of the default model.
nomad job run /opt/nomad/jobs/benchmark.nomad
View results in the job logs: nomad job logs llama-benchmark
For advanced users, this project includes a workflow for automatically improving the agent's core prompt using evolutionary algorithms. See prompt_engineering/PROMPT_ENGINEERING.md for details.
This section outlines the major feature enhancements and maintenance tasks planned for the future.
llama-expert.nomad job to include a final, lightweight fallback model to ensure the expert service always starts with a basic capability.sidecar_service in the Nomad job files and document the process and performance overhead.power_agent.py.target_user.e2e-tests.yaml to verify core agent functions.For solutions to common issues, such as failing Nomad service checks or deployment errors, please refer to the Troubleshooting Guide.
This project is licensed under the GNU General Public License v3.0.
The project supports a Hybrid / Cluster-Native architecture, which means it can be deployed in different configurations depending on the hardware available.
mid):
Runs all tools, inference (LLM), and core logic on a single node. Ideal for a standalone server with adequate resources.edge, core):
Distributes the workload across a cluster using Consul for service discovery.
node_tier in Ansible inventoryYou can specify the tier of a node by setting the node_tier variable in your Ansible inventory file.
Example inventory.yaml:
all:
children:
controllers:
hosts:
controller1:
ansible_host: 192.168.1.100
node_tier: core
workers:
hosts:
worker1:
ansible_host: 192.168.1.101
node_tier: edge
worker2:
ansible_host: 192.168.1.102
node_tier: mid
Alternatively, if you're using bootstrap.sh, you can specify the tier via the command line:
./bootstrap.sh --tier [edge|mid|core]
The cluster maintains hardware agnosticism by utilizing a distributed file system (unified_fs_mount_point) as the primary shared volume across all nodes, rather than relying on host-specific local storage configurations like RAID-5.
This guarantees seamless high availability (HA) for persistent state.
To optimize SSD usage and prevent unnecessary duplication of multi-gigabyte machine learning models, the IPFS daemon (kubo) is configured with the experimental FilestoreEnabled setting.
ipfs add --nocopy flag..tar images that are immediately deleted post-load, must not use --nocopy. Using --nocopy on files that will be deleted corrupts the IPFS datastore by breaking the underlying file references.Python
45.6%
CSS
17.7%
JavaScript
17.4%
Rust
8.2%
Shell
3.6%
Jinja
3.3%
HTML
3.0%
A stateful, embodied AI agent pipeline on a cluster of legacy computers, powered by Ansible, Nomad, Consul, and Pipecat.
Requirements • Structure • Setup • Bootstrap • Expanding • Agent Arch • Deployment
It uses Ansible for automated provisioning, Nomad for cluster orchestration, and a state-of-the-art AI stack to create a responsive, streaming, and embodied voice agent. For a detailed technical description of the system's layers, see the Holistic Project Architecture document or explore the comprehensive Thematic Manuals Index.
A brief overview of the key directories in this repository:
nomad, consul, pipecatapp).app.py, memory.py, and the tools directory.default_agent_loop.yaml).playbook.yaml, heal_cluster.yaml).all.yaml and models.yaml.SECURITY WARNING: Before provisioning any machines, it is strongly recommended to isolate your cluster network. Operating an autonomous AI agent on your primary home network presents significant risks. See the Network Isolation Guide for instructions on setting up a secure DMZ/VLAN.
Setting up a new cluster involves two main methods: a one-time manual setup for the first node, and a fully automated setup for all subsequent nodes.
The first node in your cluster requires a manual OS installation. This node will later be configured by Ansible to act as the PXE/iPXE boot server for all other nodes.
git clone <repo_url>initial-setup directory and edit
the setup.conf file. You must provide the machine's desired HOSTNAME, a
static IP address, and the CONTROL_NODE_IP (which should be the static IP
of this same machine, as it will become the control node).sudo bash setup.shAfter rebooting, this node is ready for Ansible provisioning (see Section 4).
It should be designated as both a controller_node and your pxe_server in
the Ansible inventory.
Once your first node has been provisioned by Ansible and the pxe_server role has been applied to it, you can automatically install Debian on all other bare-metal machines in your cluster.
This system uses an advanced iPXE-over-HTTP method that is significantly faster and more reliable than traditional PXE. For detailed instructions on how to apply the Ansible role and prepare the client machines for network booting, see the iPXE Boot Server Setup Guide.
For development, testing, or bootstrapping the very first node of a new cluster, you can use the provided bootstrap script. This is the recommended method for getting started.
On your control node, install Git: sudo apt install git -y
Clone this repository.
Run the Bootstrap Script: This script is a powerful wrapper around Ansible that handles all necessary steps to configure the local machine as a fully-functional, standalone agent, a cluster controller, or a new worker node.
Basic Usage:
./bootstrap.sh
sudo password, as the script needs
administrative privileges to install and configure software.Common Flags for Customizing the Bootstrap Process:
You can control the behavior of the bootstrap script with the following flags:
--role <role>: Specify the role for the node.
all (default): Full setup for a standalone control node.controller: Sets up only the core infrastructure services (Consul, Nomad, etc.).worker: Configures the node as a worker and requires --controller-ip.--controller-ip <ip>: The IP address of the main controller node. Required when --role is worker.--user <user>: Specify the target user for Ansible (default: pipecatapp).--tags <tag1,tag2>: Run only specific parts of the Ansible playbook (e.g., --tags nomad would only run the Nomad configuration tasks).--external-model-server: Skips the download and build steps for large
language models. This is ideal for development or if you are using a remote
model server.--purge-jobs: Stops and purges all running Nomad jobs before starting the
bootstrap process, ensuring a clean deployment.--leave-services-running: Do not clean up Nomad and Consul data on
startup (useful for restarts without state loss).--system-cleanup: Use with caution. Aggressively cleans Docker
resources, Apt cache, and logs to free up disk space on the host machine.--clean: Use with caution. This will permanently delete all untracked
files in the repository (git clean -fdx), restoring it to a pristine
state.--debug: Enables verbose Ansible logging (-vvvv) and saves the full
output to playbook_output.log.--verbose [level]: Set verbosity level (0-4). Default 0, or 3 if flag is
used without value.--continue: If a previous bootstrap run failed, this flag will resume the
process from the last successfully completed playbook, saving significant
time.--benchmark: Run benchmark tests during deployment.--deploy-docker: Deploy the pipecat application using Docker (Default).--run-local: Deploy the pipecat application using local raw_exec
(useful for debugging without Docker rebuilds).--container: Run the entire infrastructure inside a single large
container (experimental).--home-assistant-debug: Enable debug mode for Home Assistant integrations.--watch <target>: Pause for inspection after the specified target (task/role) completes.This single node is now ready to be used as a standalone conversational AI agent. It can also serve as the primary "seed" node for a larger cluster. To expand your cluster, see the advanced guide below.
If you are setting up a multi-node cluster, you will need to work with the Ansible inventory directly.
Configure Initial Inventory (inventory.yaml): Edit the inventory.yaml file to define your initial controller nodes. While new worker nodes will be added to the cluster automatically, you must define the initial seed nodes for the control plane here.
controller_nodes. This group must contain at least one node that will act as the primary control node and Nomad server.worker_nodes. This group will be populated automatically as new nodes join the cluster.Run the Main Playbook: Run the following command from the root of this repository. This will configure the initial control node(s) and prepare the cluster for auto-expansion.
ansible-playbook -i inventory.yaml playbook.yaml --ask-become-pass
--ask-become-pass: This flag is important. It will prompt you for your sudo password, which Ansible needs to perform administrative tasks.This cluster is designed for resilience and scalability. As your needs grow, you may need to add more controller nodes to the control plane for higher availability. This process is fully automated.
To promote an existing worker node to a controller:
Ensure the worker is part of the cluster: The node you wish to promote must already be a provisioned worker and visible in nomad node status.
Run the promotion playbook:
ansible-playbook playbooks/promote_controller.yaml
Enter the hostname: You will be prompted to enter the exact hostname of the worker node you want to promote (e.g., worker1).
The playbook will handle everything:
inventory.yaml file to move the node from the workers group to the controller_nodes group.consul and nomad configuration roles to re-provision it as a server.TwinService & Workflow EngineThe core of this application is the TwinService, which now orchestrates the agent's behavior using a flexible Workflow Engine. Instead of a hardcoded logic loop, the agent's thought process is defined in declarative YAML files (e.g., pipecatapp/workflows/default_agent_loop.yaml).
The agent uses a graph-based workflow engine where nodes represent steps in the thought process (e.g., "Summarize", "Reason", "Execute Tool").
The agent is capable of using a wide variety of tools to interact with the world. While the default workflow is a simple conversational loop, advanced workflows can leverage the following tools:
These features are integrated directly into the TwinService or prompt system:
route_to_expert.The following tools are available in the codebase (pipecatapp/tools/):
ansible): Runs Ansible playbooks to manage the cluster.autoresearch): Performs an iterative, autonomous code optimization loop.archivist): Performs deep research on the agent's long-term memory.claude_clone): A tool for interacting with a Claude-like model.code_runner): Executes Python code in a secure, sandboxed environment.container_registry): Search for container images and tags in the Docker Registry.council): Convenes a council of AI experts to deliberate on a query.dependency_scanner): Scans Python packages for vulnerabilities using the OSV database.desktop_control): Provides full control over the desktop environment (screenshots, mouse/keyboard).experiment): Orchestrates A/B testing or parallel experiments for code generation.file_editor): Reads, writes, and patches files in the codebase.final_answer): A tool to provide a final answer to the user.git): Interacts with Git repositories.ha): Controls smart home devices via Home Assistant.llxprt_code): A specialized tool for code-related tasks.mcp): Provides agent introspection and self-control.mtac): Programmatically triggers and monitors containerized ML model fine-tuning loops.open_workers): Manages and interacts with open worker agents.openclaw): Send messages via OpenClaw Gateway to various channels (WhatsApp, Telegram, etc.).opencode): Interface for the OpenCode tool.orchestrator): Dispatches high-priority, complex jobs to the cluster.ouroboros): Manages and navigates the circular Ouroboros webring.planner): Plans complex tasks and executes them.power): Controls the cluster's power management policies.project_mapper): Scans the codebase to generate a project structure map.prompt_improver): A tool for improving prompts.rag): Searches the project's documentation to answer questions.search): Search the codebase for text patterns or file names.set_operational_mode): Dynamic operational mode selector and behavioral guideline loader (via SkillLibrary).shell): Executes shell commands (uses a persistent tmux session).smol_agent_computer): A tool for creating small, specialized agents.spec_loader): Clones external Git repositories (docs, specs) and ingests them into the agent's context.ssh): Executes commands on remote machines.submit_solution): Allows a Worker Agent to submit a code solution or artifact to be parsed by the ExperimentTool/Judge.summarizer): Summarizes conversation history.swarm): Spawns multiple worker agents to perform tasks in parallel.term_everything): Provides a terminal interface for interacting with the system.ternlight): Executes high-speed ternary embedding semantic lookups on documentation.vr): Tools for Virtual Reality interactions.web_browser): Enables web navigation and content interaction.Note on Implementation History: Previous versions of the agent relied on a hardcoded "Router" agent with a static list of tools. The current system has evolved to a dynamic, workflow-driven architecture (
TwinService+WorkflowRunner), enabling more complex and varied agent behaviors.
The agent is designed to function as a "Mixture of Experts." The primary
pipecat agent acts as a router, classifying the user's query and routing it to
a specialized backend expert if appropriate.
TwinService (or a SimpleLLMNode in the workflow)
classifies the user's query. If it determines the query is best handled by a
specialist (e.g., a 'coding' expert), it routes the request to that expert
service via Consul.deploy_expert.yaml Ansible playbook. For detailed instructions, see the
Advanced AI Service Deployment
section below.The personality and instructions for the main router agent are defined in ansible/roles/pipecatapp/files/prompts/router.txt. You can edit this file to customize the behavior of the main agent. Expert agents are configured via the group_vars/models.yaml file, where you can define the models they use.
There are two primary ways to interact with the conversational agent: the web interface and the Gemini CLI extension.
Navigate to the IP address of any node in your cluster on port 8000 (e.g., http://192.168.1.101:8000). The web UI provides real-time conversation logs, a request-approval interface, and the ability to save and load the agent's memory state.
For command-line users, a Gemini CLI extension is provided to send messages directly to the agent.
Install the Gemini CLI:
npm install -g @google/gemini-cli
Navigate to the extension directory:
cd pipecat-agent-extension
Install dependencies and build the extension:
npm install
npm run build
Link the extension to your Gemini CLI installation:
gemini extensions link .
Once the extension is linked, you can use the custom /pipecat:send command to send a message to the agent:
gemini /pipecat:send "Your message here"
Example:
gemini /pipecat:send "Can you write a python script to list files in a directory?"
The agent will process this message as if you had typed it in the web UI.
In addition to the agent's interface, you can access the dashboards for the underlying infrastructure services.
URL: http://<node_ip>:8500
Login: Access requires the SecretID (management token).
Retrieving the Token: Run this command on your controller node:
sudo cat /etc/consul.d/management_token
http://<node_ip>:4646The system is designed to be self-bootstrapping. The bootstrap.sh script (or the main playbook.yaml) handles the deployment of the core AI services on the primary control node. This includes a default instance of the llama-expert job and the pipecat voice agent.
If a job has been stopped, or you just want to verify that everything is running as it should be, you now use your new, lightweight playbook. It will skip all the system setup and only manage the Nomad jobs.
ansible-playbook playbooks/heal_cluster.yaml
If you make a change to a job file or need to restart the services from a clean state, it's best to purge the old jobs before running the start script again.
nomad job stop -purge llamacpp-rpc
nomad job stop -purge pipecat-app
The true power of this architecture is the ability to deploy multiple,
specialized AI experts that the main pipecat agent can route queries to. With
the new unified llama-expert.nomad job template, deploying a new expert is
handled through a dedicated Ansible playbook.
Define a Model List for Your Expert:
First, open group_vars/models.yaml and create a new list of models for your expert. For example, to create a creative-writing expert, you could add:
creative_writing_models:
- name: "phi-3-mini-instruct"
# ... other model details
Deploy the Expert with Ansible:
Use the playbooks/deploy_expert.yaml playbook to render the Nomad job with your custom parameters and launch it. You pass variables on the command line using the -e flag.
Example: Deploying a creative-writing expert to the creative namespace:
ansible-playbook playbooks/deploy_expert.yaml \
-e "job_name=creative-expert service_name=llama-api-creative namespace=creative model_list={{ creative_writing_models }} worker_count=2"
The TwinService in the pipecatapp will automatically discover any service
registered in Consul with the llama-api- prefix and make it available for
routing.
To support running large models on legacy hardware with limited RAM, the system supports Split Inference.
expert job can be configured to offload computation to rpc-server providers running on worker nodes.rpc-provider services via Consul and passes them to the llama-server using the --rpc argument. This allows the model's layers to be split across multiple machines, aggregating their memory and compute power.For high-throughput, low-latency execution of DeepSeek V4 models (Flash and PRO) on legacy CPU and GPU hardware, the cluster deploys the DwarfStar (ds4) inference engine natively.
ds4 Ansible role and Nomad templates (ds4-server.nomad.j2), it enables distributed GGUF model serving integrated into the Mixture of Experts namespace-isolated mesh.To optimize resource usage on legacy hardware, this project includes an intelligent power management system.
power_agent.py, uses an eBPF program (traffic_monitor.c) to monitor network traffic to specific services at the kernel level with minimal overhead.power.set_idle_threshold tool.This project includes a web-based dashboard for real-time display and debugging. To access it, navigate to the IP address of any node in your cluster on port 8000 (e.g., http://192.168.1.101:8000). The UI provides:
To protect node environments from deployment corruptions and accidental modifications, the cluster integrates Btrfs-level snapshot backups.
btrfs_snapshot role takes automated snapshots of critical folders (such as /opt/pipecatapp, /etc/consul.d, /etc/nomad.d) prior to running major playbooks.recover_os.py script executes rsync/rollback routines from the read-only pre-deployment snapshots to restore system state instantly.The cluster supports managing encrypted secrets (like API keys, database passwords) directly within the Git repository using SOPS and age encryption.
Ansible automatically decrypts variables from *.sops.yaml files during playbook runs, ensuring that sensitive data is secure at rest while enabling GitOps workflows.
Setup Age Key:
age-keygen -o ~/.config/sops/age/keys.txt
Update SOPS Config:
Update .sops.yaml at the root of the project to include the public key generated above.
Manage Secrets:
Create encrypted variable files in group_vars/ or host_vars/ (e.g. group_vars/secrets.sops.yaml).
sops group_vars/secrets.sops.yaml
The Pipecat cluster utilizes a Trusted Platform Module (TPM) to provide a unique, immutable identity for each node.
Instead of storing traditional SSH private keys (id_rsa) in plaintext on the filesystem, the tpm_ssh Ansible role generates an SSH key and securely seals it inside the physical TPM chip.
tpm-ssh-agent systemd service. It unlocks the TPM key on boot using a securely stored, randomly generated PIN (located at /etc/tpm_ssh_pins with 0600 root-only access), and provides a persistent SSH_AUTH_SOCK.The cluster employs an Identity-Based Debugging flow to manage the dynamic Tailscale (Headscale) mesh network, separating automated machine identities from human administrative access.
Rather than distributing static Tailscale Auth Keys, human operators must use a hardware security key (e.g., YubiKey) to enroll their external machines into the cluster. This relies on an SSH challenge to the controller node.
scripts/enroll-admin.sh helper script on your external machine.headscale.local.mesh Tailnet over HTTPS (routed via Traefik).To eliminate the dependency on external services (such as GitHub) for fetching authorized_keys, the cluster implements a purely offline key distribution model using the Consul Key-Value (KV) store.
http://127.0.0.1:8500/v1/kv/ssh-keys/<hostname>)./usr/local/bin/update-ssh-authorized-keys.sh every 5 minutes. This script fetches all public keys from the Consul KV and idempotently updates the local ~/.ssh/authorized_keys file.nomad node statusnomad job statusnomad alloc logs <allocation_id> or use the Mission Control Web UI.A dedicated health check job exists to verify the status of all running LLM experts. This provides a quick way to ensure the entire cluster is operational.
ansible-playbook playbooks/run_health_check.yamlnomad job logs health-checktests/scripts/ directory.This project uses a suite of linters to ensure code quality and consistency. For detailed instructions on how to install the development dependencies and run the checks, please see the Linting Documentation.
To run all linters, use the following command:
npm run lint
llama-expert.nomad job is configured via Ansible variables in group_vars/models.yaml. You can define different model lists for different experts.RealtimeSTT sensitivity can be tuned in app.py for better performance in noisy environments.pipecatapp.nomad job file.This project includes two types of benchmarks.
Measures the end-to-end latency of a live conversation. Enable it by setting BENCHMARK_MODE = "true" in the env section of the pipecatapp.nomad job file. Results are printed to the job logs.
Uses llama-bench to measure the raw inference speed (tokens/sec) of the deployed LLM backend. Run the benchmark.nomad job to test the performance of the default model.
nomad job run /opt/nomad/jobs/benchmark.nomad
View results in the job logs: nomad job logs llama-benchmark
For advanced users, this project includes a workflow for automatically improving the agent's core prompt using evolutionary algorithms. See prompt_engineering/PROMPT_ENGINEERING.md for details.
This section outlines the major feature enhancements and maintenance tasks planned for the future.
llama-expert.nomad job to include a final, lightweight fallback model to ensure the expert service always starts with a basic capability.sidecar_service in the Nomad job files and document the process and performance overhead.power_agent.py.target_user.e2e-tests.yaml to verify core agent functions.For solutions to common issues, such as failing Nomad service checks or deployment errors, please refer to the Troubleshooting Guide.
This project is licensed under the GNU General Public License v3.0.
The project supports a Hybrid / Cluster-Native architecture, which means it can be deployed in different configurations depending on the hardware available.
mid):
Runs all tools, inference (LLM), and core logic on a single node. Ideal for a standalone server with adequate resources.edge, core):
Distributes the workload across a cluster using Consul for service discovery.
node_tier in Ansible inventoryYou can specify the tier of a node by setting the node_tier variable in your Ansible inventory file.
Example inventory.yaml:
all:
children:
controllers:
hosts:
controller1:
ansible_host: 192.168.1.100
node_tier: core
workers:
hosts:
worker1:
ansible_host: 192.168.1.101
node_tier: edge
worker2:
ansible_host: 192.168.1.102
node_tier: mid
Alternatively, if you're using bootstrap.sh, you can specify the tier via the command line:
./bootstrap.sh --tier [edge|mid|core]
The cluster maintains hardware agnosticism by utilizing a distributed file system (unified_fs_mount_point) as the primary shared volume across all nodes, rather than relying on host-specific local storage configurations like RAID-5.
This guarantees seamless high availability (HA) for persistent state.
To optimize SSD usage and prevent unnecessary duplication of multi-gigabyte machine learning models, the IPFS daemon (kubo) is configured with the experimental FilestoreEnabled setting.
ipfs add --nocopy flag..tar images that are immediately deleted post-load, must not use --nocopy. Using --nocopy on files that will be deleted corrupts the IPFS datastore by breaking the underlying file references.Python
45.6%
CSS
17.7%
JavaScript
17.4%
Rust
8.2%
Shell
3.6%
Jinja
3.3%
HTML
3.0%