Skip to Content
⚠️ v0.1 — Early preview. APIs and schema may change.
SDK Reference

SDK Reference

The Zil SDK reads your project’s manifest.yaml, identity files, and adapter config, then wires them into a running agent. One function call replaces manual configuration.

zil.create_agent()

import zil agent = zil.create_agent()

This returns a fully-configured agent (the type depends on your framework — e.g., an ADK LlmAgent for the adk backend) with:

  • Name and description from manifest.yamlmetadata.name, metadata.description
  • Model resolved from adapters/llm.yaml → mapped to an ADK-compatible model string
  • Instruction composed from identity/persona.md + identity/instructions.md + identity/guardrails.yaml
  • Telemetry configured from observability/config.yaml (if endpoint set)
  • Guardrail engine loaded from identity/guardrails.yaml → attached as agent._zil_guardrails
  • Cost tracking initialized from spec.cost → accessible via zil.cost and agent._zil_cost
  • Environment config populated via zil.config from spec.env declarations
  • Sub-agents built from spec.agents — each with their own identity, model, MCP servers, and skills
  • Runtime dependencies parsed from spec.runtime.dependencies → accessible via ProjectContext.runtime_deps

How it works

manifest.yaml ├── metadata.name → agent name ├── metadata.description → agent description ├── spec.runtime.llm.adapter │ └── adapters/llm.yaml │ ├── provider → model prefix (e.g. "anthropic/") │ └── model → model id (e.g. "claude-sonnet-4-20250514") ├── spec.runtime.dependencies → ProjectContext.runtime_deps ├── spec.identity │ └── identity/ │ ├── persona.md → ┐ │ ├── instructions.md → ├→ composed instruction string │ └── guardrails.yaml → ┘ ├── spec.agents → sub-agent LlmAgents (AgentTool) └── spec.skills → SkillToolset (filtered per sub-agent)

Parameters

All parameters are optional — by default, everything is read from the project files.

ParameterTypeDefaultDescription
toolslist[Callable][]Tool functions to attach to the agent
project_dirstr | Pathauto-detectProject root (walks up from cwd looking for manifest.yaml)
namestrfrom manifestOverride agent name
descriptionstrfrom manifestOverride description
modelstrfrom adapterOverride model string
instructionstrcomposedOverride the entire instruction
thinking_budgetintfrom manifestToken budget for Gemini thinking mode. Enables chain-of-thought reasoning when set. Falls back to spec.thinking_budget in manifest
enable_telemetryboolTrueAuto-setup OTel tracing from observability/config.yaml
enable_guardrailsboolTrueLoad runtime guardrail engine from identity/guardrails.yaml
enable_cost_trackingboolTrueTrack token usage and enforce budgets from spec.cost
rawboolFalseReturn a WiredAgent wrapper instead of the framework-specific agent. Required when using zil.Session.

Adding tools

Tools are plain Python functions. Define them and pass them to create_agent():

import zil def lookup_order(order_id: str) -> dict: """Look up an order by its ID.""" return {"order_id": order_id, "status": "shipped"} def cancel_order(order_id: str) -> dict: """Cancel an order.""" return {"order_id": order_id, "cancelled": True} root_agent = zil.create_agent( tools=[lookup_order, cancel_order], )

The framework backend automatically generates tool descriptions from the function docstrings and type hints.

Model resolution

The SDK maps adapters/llm.yaml to ADK model strings:

ProviderModelADK string
anthropicclaude-sonnet-4-20250514anthropic/claude-sonnet-4-20250514
openaigpt-4oopenai/gpt-4o
vertex-aigemini-2.0-flashgemini-2.0-flash
gemini / vertex-aigemini-3.5-flashgemini-3.5-flash

Anthropic and OpenAI use the LiteLLM  prefix convention. Vertex/Gemini models are passed directly (ADK native).

Unknown providers fall through as provider/model.

Identity composition

The SDK composes a single instruction string from three files, separated by ---:

  1. persona.md — who the agent is (personality, expertise, tone)
  2. instructions.md — how the agent behaves (rules, format, boundaries)
  3. guardrails.yaml — hard rules, converted to natural-language directives

Guardrails are translated into explicit instructions:

  • hard_blocks → “You MUST follow these rules… Blocked topics: …”
  • escalation_triggers → “When condition: message”
  • output_constraints → “Maximum response length: N characters.”

Overrides

You can override any resolved value:

# Use a different model for testing agent = zil.create_agent( model="openai/gpt-4o-mini", ) # Enable Gemini thinking mode with a 2048-token budget agent = zil.create_agent( thinking_budget=2048, ) # Completely replace the instruction agent = zil.create_agent( instruction="You are a test agent. Always respond with 'OK'.", ) # Point to a different project agent = zil.create_agent( project_dir="/path/to/my-agent", )

Prerequisites

The SDK requires a framework backend. Install one via an optional extra:

