Skip to main content

Example: an agent function, and the code function it will call

Synced from bicycle-studio-api

Source: studio_guide("example-agent") (GET /api/studio/v1/guide/example-agent) rendered at origin/platform (1afd83f), synced 2026-09-28. Do not edit this page here; change the source and run yarn sync:studio.

Build this when the answer needs a few model steps and, later, your own functions as tools. A tiny code function works out order KPIs; an agent explains a day's numbers in two sentences.

Person-only stops: the first publish of demo_kpis (with expose.agents turned on) and the first publish of demo_explainer. A person with publish rights on functions (the owner or an admin) confirms each in Studio from the link function_publish answers. A token cannot confirm.

Files​

demo_kpis/function.json

{
"schema": "bicycle.function/v1",
"name": "demo_kpis",
"kind": "code",
"title": "Order KPIs from two counts",
"entrypoint": "main:handler",
"image": "bda-python:3",
"mode": "sync",
"timeout_ms": 10000,
"resources": {
"class": "fn-xs"
},
"input_schema": {
"type": "object",
"required": [
"orders",
"cancelled"
],
"properties": {
"orders": {
"type": "integer",
"minimum": 0
},
"cancelled": {
"type": "integer",
"minimum": 0
}
}
},
"output_schema": {
"type": "object",
"required": [
"orders",
"cancelled",
"cancel_pct"
],
"properties": {
"orders": {
"type": "integer"
},
"cancelled": {
"type": "integer"
},
"cancel_pct": {
"type": "number"
}
}
},
"capabilities": [],
"visibility": {
"audience": "private",
"expose": {
"apps": true,
"workflows": true,
"agents": false,
"mcp": false
}
},
"tests": [
{
"name": "simple",
"input": {
"orders": 200,
"cancelled": 9
},
"expect": {
"/orders": 200,
"/cancelled": 9,
"/cancel_pct": 4.5
}
}
],
"docs": {
"summary": "Returns orders, cancellations and the cancel rate from two counts."
}
}

demo_kpis/main.py

def handler(input, ctx):
orders = int(input["orders"])
cancelled = int(input["cancelled"])
pct = round(100 * cancelled / orders, 2) if orders else 0.0
return {"orders": orders, "cancelled": cancelled, "cancel_pct": pct}

demo_explainer/function.json

{
"schema": "bicycle.function/v1",
"name": "demo_explainer",
"kind": "agent",
"title": "Explain a day's order KPIs",
"mode": "async",
"input_schema": {
"type": "object",
"required": [
"orders",
"cancelled"
],
"properties": {
"orders": {
"type": "integer"
},
"cancelled": {
"type": "integer"
}
}
},
"output_schema": {
"type": "object",
"required": [
"summary",
"watch"
],
"properties": {
"summary": {
"type": "string"
},
"watch": {
"type": "boolean"
}
}
},
"capabilities": [],
"agent": {
"spec": {
"spec_version": 1,
"name": "demo_explainer",
"display": "Explain a day's order KPIs",
"description": "Explains a day's orders and cancellations in two sentences.",
"system": "You explain order numbers to an operations manager in plain words. Use only the numbers you are given or that a tool returns. Never guess causes.",
"task": "Work out the cancel rate for the given counts, then write a two-sentence summary. Set watch to true when the cancel rate is above 5%.",
"input_schema": {
"type": "object",
"required": [
"orders",
"cancelled"
],
"properties": {
"orders": {
"type": "integer"
},
"cancelled": {
"type": "integer"
}
}
},
"output_schema": {
"type": "object",
"required": [
"summary",
"watch"
],
"properties": {
"summary": {
"type": "string"
},
"watch": {
"type": "boolean"
}
}
},
"capabilities": [
{
"id": "semantic.catalog"
}
],
"model": "claude-haiku-4-5",
"budgets": {
"max_steps": 8,
"max_tool_calls": 20,
"max_cost_usd": 0.5,
"max_wall_s": 120
}
}
},
"budgets": {
"max_steps": 8,
"max_cost_usd": 0.5,
"max_wall_s": 120
},
"visibility": {
"audience": "private",
"expose": {
"apps": true,
"workflows": true,
"agents": false,
"mcp": false
}
},
"docs": {
"summary": "Explains a day's orders and cancellations in two sentences."
}
}

agent.spec.capabilities needs at least one entry (semantic.catalog is read-only): an empty list fails the run with spec_invalid. max_tool_calls goes only in the spec's budgets, not the top-level one.

Granting demo_kpis to the agent. Validation accepts "grants": {"functions": ["fn:<tenant>/demo_kpis@1"]} on a tested draft, but the run does not attach the tool (0 tool calls; the agent says the tool is not available). Ask the person to publish demo_kpis with expose.agents: true first; then add the grant, put another version of demo_explainer and try it (not run here).

Calls, in order​

  1. function_create(name="demo_kpis", title="Order KPIs from two counts"), then function_put_files(name="demo_kpis", files={"function.json": ..., "main.py": ...}): version 1, validated.
  2. function_test(name="demo_kpis", n=1): state tested; test simple passed with {"orders": 200, "cancelled": 9, "cancel_pct": 4.5}.
  3. function_publish(name="demo_kpis", n=1): "A person must confirm this in Studio before v1 ...": the link and who. Give it to the person.
  4. function_agent_connections(): the viewer's connections and saved setups (none needed here).
  5. function_agent_limits(): the workspace limits (30 steps, $2 per run). Stay at the Quick preset or lower.
  6. function_models(): the model ids; pick the cheapest (claude-haiku-4-5 here).
  7. function_create(name="demo_explainer", title="Explain a day's order KPIs"), then function_put_files(name="demo_explainer", files={"function.json": ...}): version 1, validated.
  8. function_try(name="demo_explainer", n=1, input={"orders": 200, "cancelled": 13}): succeeded in about 4 s for about $0.01: "... a cancel rate of 6.5% ...", "watch": true, 0 tool calls.
  9. invocation_events(invocation_id="inv_..."): running, agent run started, succeeded, then the agent's steps.
  10. function_publish(name="demo_explainer", n=1): "A person must confirm this in Studio before v1 of demo_explainer goes live": a link to /<company>/apps/functions/demo_explainer?publish=v1, and who. Give the person the link and stop.

Switch it off​

function_disable(name="demo_explainer", reason="no longer needed")
function_disable(name="demo_kpis", reason="no longer needed")

Verified on preview 2026-09-28: function_create, function_put_files, function_test, function_publish (refusals captured), function_agent_connections, function_agent_limits, function_models, function_try, invocation_events, function_disable ran; the grant was tried on a draft and not attached; invocation ids inv_01M3J6SKE94QVVC8G7DKMABG7V (agent without grant), inv_01M3J6S0WZAZ4T0DCRSCV2M8HT (agent with the draft grant).

guide_version bb2461328de4