Skip to content
Bifrost Docs

Agents and Chat

Build conversational and autonomous AI agents that call workflows as tools

Agents combine a system prompt, a set of workflows exposed as tools, optional knowledge sources, and a budget. They can run interactively (Chat) or autonomously (event-driven, scheduled, or SDK-invoked).

  1. Go to Agents in the sidebar and click New agent.

  2. Fill in name, description, and system prompt. The system prompt is the agent’s instructions and personality.

  3. Set the Access Level (Public, Authenticated, or Role-based) and assign roles if applicable.

  4. Pick Tools (workflows tagged is_tool=True and any system tools).

  5. Optionally add Knowledge Sources for RAG, and Delegated Agents for multi-agent workflows.

  6. Save. The agent is immediately available for chat and autonomous runs.

Level Who can chat / run
Public Anyone, no auth
Authenticated Any logged-in user
Role-based Users with at least one assigned role

Mark a workflow as a tool by setting is_tool=True (or using the @tool alias):

from bifrost import workflow
@workflow(
is_tool=True,
description="Look up a customer by email address",
)
async def lookup_customer(email: str):
# ...
return {"name": "John Doe", "account": "12345"}

The workflow description is what the LLM reads to decide when to call the tool. Be specific. If you need to tune the agent-facing phrasing after discovery, edit it in the workflow record rather than the decorator.

Attach a knowledge namespace to give the agent retrieval-augmented answers. The agent searches the namespace automatically when it needs background context. See Knowledge Bases for namespace setup.

Start conversations programmatically:

import requests
# Create a conversation (optionally pinned to an agent)
response = requests.post(
f"{api_url}/api/chat/conversations",
json={"agent_id": "agent-123"},
headers={"Authorization": f"Bearer {token}"},
)
conversation_id = response.json()["id"]
# Send a message
requests.post(
f"{api_url}/api/chat/conversations/{conversation_id}/messages",
json={"content": "Hello, I need help with my account."},
headers={"Authorization": f"Bearer {token}"},
)

For real-time tokens, connect a WebSocket:

const ws = new WebSocket(
`wss://your-instance.com/api/chat/conversations/${conversationId}/stream`
);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "delta") console.log(data.content);
};
ws.send(JSON.stringify({ content: "How do I reset my password?" }));

Switch agents mid-conversation using @mentions:

User: @support-agent I need help with billing
AI: (responds as support-agent)
User: @tech-agent Can you check my server status?
AI: (switches to tech-agent)

Posting to /api/chat/conversations without an agent_id uses AI-based routing to pick an appropriate agent or fall back to a system default.

Agent runs execute without a chat session. The agent receives input, runs a tool-calling loop, and returns structured output.

  1. A run is created with input data and (optionally) an output schema.
  2. The agent loads its prompt, tools, and knowledge.
  3. The LLM picks a tool, the tool runs, the result feeds back into the LLM.
  4. The loop continues until the agent answers or hits a budget limit.
Field Default Max Purpose
max_iterations 50 200 Cap on LLM call cycles
max_token_budget 100,000 1,000,000 Cap on total tokens
max_run_timeout unset Optional hard time limit

If either iterations or token budget is hit, the run finishes with status budget_exceeded.

Use agents.run() when the workflow needs the agent’s output before it can continue.

from bifrost import workflow, agents
@workflow
async def triage_ticket(ticket_id: str):
return await agents.run(
"ticket-triage-agent",
input={"ticket_id": ticket_id},
output_schema={
"type": "object",
"properties": {
"priority": {"type": "string"},
"category": {"type": "string"},
},
},
)

Use agents.enqueue() when the workflow only needs to submit the work. Awaiting it waits for the short HTTP 202 acceptance response, not for the agent to finish.

from bifrost import workflow, agents
@workflow
async def queue_ticket_triage(ticket_id: str):
handle = await agents.enqueue(
"ticket-triage-agent",
input={"ticket_id": ticket_id},
output_schema={
"type": "object",
"properties": {
"priority": {"type": "string"},
"category": {"type": "string"},
},
},
)
return {"run_id": handle.run_id, "status": handle.status}

The returned run has already been written with status queued, so it can be read immediately:

run = await agents.get_run(run_id)
if run.status == "completed":
return run.output
if run.status in {"failed", "timeout", "cancelled", "budget_exceeded"}:
raise RuntimeError(run.error or f"Agent run ended with {run.status}")

Poll with a delay when the caller needs to wait later. Avoid a tight loop; one request per second is usually sufficient.

  1. Go to Events and open your event source.

  2. Click Add Subscription and set Target Type to Agent.

  3. Pick the agent. The event payload becomes the agent’s input.

response = requests.post(
f"{api_url}/api/agent-runs/execute",
json={
"agent_name": "ticket-triage-agent",
"input": {"ticket_id": "T-123"},
"output_schema": {"type": "object", "properties": {"priority": {"type": "string"}}},
"timeout": 300,
},
headers={"Authorization": f"Bearer {token}"},
)

Agents can delegate to other agents. When you add a delegated agent, the parent automatically receives a delegate_to_<agent_name> tool. The LLM decides when to call it.

Limit Value
Max depth 5
Per-delegation timeout 600s

Delegation creates child AgentRun rows linked via parent_run_id. Child runs are filtered out of the top-level run list and rolled up under the parent in the run detail view.

  • Cancel: stops a running agent. Status transitions runningcancellingcancelled. Cancellation is checked before each iteration, during tool execution, and via a background watcher.
  • Rerun: re-executes a finished run with the same input. Creates a new run with trigger_type="rerun".

Both are exposed in the run detail UI and via POST /api/agent-runs/{run_id}/cancel / POST /api/agent-runs/{run_id}/rerun.

Every autonomous run records:

  • Steps — every LLM call and tool execution, including delegation steps with expandable child detail.
  • AI usage — input/output tokens and cost grouped by model.
  • Statusqueuedrunningcompleted | failed | budget_exceeded | cancelled.
  • Triggermanual, webhook, schedule, workflow, rerun, or delegation.

View runs from the agent detail page’s Runs tab, the global History → Agents page, or via GET /api/agent-runs.

The Coding Agent is a built-in platform agent for creating and editing Bifrost workflows. It runs in your workspace context and uses precision editing tools rather than full-file replacements.

Tool Purpose
list_content List files by entity (app_file, workflow, module)
search_content Regex search with context lines
read_content_lines Read a specific line range
patch_content Surgical string replacement
replace_content Full content replacement / create new file
delete_content Delete a file

Combined with the Coding Agent’s workflow execution, validation, and execution-history tools, these power a search → read → patch loop suited to LLM editing. A client connected to the root /mcp endpoint reaches this catalog through the four gateway tools; a client connected to the Coding Agent’s /mcp/{agent_id} endpoint sees its approved native tools directly.