# Google ADK (default) uv pip install 'zil-ai[adk]' # OpenHands uv pip install 'zil-ai[openhands]'

Projects scaffolded with zil init include the correct extra in requirements.txt automatically. See Frameworks for details on each backend.

Generated agent.py

When you run zil init my-agent, the generated my_agent/agent.py uses the SDK:

import zil root_agent = zil.create_agent( tools=[], # add your tool functions here )

The root_agent module-level variable is the convention for agent discovery. zil serve loads this entry point automatically. The agent lives inside a Python package (my_agent/) for proper module resolution.

Note: Zil manifests use kebab-case names (e.g., my-agent), but the module directory uses snake_case (my_agent) to be a valid Python package. The SDK handles this conversion automatically.


Multi-agent support

When spec.agents is declared in the manifest, create_agent() automatically builds sub-agents and attaches them as AgentTool instances on the root agent.

Each sub-agent gets:

  • Its own identity (persona, instructions, guardrails) from its identity directory
  • Its own model (resolved from llm.model / model_env_var or the root adapter)
  • A filtered subset of MCP servers from spec.tools.mcp_servers
  • A filtered SkillToolset from the skills/ directory
import zil # Sub-agents are automatically added as tools root_agent = zil.create_agent( tools=[my_custom_tool], ) # root_agent.tools includes: [my_custom_tool, AgentTool(vta), AgentTool(vtd)]

See Multi-Agent & HITL for the full manifest schema and advanced usage.


HITL SDK

The zil.sdk.hitl module provides primitives for pausing agent execution and waiting for human input:

from zil.sdk.hitl import request_human_input, HumanInputRequest async def approve_plan(plan: str, tool_context) -> str: request = HumanInputRequest( question="Please review this plan.", context={"plan": plan}, options=["approve", "reject", "modify"], ) response = await request_human_input(request, tool_context) if response.choice == "__pending__": return "Waiting for human approval." return f"Human responded: {response.choice}"

See Multi-Agent & HITL — HITL for the complete API reference.


Skills

When spec.skills is set in the manifest, the SDK discovers skill definitions from the skills/ directory and makes them available via ADK’s SkillToolset:

# Skills are loaded and attached automatically root_agent = zil.create_agent(tools=[...])

In multi-agent projects, skills are filtered per sub-agent based on spec.agents[].tools.skills. See Skills for the full guide.


Session API

The zil.Session class provides a framework-neutral interface for invoking agents programmatically, with streaming support:

import zil # Create the agent with raw=True to get a WiredAgent agent = zil.create_agent(tools=[...], raw=True) # Open a session session = zil.Session(agent=agent, workspace="/tmp/work") # Stream events in real time async for event in session.stream("What files are in this repo?"): print(f"[{event.type}] {event.text or event.tool_name}") # Or get an aggregated response response = await session.send("Summarize the README") print(response.text) # Final text print(response.events) # Full event list print(response.session_id) # Session identifier # Clean up session.close()

SessionEvent

FieldTypeDescription
type"text" | "tool_call" | "tool_result" | "error" | "done"Event kind
textstr | NoneText content (for text and error types)
tool_namestr | NoneTool name (for tool_call and tool_result)
argsdict | NoneTool arguments (for tool_call)
resultAny | NoneTool return value (for tool_result)
metadatadict | NoneBackend-specific data (token counts, session ID)

SessionResponse

FieldTypeDescription
textstrFinal concatenated text response
eventslist[SessionEvent]Full ordered event list
session_idstrSession identifier
token_usagedict | NoneToken consumption data

zil serve uses this same Session API internally. See Frameworks for event type details across backends.


ProjectContext

The SDK’s internal ProjectContext holds all resolved project configuration. Access it directly for advanced use cases:

from zil.sdk.loader import load_project ctx = load_project() # Core fields ctx.project_dir # Path — project root ctx.manifest # dict — raw manifest.yaml ctx.identity # IdentityContext — persona, instructions, guardrails ctx.llm_adapter # dict — resolved LLM adapter config ctx.observability # dict | None — observability/config.yaml ctx.env_declarations # list[dict] — spec.env entries ctx.cost_config # dict | None — spec.cost ctx.tools_config # dict | None — spec.tools (MCP servers, host deps) ctx.agents # list[AgentSpec] — spec.agents entries ctx.service_config # dict | None — spec.runtime.service ctx.skills_dir # Path | None — resolved spec.skills directory ctx.thinking_budget # int | None — spec.thinking_budget (Gemini thinking mode) ctx.runtime_deps # list[dict] — spec.runtime.dependencies entries

runtime_deps format

Each entry in ctx.runtime_deps is a dict with:

{"name": "nodejs", "type": "apt-nodesource", "version": "20"} {"name": "pnpm", "type": "npm-global"} {"name": "git", "type": "apt"}

See Runtime Dependencies for the full specification.