Agent API
Backbone Mode
Run the Agent API as a bare LLM backend: mode:"model", the builtin_tools allowlist, and the full tool_choice contract.
Backbone mode turns POST /v1/responses into a bare inference backend — an OpenAI-compatible LLM with client function tools, but no server-side agent runtime, tools, planning, or stored state. Use it when your own agent framework (LangChain, openai-agents, a custom loop) is the orchestrator and Mirobody is “the model.”
The default (mode: "agent") is the whole Mirobody agent: server-side runtime, built-in health-data tools, planning, and stored conversations.
All /v1 endpoints share one base URL — pick the cluster your account uses:
https://api.mirobody.ai/v1 # Globalhttps://api.mirobody.cn/v1 # ChinaJapan and EU clusters are in preparation — see Regions. Model providers and pricing can differ by region, so read GET /v1/models from the cluster you call.
{ "mode": "agent" | "model" } // default "agent"mode:"agent" (default) | mode:"model" (backbone) | |
|---|---|---|
| Runtime | planning, sub-agents, code eval, virtual filesystem | single bare-model inference |
| Server tools | built-in domain tools (trim with builtin_tools) | none |
| System prompt | full agent base (~10k tokens) | minimal safety base (<100 tokens) |
| State | previous_response_id / session_id threads | stateless (both → 400) |
| Continuation | stateful resume or stateless full replay | stateless full replay only |
| Client tools | handoff via function_call (server-issued call_id) | bound directly to the model (call_id preserved) |
| MCP tools | supported (server-executed) | 400 (no server tool loop) |
| Server-side thread state | yes (deleted at request end when store=false) | none |
text.format | 400 (not supported) | supported |
With store=true, model mode still persists the response object (GET /v1/responses/{id} works), but the conversation is tagged as model-mode: a later previous_response_id against it returns an explicit 400 (“replay the transcript to continue”) rather than silently resuming an empty thread.
curl https://api.mirobody.ai/v1/responses \ -H "Authorization: Bearer $MIROBODY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "mirobody-flash", "mode": "model", "builtin_tools": "none", "input": "Summarize the attached lab panel.", "user": "alice" }'builtin_tools
Section titled “builtin_tools”{ "builtin_tools": "auto" | "none" | ["query_health_data", ...] } // default "auto"Controls the server-side domain-tool family — the health, clinical-record, and external-evidence tools:
query_health_data · list_clinical_records · list_family_members · search_medical_evidence · read_source
The external-evidence surface accepts only search_medical_evidence and read_source. Arbitrary URL fetching is not available: read_source accepts refs returned by search (pmid:, pmc:, nct:, or doi:), not model-constructed URLs.
| Value | Effect |
|---|---|
"auto" (default) | All domain tools available — current behavior. |
"none" | Domain tools all hidden. Required when your backbone caller brings its own data or tools — otherwise the model prefers to query the (empty) Mirobody Subject and skips your client tool. |
["name", ...] | Allowlist. An unknown name returns 400 invalid_value and lists the available set. |
The server-side orchestration primitives (write_todos, filesystem, task, eval) belong to agent mode itself, outside this parameter’s scope — they may still appear in tool_steps. For a zero server-tool trace, use mode:"model".
tool_choice
Section titled “tool_choice”tool_choice follows OpenAI semantics, with model mode adding hard guarantees the agent runtime cannot make:
tool_choice | mode:"model" | mode:"agent" (default) |
|---|---|---|
omitted / "auto" | Model decides | Model decides |
"none" | Plain text (auto-drops into model mode) | Same, when stateless; with previous_response_id/session_id → 400 |
"required" | Guarantees the output has at least one client function_call | 400 unsupported_parameter (the agent runtime can’t force a client-tool call) |
{"type":"function","name":X} | Guarantees only X is called (stray calls pruned) | Accepted but best-effort (not forced) |
required / named + empty tools | 400 invalid_value, param="tool_choice" | Same |
Forced tool calls are a true guarantee
Section titled “Forced tool calls are a true guarantee”required / a named tool is a hard guarantee, not a hint — if it can’t be honored you get a 502, never a silent downgrade. In streaming, a forced call is synthesized into the standard event sequence (response.output_item.added → response.function_call_arguments.delta/.done → response.output_item.done), identical to the non-streaming behavior.
This is verified against LangChain create_agent with both ToolStrategy(...) (which auto-sends tool_choice:"required") and ProviderStrategy(...).
Provider error classification (model mode)
Section titled “Provider error classification (model mode)”In model mode, provider exceptions are classified by retryability instead of a blanket 502:
| Provider side | Returned | Meaning |
|---|---|---|
400 / 404 / 413 / 422 (params, over-length) | 400 invalid_request_error | The provider’s actionable message is passed through (truncated). Do not retry. |
429 | 429 rate_limit_error | Retry-After passed through or synthesized. |
401 / 403 (platform-side credentials / quota) | 502 upstream_error | A platform fault, not your request — no internal detail leaked. |
5xx / timeout / transport | 502 upstream_error | Error type only; full text is logged with an X-Request-Id. |
See also
Section titled “See also”- Structured output —
text.format(model mode only). - Function calling — declaring client tools and the two continuation paths.
- Streaming —
response.*events, including the server-tool side-channel. - Rate Limits & Quota — per-key rate limit and the monthly account quota.