March 27, 2026 · 7 min read

From Zero to Production-Safe AI Agent in 15 Minutes

Install expacti, wire up a LangChain agent, approve your first command, whitelist a pattern, and ship it. No prior setup required.

You have an AI agent that needs to run shell commands, call APIs, or modify infrastructure. You know that letting it run unsupervised is a bad idea. But adding a proper approval layer sounds like a weekend project.

It is not. This tutorial walks you through the entire flow in 15 minutes flat. By the end you will have a LangChain agent connected to expacti, a reviewer dashboard where humans approve or reject commands, and a whitelist so trusted patterns never block the agent again.

Let's go.

1 Install expacti

The fastest way to get expacti running locally is with Docker Compose. We ship an evaluation config that starts the backend, the web dashboard, and a SQLite database in one command.

# Clone the repo and start the evaluation stack
git clone https://github.com/expacti/expacti.git
cd expacti
docker compose -f docker-compose.eval.yml up -d

After a few seconds you will have the backend listening on localhost:7400 and the reviewer dashboard on localhost:7401. No external dependencies, no cloud account required.

Tip If you prefer to skip Docker entirely, install the Python SDK directly. It includes a lightweight embedded server for local development.

pip install expacti

The SDK ships with a CLI. Run expacti config generate to scaffold a starter configuration file that points at localhost:7400. You can edit this later to add webhook URLs, Slack integrations, or multi-reviewer policies.

2 Connect your first AI agent

Expacti sits between your agent and the commands it wants to execute. Instead of calling subprocess.run() directly, you call expacti.execute(). The SDK handles the round-trip: it sends the command to the backend, waits for approval, and returns the result.

Here is a minimal LangChain agent with expacti wired in. Ten lines of integration code, everything else is standard LangChain.

import expacti
from langchain.agents import initialize_agent, AgentType
from langchain.chat_models import ChatOpenAI
from langchain.tools import Tool

client = expacti.Client(
    endpoint="http://localhost:7400",
    agent_id="deploy-bot",
    api_key="eval-key-local",
)

def safe_shell(command: str) -> str:
    """Run a shell command through expacti approval."""
    result = client.execute(command, context={"source": "langchain"})
    return result.stdout

shell_tool = Tool(
    name="shell",
    func=safe_shell,
    description="Run a shell command on the server. Requires human approval.",
)

llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = initialize_agent(
    tools=[shell_tool],
    llm=llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True,
)

agent.run("Check disk usage on the server and report any partitions over 80%.")

That is it. The agent thinks it is running shell commands. In reality every command passes through expacti first. If no whitelist rule matches, the command enters a pending state and the agent blocks until a human reviewer makes a decision.

3 Open the reviewer dashboard

Open http://localhost:7401 in your browser. The dashboard shows all connected agents, pending commands, and recent activity. If you started expacti with Docker Compose the dashboard is already running. If you installed via pip, start it with:

expacti dashboard --port 7401

You will see your deploy-bot agent listed under connected agents. No commands yet because we have not triggered one. Let's fix that.

4 Run a command and watch it appear

Run the agent script from Step 2. The LLM will decide to run something like df -h to check disk usage. Instead of executing immediately, you will see the command appear in the dashboard under Pending Review.

The dashboard shows:

Meanwhile your Python script is blocking on client.execute(). The SDK polls the backend every 500ms by default, waiting for a verdict. The agent is paused but not crashed. It will resume the moment you act.

Note In production you would configure a webhook or Slack notification so reviewers do not need to keep the dashboard open. See the webhook docs for setup instructions.

5 Approve it — agent continues

Click the green Approve button in the dashboard. Within a second, the backend notifies the SDK, the command executes on the host, and the result flows back to your agent. The LLM receives the output of df -h and continues reasoning.

If the command looked suspicious — say the agent tried rm -rf / instead — you would click Reject. The SDK raises an expacti.CommandRejectedError, which you can catch in your tool function and return a safe message to the LLM:

def safe_shell(command: str) -> str:
    try:
        result = client.execute(command, context={"source": "langchain"})
        return result.stdout
    except expacti.CommandRejectedError:
        return "Command was rejected by a human reviewer. Try a different approach."

This keeps the agent in the loop without giving it a stack trace or sensitive error details.

6 Whitelist the pattern — never ask again

Approving every df -h gets old fast. Expacti lets you create whitelist rules so safe, predictable commands auto-approve. You can do this from the dashboard or with a YAML config file.

# expacti-rules.yml
rules:
  - id: disk-usage-read
    description: "Allow read-only disk usage checks"
    pattern: "^df\\s"
    agent_ids: ["deploy-bot"]
    action: auto_approve

  - id: list-files
    description: "Allow listing directory contents"
    pattern: "^ls\\s"
    agent_ids: ["*"]
    action: auto_approve

  - id: block-destructive
    description: "Never allow rm -rf on root"
    pattern: "^rm\\s+-rf\\s+/"
    agent_ids: ["*"]
    action: reject

Load the rules into expacti:

expacti rules apply -f expacti-rules.yml

From now on, when deploy-bot runs df -h, the command auto-approves and executes in under 50ms. No human in the loop, no latency hit. But anything matching the destructive pattern is instantly rejected without ever reaching a reviewer.

Rules are versioned. Every change creates a new version with a timestamp and the author who applied it. You can view the full history with expacti rules history and roll back to any previous version if needed.

Tip Start with a small whitelist and expand it over time. The dashboard shows which commands are being approved most often, so you can identify candidates for new rules at a glance.

7 What to do next

You now have a working approval layer. Here is where to go from here.

Anomaly detection

Enable the anomaly detection module to flag commands that are technically whitelisted but statistically unusual. For example, ls is safe, but ls running 500 times in 10 seconds is probably a bug. Anomaly detection catches these patterns and escalates them to a reviewer automatically.

# Add to your expacti config
anomaly_detection:
  enabled: true
  sensitivity: medium
  window_seconds: 60
  max_commands_per_window: 50

Multi-reviewer policies

For high-stakes commands — database migrations, infrastructure changes, production deployments — require approval from two or more reviewers. Define policies per agent, per command pattern, or per environment.

policies:
  - pattern: "^(terraform|kubectl) apply"
    min_approvals: 2
    reviewer_groups: ["platform-team"]
    timeout_minutes: 30

CI/CD gates

Integrate expacti into your deployment pipeline. Use the GitHub Actions approval gate to require human sign-off before an agent-driven deployment proceeds. The gate blocks the workflow until the configured number of approvals are received through the expacti dashboard or Slack.

# .github/workflows/deploy.yml
- name: Request deployment approval
  uses: expacti/approval-gate@v1
  with:
    endpoint: ${{ secrets.EXPACTI_ENDPOINT }}
    api_key: ${{ secrets.EXPACTI_API_KEY }}
    command: "kubectl apply -f k8s/production/"
    min_approvals: 2
    timeout_minutes: 15

Audit trail

Every command, approval, rejection, and whitelist change is logged with full context. Export the audit log to your SIEM or use the built-in search to investigate incidents. When your security team asks "what did the agent do last Tuesday?" you will have a precise answer.

See it in action

Try the live demo with a pre-configured agent and reviewer dashboard. No signup required.

Launch demo    Read the docs

Fifteen minutes. That is all it takes to go from a bare-metal agent to one with proper human oversight, auditable approvals, and a whitelist that grows smarter over time. The gap between "demo" and "production" is smaller than you think.