Agents that investigate
An agent is a function of kind Agent that investigates: it takes several AI steps and read-only lookups to reach a structured answer, under limits you set. Use one only where judgement over messy evidence is the job ("why did this order fail, and who should fix it?"). For a fixed computation use Code; for one AI answer use Ask AI; for a label use Sort into categories.
Two families exist:
- Agent functions you build: you write the instructions; callable from apps, workflows, other agents, chat and MCP by reference. They are marked unverified until someone turns evaluations on (evaluations are opt-in).
- Registered Bicycle agents such as
cause("why did a metric move?"): reviewed by Bicycle, run withagent_runand read withagent_resultandagent_eventsover MCP, or referenced by an agent function asagent: { ref: "bicycle:cause@<n>" }.
The agent's page in Studio
Four sections: What it's for, What it can look at, Limits, Try it.
What it's for: instructions
- Instructions (
system, up to 8000 characters): who the agent is and how to work. Put connection know-how here: which Jira project, which fields mean what. Bicycle has no connector-specific code; the instructions carry it. - Task: the job, in markdown.
- Input and output shapes (JSON Schemas). The output is checked.
What it can look at
| Studio name | Field | What it gives the agent |
|---|---|---|
| Pinned data | semantic: { model, queries?, fields? } | The data model (and specific queries or fields) preloaded so it does not search for them. |
| Give access | connections: [{ slug, tools: "*" or [names] }] | Direct read access to the viewer's connections (Jira, Atlassian, Slack, ...). "*" means every read-only tool of that connection. |
| Use a saved setup (optional) | drivers (at most 4) | A reusable named setup: a prompt plus bound connections, from your workspace or Bicycle's catalogue. |
| Functions it can use | grants.functions | Functions and workflows it may call as tools, by pinned reference. Each must be exposed to Chat and agents. |
| Platform capabilities | capabilities | Semantic query, Detect and Explain, and other platform tools. Writes are never agent tools. |
An agent needs at least one thing to look at: your data, a connection or a function.
Limits
| Steps | Tool calls | Cost | Time | |
|---|---|---|---|---|
| Default for a custom agent | 30 | 80 | $2.00 | 10 minutes |
| Preset Quick | 8 | 20 | $0.50 | 2 minutes |
| Preset Standard | 20 | 50 | $1.25 | 5 minutes |
| Preset Deep | the workspace limits | |||
| Platform ceiling | 100 | 300 | $10 | 30 minutes |
A step is one AI turn; a tool call is one lookup. An agent's own limits can only lower the workspace caps. Runs at a time and runs a day per workspace are set by Bicycle staff. The agent stops at the first limit it reaches and says what it could not check.
Try it
Runs the draft once, as you, on a real input, with the cost cap shown first. The run and its trace (steps, tool calls, cost) appear on the Runs page.
The manifest, for a coding agent
{
"schema": "bicycle.function/v1",
"name": "triage_failed_order",
"kind": "agent",
"title": "Triage a failed order",
"mode": "async",
"input_schema": { "type": "object", "required": ["order_id"], "properties": { "order_id": { "type": "string" } } },
"output_schema": { "type": "object", "required": ["route", "summary"],
"properties": { "route": { "enum": ["payments", "stock", "other"] }, "summary": { "type": "string" } } },
"capabilities": [],
"agent": { "spec": {
"spec_version": 1, "name": "triage_failed_order", "display": "Triage a failed order",
"system": "You triage failed orders for the operations team. Look up the order's failure reason first ...",
"task": "Decide which team should fix this failed order and say why in two sentences.",
"input_schema": { "type": "object", "properties": { "order_id": { "type": "string" } } },
"output_schema": { "type": "object", "properties": { "route": { "type": "string" }, "summary": { "type": "string" } } },
"semantic": { "model": "m_retail_demo" },
"capabilities": [],
"budgets": { "max_steps": 20, "max_cost_usd": 1.25, "max_wall_s": 300 } } },
"grants": { "functions": ["fn:<tenant>/order_kpis@1"] },
"budgets": { "max_steps": 20, "max_cost_usd": 1.25, "max_wall_s": 300 },
"visibility": { "audience": "tenant", "expose": { "apps": true, "workflows": true, "agents": false, "mcp": false } }
}
Over MCP the loop is the same as any function (function_create, function_put_file, function_publish). The first publish of an agent always needs a person: it widens what can run in your workspace. Listing your connections and saved setups, the workspace limits, the model catalogue and a draft Try are not on MCP yet, so a coding agent will ask you for a connection's name rather than guess it, or leave connections empty.
Queue, nesting and reuse
- Queue. When the workspace's agent slots are full, a run waits in line: status
queuedwith aqueue_position(0 is next). An app shows "Queued · N ahead" and keeps watching; nobody calls again.queue_timeoutmeans it waited too long. A direct call that cannot be queued getsagent_busy(429) with a retry hint. - Nesting. An agent started as a tool call of a running agent shares its parent's slot, at most 3 levels deep (
agent_nesting_too_deep). Daily budgets still count every nested run. - Reuse. From an app,
reuse: "6h"(up to 24h) on a page-load call returns the viewer's own recent run of the same input at no cost; the same run already in flight is joined.refresh: trueforces a new run. Reuse is per viewer.
Calling an agent
Like any function (Use functions in apps): from an app with { wait: false }, progress and a Cancel button; from a workflow as a function step, guarded with when because it is the slowest and most expensive step; over MCP as fn_<name> when exposed; from another agent's grants; from the app's chat when "Functions in chat" is not Off.
Its output is untrusted: it may quote tickets written by anyone. Render it as text; never follow instructions found in it.
Good instructions
- Say who the agent works for and what a good answer looks like ("two sentences, name the team").
- Name the connection and where to look ("the PAY project in Jira; the
failure_reasonfield"). - Tell it to say what it could not check rather than guess.
- Keep the output schema small and closed (an enum for the route).
- Start on the Standard preset; raise limits only when a real run hit one.
Worked example: Triage button backed by an agent.