A research agent with the web tool and a budget#
You will build researcher: given a question, it searches and reads
public pages and answers in five bullet points, each with its source. Each
run stops at $1; short requests go to a cheaper model; and when the agent
has spent $100 in the month, each further run asks an admin before
spending another $10.
Before you begin#
- A machine that can run agents; the editor role, and admin for the monthly budget.
- An Anthropic API key in a credential
anthropic-key(keyapi_key).
1. Create the agent#
The web tool reads any public page, GET only; the provider's own search
is allowed, its code execution is not:
@id("web-reads")
permit (principal, action == Action::"http", resource in Server::"web")
when { ["GET", "HEAD"].contains(resource.method) };
@id("model-web-search")
permit (principal, action == Action::"model/server_tool", resource)
when { ["web_search", "web_fetch"].contains(resource.tool) };
- Anemoi → Agents → New agent, name
researcher, Blank, Assistant. - Model: Anthropic,
claude-sonnet-4-5,anthropic-key. - Connections: Web browsing, Read only. Tick Let the model search the web with its provider's search.
- Instructions: You research a question on the public web and answer in five bullet points, each with its source URL. Prefer primary sources: release notes, specifications, vendor documentation.
- Limits: Longest run, minutes
15; Stop at, per run1.00; When reached Stop the run. - Switch to Advanced → Routing, Add a rule: at most 2 000
input tokens → Anthropic,
claude-haiku-4-5,anthropic-key. - Create the agent.
$ jq -n --rawfile p researcher.cedar '{
metadata: {name: "researcher"},
spec: {
kind: "astralyx",
model: {
provider: "anthropic", model: "claude-sonnet-4-5", credential: "anthropic-key",
routes: [{when: {max_input_tokens: 2000},
model: {provider: "anthropic", model: "claude-haiku-4-5", credential: "anthropic-key"}}]
},
instructions: "You research a question on the public web and answer in five bullet points, each with its source URL. Prefer primary sources: release notes, specifications, vendor documentation.",
tools: [{name: "web", kind: "web"}],
policy: {tools: $p},
budget: {max_seconds: 900, max_cost_usd: 1.0, on_exceed: "stop"}
}}' \
| curl -sS -X POST "$WS/agents" -H "Authorization: Bearer $ASTRA_TOKEN" -H "Content-Type: application/json" -d @-
2. Add a monthly budget#
Anemoi → Budgets → New budget: name researcher-monthly; For
the agent researcher; Per Month (UTC); Dollars 100;
When reached Ask for approval; Each approval allows, dollars
10. Create.
3. Run it#
The Trace shows the model calls, each with its route — route:0 for
short requests on the smaller model, primary for the rest — and tokens
and cost; the provider's searches with their queries; and each page the
agent fetched through web, with its URL. Activity lists the same
requests as they left the machine.
4. Watch what it spends#
The run's page shows its cost. The agent's Costs tab lists each
run for Today or This month; Anemoi → Budgets shows
researcher-monthly: spent, limit, Ask for approval, Ok.
What happens at each limit#
| Limit | When reached |
|---|---|
| $1 in one run | The next model call is refused: budget reached: $1.00 of $1.00. The agent ends with what it has. |
| $100 in the month | The budget turns Exhausted. New runs are refused with 409 BUDGET_EXHAUSTED; a running run's next model call asks for an approval of kind budget. Approved, the agent may spend $10 more. |
| A new month | The budget is Ok again. |
The monthly budget is close but not exact: runs under way may spend about 20 seconds' worth past it. The per-run limit is exact to one call.
Narrow the web#
To keep it to trusted sites, replace web-reads:
@id("web-reads-trusted")
permit (principal, action == Action::"http", resource in Server::"web")
when { ["GET", "HEAD"].contains(resource.method) &&
(resource.host like "*.nvidia.com" || resource.host == "docs.nvidia.com" || resource.host like "*.rust-lang.org") };
Addresses inside your network are never reachable through the web tool, whatever the policy says.