Skip to content

tulip agents · the control runtime for agents that act

Agents that act. Safe by construction.

Tulip is an open-source agentic harness with one hard rule: the model never holds the trigger. The agent decides to act — issue the refund, ship the deploy, change the account — and the action runs only after your policy clears it, in code the model can't reach. Build agents on it, or govern the ones you already run in LangChain, CrewAI, or the OpenAI Agents SDK.

No install — the live workbench runs in your browser with your own model key.

pip install "tulip-agents[anthropic]"
from tulip import Agent, tool

@tool
def search_flights(
    origin: str, dest: str, date: str
) -> list[dict]:
    "Find flights between two cities."
    return flights.search(origin, dest, date)

# A model is a string; a tool is a function.
agent = Agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search_flights],
    system_prompt="You are a travel agent.",
)

print(agent.run_sync(
    "Cheapest flight Lisbon to Berlin Friday?"
).text)

Control in the core

A model can be brilliant and still be talked into the wrong action. That's a control problem, not an intelligence problem — so Tulip puts the control in code the model can't reach:

  • The router picks which shape runs — deterministically. The model classifies the task; it never authors the topology.
  • GSAR scores every claim against typed evidence — below threshold the agent regenerates or abstains, never guesses.
  • The admission gate clears every side-effecting call: admit() allows it, holds it for a human, or denies it — and records the decision either way, on a trail where editing any entry breaks verify().
from tulip.control import (
    Action, AuditTrail, ControlPolicy, admit, AdmissionError,
)

policy = ControlPolicy(require_human_for={"production"})
trail = AuditTrail()

async def safe_refund(order_id: str, usd: float):
    try:  # the gate runs before money moves
        return await admit(
            Action(name="refund", asset=order_id,
                   kind="payment", environment="production"),
            lambda: payments.refund(order_id, usd),
            policy=policy, trail=trail,
        )
    except AdmissionError:
        return "Held for a human — not run."

A prompt rule is advisory — the model can be argued out of it. The gate is structural — the wrong action isn't caught in a filter, it never runs. How this compares to prompt rules and guardrails →

Govern the agents you already run

You don't rebuild anything. Wrap a tool once with tulip-frameworks and drop it back in — same name, same schema — and every call now goes through the same gate and audit trail:

from tulip.control import Action, AuditTrail
from tulip_frameworks.langchain import gate_langchain_tool
from tulip_frameworks.policy_presets import action_gate_policy

safe_refund = gate_langchain_tool(
    refund,  # your existing LangChain @tool, unchanged
    action=lambda name, a: Action(
        name=name, asset=a["order_id"],
        kind="payment", environment="production",
    ),
    policy=action_gate_policy(),  # production → held for a human
    trail=AuditTrail(),
)

Bridges ship for LangChain, LangGraph, CrewAI, the OpenAI Agents SDK, LlamaIndex, and Google ADK.

What you get

  • A real agent framework


    One Agent class — tools, memory, RAG, streaming — over vendor-neutral backends. Swap models with a string.

  • Cognitive router


    A plain-language task compiles to the right shape — direct answer, pipeline, fan-out, debate, or a gated action.

  • Multi-agent workflows


    Sequential, parallel, loop, graph, orchestrator, swarm, handoff, and cross-process A2A — one Agent class, one event stream.

  • Grounded by construction


    ground_finding() emits a typed result only above the GSAR threshold — else an auditable Abstention, never a guess.

  • Gate + human-in-the-loop


    require_human_for pauses the actions that matter and resumes on a human's decision. Approvals survive restarts.

  • Audit trail by default


    Every call, verdict, and approval is a typed, hash-chained event — verify() fails on any edit. Replay any run.

Build it across any domain

Every example is a single self-contained file under examples/ with a matching docs page.

You're building… Start here
A support / ops agent that acts human-in-the-loop approvals · incident response
An agent on your own data (RAG) RAG basics · RAG agents
A multi-agent workflow swarm / war-room · supervisor + critic
A task-routed agent cognitive router · procurement approval
A security / AI-safety agent GSAR grounding · injection guardrails

Full catalog → Notebooks index · Capabilities matrix · API reference

When Tulip is overkill

If your agent only reads and summarizes, you may not need an admission gate yet — the control layer earns its keep the moment an action can cost something.

Start building

pip install "tulip-agents[openai]"

Get started → Try it live ↗ Why Tulip →


The open-source agentic harness — control the action, prove what it did. Safe by construction. Apache-2.0.