Skip to main content

Core primitives

The two main primitives are create_agent(...) and @agent_tool(...). Use them when you want to define everything directly in Python.

Wrap an existing framework agent

Already have an OpenAI Agents / LangChain / Google ADK / Pydantic AI agent? Wrap it once and you’re done:
That’s it. You get:
  • Tool-call enforcement at every tool boundary (PolicyEnforcer.decide())
  • Role resolution from the active context’s user_roles at call time (every role evaluated, most permissive wins)
  • Per-request biscuit attenuation
  • Langfuse traces tagged with the caller’s identity
See Framework adapters for the per-framework wrapping details. Whichever way you define the agent, the policy can live in your code or on the platform. To let operators own and edit it in the dashboard without a redeploy (register → edit → test → deploy), see the platform workflow.

Env vars: that is the whole config surface

No config object to instantiate, no enforce_policy(...) call to remember on the platform path. The adapter / loader threads it all through. The full list of env vars lives in the environment reference. Connecting to Hexgate. The key and the URL are coupled: a fty_live_… key only verifies against the platform instance that minted it.
  • Hexgate Cloud (default): set HEXGATE_API_KEY to the key from app.hexgate.ai. Leave HEXGATE_API_URL unset — it defaults to the cloud, so this is the zero-infra path.
  • Self-hosted / local platform: additionally set HEXGATE_API_URL=http://localhost:8000 (or your host), and use a key minted by that platform.

Two carve-outs worth knowing

  1. Per-call identity stays explicit. HexgateContext is the one piece the adapter can’t infer from env, because it’s per-request, not per-process. One line wrapping each call (hexgate_context=HexgateContext(...) kwarg on adapters, async with HexgateContext(...) for native). See Request context.
  2. approval_required tools. If the policy uses that mode, dev decides what happens — pass approval_handler= (True / False / callable) when wrapping. The CLI default is ask for both commands: hexgate chat prompts the TTY, and hexgate serve routes the request to the connected Playground (pass --approval-mode auto-approve for headless runs). See Approval-required tool calls.
Everything else — fetch, verify, hot-reload, role selection, signature check, decision rendering, tracing — the runtime handles.

Define agents in code and resolve them by name

If you want the CLI and shared loader to resolve a create_agent(...) agent by name, register it first and then load it through load_agent(...). A small end-to-end example registry lives in examples/file_agents.py and examples/research_agents.py. It demonstrates:
  • building one agent with create_agent(...) only
  • building another with create_agent(...) plus enforce_policy(...)
  • building a research agent with approval-gated file writes via enforce_policy(..., approval_handler=...)
  • registering it with register_agent_factory(...) and loading it through load_agent(...)

Local agents

The CLI discovers local agents from ./<agent_dir>/agent.yaml, ./agents/<agent_dir>/agent.yaml, and ./examples/<agent_dir>/agent.yaml. This repo ships a demo agent at examples/example_agent/, so from the project root:

Stream results

For direct Python usage, the simplest runtime path is:
stream_agent(...) yields normalized events for assistant text deltas, tool lifecycle, and final run completion.