Tutorial: Your First Agent
Goal: not just to create an agent, but to see everything the platform does when you use one. 15 minutes, both servers running (Installation).
1 · Design before you create
Three decisions matter (Agents):
- System prompt — the only free-text the LLM sees. Be concrete about role and boundaries.
- Tools — grant the minimum. We start with none to prove chat needs no tools.
- Language/tone — enforced on every call; don't restate them in the prompt.
2 · Create it (two ways)
Open http://localhost:3000/create-agent. Fill: name Docs Helper, type Customer Support, system prompt below, language English, tone Friendly. Untick every tool. Watch the Live Preview mirror your input, then submit — the success banner shows the new ID.
Note the returned id (assume 2). Verify it appears at http://localhost:3000/agents — no deploy step; configuration is the agent.
3 · Chat, then autopsy the exchange
curl -X POST http://localhost:8001/agents/2/chat \
-H "Content-Type: application/json" \
-d '{"message": "Can you check why my account is locked?"}'The reply should respect your boundary ("I can't access accounts…") — that's your system prompt working inside the chat wrapper. Now the autopsy — three records were written (Request Lifecycle):
curl "http://localhost:8001/agents/2/messages?limit=2" | jq # user + assistant Message rows
curl http://localhost:8001/agents/2/runs | jq '.[0]' # AgentRun: latency_ms, statusOr straight in SQLite:
sqlite3 backend/app/agentops.db \
"SELECT role, substr(content,1,40) FROM messages WHERE agent_id=2 ORDER BY id;"4 · Prove statelessness
curl -X POST http://localhost:8001/agents/2/chat \
-H "Content-Type: application/json" -d '{"message": "What did I just ask you?"}'The agent doesn't know — history is stored but never replayed into context (the platform's biggest chat limitation). You now understand it because you watched it.
5 · Prove tool gating
curl -X POST http://localhost:8001/agents/2/email/analyze \
-H "Content-Type: application/json" -d '{"email_text": "Please send the invoice by Friday."}'{ "detail": "This agent does not have email_analyzer tool enabled" }403 — the allow-list doing its job (Tools). Create a second agent with email_analyzer,task_creator and rerun to see the difference; then check GET /agents/{id}/tool-calls for the logged calls.
6 · Watch a failure get recorded
Temporarily rename OPENAI_API_KEY in backend/app/.env (uvicorn reloads), chat once, and observe graceful degradation:
- Response:
200with "Sorry, I could not generate a response right now." GET /agents/2/runs→ newest run hasstatus: "failed"and the realValueErrorinerror_message.
Restore the key. You've now seen the chat error philosophy end to end — failures are polite to users and loud to operators.
What you learned
| You did | The concept |
|---|---|
| created config, used it instantly | agents are rows, not deployments |
| read messages/runs after chatting | the observability contract |
| watched a boundary hold | prompt wrapping (catalog) |
| got a 403 | tool gating |
| broke the key safely | graceful degradation |
Next: Email Workflow Tutorial — where one request becomes a four-stage traced pipeline.