Core primitives
The two main primitives arecreate_agent(...) and @agent_tool(...). Use them
when you want to define everything directly in Python.
Two shapes
Devs pick one of two shapes. Both end up at the same enforcement seam — they differ only in where the policy comes from.Shape A — “I have an existing framework agent”
Dev wrote an OpenAI Agents / LangChain / Google ADK / Pydantic AI agent. They wrap it once and they’re done:- Tool-call enforcement at every tool boundary (
PolicyEnforcer.decide()) - Role resolution from the active
User.roleat call time - Per-request biscuit attenuation
- Langfuse traces tagged with the caller’s identity
Shape B — “I want the platform to own the agent’s YAML”
Dev authored the agent’sagent.yaml / policy.yaml / system.md in the
dashboard. The SDK fetches them:
User scope. The difference is whose system of
record holds the YAML — the dev’s code vs the dashboard. For the full
platform-owned path (register → edit → test → deploy), see the
platform workflow.
Env vars: that is the whole config surface
| What dev sets | What changes |
|---|---|
HEXGATE_API_KEY=fty_live_<project>_… | Wakes up the platform path. Without it, adapters / load_agent fall back to local / registered. |
HEXGATE_API_URL=https://app.hexgate.ai (optional) | Platform endpoint. Defaults to Hexgate Cloud. Set to http://localhost:8000 only when self-hosting locally — your key must be minted by whichever platform this points at. |
HEXGATE_LOCAL_POLICY=./policy.yaml or ./bundle/ | Dev escape hatch: enforce a policy from disk, hot-reload on save. Wins over the platform’s bundle. |
HEXGATE_BUNDLE_SIGN_KEY_PATH=./keys/dev.private (optional) | Sign locally-recompiled yaml so bundle.is_signed reads True. |
HEXGATE_BUNDLE_PUBKEY_PATH=./keys/prod.public (optional) | Verify a pre-built bundle dir against this pubkey on every reload. |
HEXGATE_BUNDLE_REQUIRE_SIGNATURE=true (optional) | Strict mode — refuse any unsigned or unverifiable bundle at startup. |
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_KEYto the key from app.hexgate.ai. LeaveHEXGATE_API_URLunset — 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
- Per-call identity stays explicit.
Useris the one piece the adapter can’t infer from env, because it’s per-request, not per-process. One line wrapping each call (user=User(...)kwarg on adapters,async with User(...)for native). See User scope. approval_requiredtools. If the policy uses that mode, dev decides what happens — passapproval_handler=(True / False / callable) when wrapping. Default forhexgate serveis auto-approve; forhexgate chatit prompts the TTY. See Approval-required tool calls.
Define agents in code and resolve them by name
If you want the CLI and shared loader to resolve acreate_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(...)plusenforce_policy(...) - building a research agent with approval-gated file writes via
enforce_policy(..., approval_handler=...) - registering it with
register_agent_factory(...)and loading it throughload_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.