Projects/Autonomous Ops Agent/Part 2
Metrics World and Read Tools
Simulate services, SLOs, logs, and read-only tools that never mutate world state.
The environment is a mutable dict the mutate tools will change later. Read tools must not change it. That split is a test: call get_metrics a thousand times, hashes of WORLD stay equal. This part builds the world, SLOs, log lines, and read APIs.
World schema
Each service:
error_ratefloat 0–1p95_msintcpufloat 0–1deploy_id,prev_deploy_idreplicasintslo_error,slo_p95_mslogslist of strings (ring buffer)
A healthy checkout sits at 0.4% errors, p95 220ms. An incident snapshot starts unhealthy so the agent has work. You can also store SCENARIO = "bad_deploy" to pick log lines that mention NullPointer after deploy d44 — diagnosis should point at rollback, not scale.
Read tools
get_metrics(service)→ copy of numeric fields (not necessarily all logs).get_logs(service, n=20)→ last n lines.list_services()→ names.get_slo(service)→ slo fields.
Return {"error": "unknown_service"} for typos. Copies via dict so the agent cannot mutate WORLD by accident through a returned object (in Python the numbers are immutable; lists of logs need list(...)).
Run to execute this in your browser. Nothing is sent to a server.
Step-by-step environment rules
- Deep-copy in tests when you need a snapshot; for the inequality check above,
copy.deepcopybefore reads. - Cap log n. Models will request 10 million lines.
- Do not put secrets in logs. Fake logs have no API keys. Add a redaction function if a line matches
sk-. - Multiple services so diagnosis cannot be "always checkout".
SLO burn as a boolean
A tiny helper burning(service) is not a mutate tool. It is allowed in supervisor code. You can also expose it as check_slo(service) read tool so the model does not do inequality wrong. Prefer tools that compute the predicate for ops — arithmetic in LLMs is sloppy.
Note:Metrics are the observations. Diagnosis is a policy. Keep them separate so you can test burn math without the model.
Cardinality and time
Real metrics systems have labels (region, version, customer). Your WORLD can grow a labels dict later; do not let the model pass arbitrary PromQL. If you add a query_metrics(expr) tool, you have built SQL-injection-for-ops. Stick to get_metrics(service) until you have a parser.
Time is a list of snapshots if you need it: HISTORY[service] = [snapshot, ...]. The agent may read the last two to say "error_rate jumped after d44." Do not generate 10,000 fake points in the Try it box. Two snapshots already teach change, which is what diagnosis needs.
Read tools should be idempotent: same arguments, same result, until a mutate happens. If get_logs pops the ring buffer, you have built a destructive read. Tests will flake and diagnosis will lose evidence. Copy the tail; do not consume it. Metrics snapshots should be JSON-serializable so you can hash them in the audit log.
Exercise
Add a payments service that is healthy. Confirm list_services includes it. Add a log line containing sk-demo-not-real and a redact(lines) that replaces sk- tokens with sk-***. Logs tools must use it.
Check your understanding