Agent Development Guide
Version française : ../../fr/guides/agent-development.md
This guide shows how to write, validate, test, register, run, version and retire an agent of the Navin Platform.
It is for developers with the developer or tenant_admin role; suspending and killing an agent needs
tenant_admin or security_officer. Count about one hour for a first agent from the template.
Concept
An agent is a versioned manifest of kind Agent plus code run by the Navin runtime. The Control Plane stores every
version of the manifest, starts the runs, applies the policies and the budget, and records each step. The agent never
calls a model provider directly: every model call goes through the AI gateway, every tool call through the tool
gateway, and every secret stays in the Credential Broker.
The life cycle of an agent is: active (new runs accepted), suspended (reversible kill switch, new runs refused)
and killed (final, can never be resumed).
Manifest
An agent is described by a manifest of kind Agent (contracts/v1/agent-manifest.schema.json): runtime (engine, entrypoint, limits), models (effort, max_output_tokens), memory, permissions (data_classification_max, scopes), autonomy (level, max_steps, max_subagents, max_depth, human_approval_for), evaluation and observability. The template sdks/python/src/enterprise_ai/templates/agent-basic/agent.yaml is a valid example.
enterprise-ai create agent invoice-agent --template rag-agent
enterprise-ai validate -f invoice-agent/agent.yaml
Manifest fields
| Field | Type | Required | Description |
|---|---|---|---|
apiVersion | string | yes | Always enterprise-ai.io/v1. |
kind | string | yes | Always Agent. |
metadata.name | string | yes | DNS like identifier, lowercase, up to 128 characters. |
metadata.version | semver | yes | Version of the manifest; a published version is immutable. |
metadata.owner | string | yes | Accountable team or e-mail (3 to 200 characters). |
metadata.description, metadata.labels | string, map | no | Free description and labels (the environment label is set by deploy). |
spec.description | string | yes | What the agent does. |
spec.purpose | string | no | Business purpose, passed to the agent.run policy. |
spec.risk_level | enum | no | minimal, limited, high or unacceptable (refused at registration). |
spec.identity | object | no | spiffe_id and service_account of the workload. |
spec.runtime | object | yes | engine (langgraph, temporal, custom), entrypoint, image, sandbox (none, gvisor, kata, wasm), limits. |
spec.models | array | yes | Each item: model_id, role (primary, fallback, summarizer, embedding, judge, vision, speech), effort (none to max), max_output_tokens. |
spec.tools | array | no | Each item: ref (such as tool:crm-search@1.2.0), scopes, requires_approval. |
spec.mcp_servers, spec.skills, spec.connectors, spec.knowledge_bases, spec.channels | array | no | Typed references to the objects the agent may use; a skill not listed here is refused by the Skill Loader. |
spec.memory | object | no | session, persistent, ttl_days. |
spec.permissions | object | yes | data_classification_max (public < internal < confidential < restricted < secret), scopes, allowed_regions. |
spec.autonomy | object | yes | level (assisted, supervised, autonomous), max_steps, max_subagents, max_depth, human_approval_for. |
spec.subagents | array | no | References of the agents it may delegate to. |
spec.budgets | object | no | max_cost, max_input_tokens, max_output_tokens, max_requests, max_tool_calls, max_duration_seconds, period. |
spec.schedules, spec.triggers | array | no | Planned and event driven starts. |
spec.evaluation | object | no | suites (references) and block_on_failure; read by the quality gate of deploy. |
spec.observability | object | no | trace_sampling (0 to 1) and log_prompts (none, hashed, redacted, full). |
Before you begin
- The Python SDK and its CLI are installed and logged in:
enterprise-ai doctorreports no FAIL. - Your account has the
developerrole in the tenant, and the scopeagents:runto start runs. - The model you reference is approved in the model registry of the tenant.
Steps
-
Create the project from a template. The template gives a manifest, the code, a versioned prompt, an OPA policy and tests that already pass; starting from it avoids an invalid first manifest.
enterprise-ai create agent invoice-agent --template agent-basicThe templates are
agent-basic,rag-agentandsecure-agent(an alias ofagent-basic).Expected result: the command lists the created files and ends with the manifest marked valid against
agent-manifest.If it fails: exit code 1 with "exists" means the folder is already there; add
--forceor pick another name. -
Edit the manifest. Set
spec.description, the model, the scopes and the autonomy. Keeppermissions.data_classification_maxandscopesas small as the task allows: they bound every skill, tool and connector the agent can load. You can also edit it in Agents > Registry > agent > Manifest.Expected result: the manifest describes only what the agent needs.
If it fails: see the field table above; a skill or tool that the agent will call must be listed in
spec. -
Validate the manifest. The CLI checks it against the v1 contract offline, before any call to the Control Plane.
enterprise-ai validate -f invoice-agent/agent.yamlExpected result: exit code 0, the document is valid.
If it fails: exit code 1 lists each invalid field with its JSON path; fix it and run the command again.
-
Test locally. Run the unit tests and the OPA tests of the project, then an evaluation suite on the entrypoint.
cd invoice-agentpython -m pytest -q testsenterprise-ai test authzenterprise-ai test -f suite.yaml --entrypoint agent:buildsuite.yaml: an evaluation suite of the project (cases and metrics). See the testing guide.Expected result: every test passes and the suite reports
passed.If it fails:
enterprise-ai testreturns 1 when the evaluation is below target, or whenopaorfgais not installed. -
Register the agent. POST /v1/agents creates the agent or adds a version and makes it current. The caller becomes the ReBAC owner.
enterprise-ai apply -f agent.yamlExpected result:
apply_statusiscreated,version_addedorunchanged.If it fails: 422
invalid_manifest(contract), 422agent_risk_unacceptable(risk level), 409version_immutable(same version, other content: raisemetadata.version). -
Deploy through the quality gate.
deployvalidates, applies, runs the gate POST /v1/evaluations/gates/check on the suites ofspec.evaluation, then sets theenvironmentlabel.enterprise-ai deploy agent -f agent.yaml --env stagingExpected result: the agent is deployed in
staging.If it fails: exit code 1 means the quality gate refused the promotion; the gate report is printed.
-
Start a run. POST /v1/agents/{agent_id}/runs creates a run and returns its Execution Contract. The policy
agent.runis evaluated first and the manifest budget is applied. In the portal: Agents > Registry > agent > Start run.enterprise-ai run start invoice-agent --task '{"invoice_media_id": "med_01HZX3K9"}' --followenterprise-ai runs list --agent agt_ap --state runningExpected result: the run goes
pending,running, thensucceeded;--followstreams its events.If it fails: 409
agent_not_active, 403policy_denied, 422invalid_budget; with--follow, exit code 1 means the run endedfailed,cancelledortimed_out. -
Publish a new version. Raise
metadata.version, validate, then apply again. GET /v1/agents/{agent_id}/versions lists every version, newest first, withcurrentset on the one in use.Expected result:
apply_statusisversion_added; new runs use the new version.If it fails: 409
version_immutablemeans the version number was not raised. -
Suspend, resume, kill or delete. POST /v1/agents/{agent_id}/suspend moves the agent and its active runs to
suspended. POST /v1/agents/{agent_id}/resume opens an approval (202pending_approval). POST /v1/agents/{agent_id}/kill cancels every active run and ends the agent for good. DELETE /v1/agents/{agent_id} removes the agent and its versions. In the portal: Agents > Registry > agent > Suspend, Resume or Kill.client.agents.suspend("agt_ap", reason="incident INC-42")client.agents.resume("agt_ap")Expected result: the answer lists
affected_runs.If it fails: 409
agent_killed(already killed), 409kill_switch_active(a SOC containment holds the agent), 409agent_has_active_runson delete.
Kill is final: a killed agent can never be resumed. Use suspend for an investigation.
Register and run
POST /v1/agents creates the agent, GET /v1/agents/{agent_id}/versions lists its versions, POST /v1/agents/{agent_id}/runs starts a run, POST /v1/runs/{run_id}/heartbeat reports a long run (action stop: checkpoint then exit), POST /v1/agents/{agent_id}/suspend suspends it.
import yaml
agent = client.agents.create(yaml.safe_load(open("agent.yaml")))
run = client.agents.run("agt_ap", task={"invoice_media_id": "med_01HZX3K9"}, idempotency_key="inv-2291")
run = client.runs.get(run["id"])
A repeated Idempotency-Key returns the first run. A long run sends heartbeats: the answer action is continue, or
stop with a reason (run_cancelled, run_terminal, run_suspended, agent_killed, agent_suspended). On
stop, write a checkpoint and exit; POST /v1/runs/{run_id}/resume restarts it with from_last_checkpoint,
restart_step or restart_run.
Rules
No direct call to a model provider: everything goes through the gateway. No secret in the code or in the manifest. A sub-agent always receives a restricted delegation. The Navin runtime is AgentLoop, AgentRunner, SubagentManager, RunPlan and MessageBus; LangGraph and Temporal are optional adapters.
enterprise-ai deploy agent -f agent.yaml --env staging
enterprise-ai runs list --agent agt_ap --state running
Verify
enterprise-ai agent inspect invoice-agent shows the agent as the Control Plane stores it: status active, the
manifest of the current version and runs, the number of runs per state.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
422 invalid_manifest | The manifest does not match contracts/v1/agent-manifest.schema.json. | Run enterprise-ai validate -f agent.yaml and fix the fields listed. |
422 agent_risk_unacceptable | spec.risk_level is unacceptable. | Such an agent cannot be registered; reduce its scope. |
409 version_immutable | The version exists with another content. | Raise metadata.version. |
409 agent_not_active | The agent is suspended or killed. | Resume it (approval) or register a new agent. |
403 policy_denied | The agent.run policy refused the run. | Read the decision in Security and governance; check the risk, purpose and classification. |
422 invalid_budget | The request tried to loosen the manifest budget. | A request can only tighten budgets. |
409 idempotency_conflict | The same Idempotency-Key was used for another agent. | Use one key per logical request. |
409 subagent_depth_exceeded, subagent_count_exceeded | Delegation beyond max_depth or max_subagents. | Raise the autonomy limits or reduce the delegation. |
409 run_not_resumable | The run failed on budget_exceeded, max_steps_exceeded, killed or approval_rejected. | Resume with restart_run. |
409 no_checkpoint | No checkpoint to resume from. | Use restart_step or restart_run. |