# The AgentOps Doctor, explained A 10-minute read for a platform, observability, or AI engineer (and the engineering managers who own those teams) who runs `agentops doctor` for the first time. For step-by-step setup, see the [end-to-end tutorial](tutorial-end-to-end.md). ## 1. What the Doctor is - and isn't The **Doctor** is a regular check-up for an agent project. It reads signals that are already there (eval history, App Insights telemetry, Foundry metadata, Azure resource configuration) and emits **findings** - severity-ranked observations with a recommendation attached. It does *not* fix anything. It does *not* replace Microsoft Foundry's **Operate → Compliance** surface - Foundry handles guardrails, security posture, and data governance at the resource level. The Doctor is the complementary half: runtime telemetry, identity scope, eval discipline, pipeline hygiene. A single command: ``` agentops doctor ``` …produces `.agentops/agent/report.md` and a CI-friendly exit code: `0` = clean, `2` = a finding meets the configured `--severity-fail` floor, `1` = the analyzer itself errored. For release reviews, add the evidence flag: ```bash agentops doctor --evidence-pack ``` That writes `.agentops/release/latest/evidence.json` and `evidence.md`. The evidence pack summarizes eval, baseline, Doctor, CI/CD workflow, Foundry continuous-eval, monitoring, AI Landing Zone, and trace-regression readiness without creating a second exit-code contract. Its Markdown report includes a Doctor finding summary with severity, category, finding ID, and title; generated GitHub workflows append that report to the run summary for quick triage. ## 2. The four signal sources | Source | Reads | Feeds these checks | When it's "ok" | |---|---|---|---| | `results_history` | Local `.agentops/results/*/results.json`; Foundry cloud evaluation runs as fallback | `regression`, `latency` (eval), `safety` (eval layer), `opex` (stale + flaky) | At least one local run or a reachable Foundry project with cloud evaluations. | | `azure_monitor` | App Insights / Log Analytics via KQL | `latency` (p95), `errors` (rate + no-telemetry), `safety` (runtime layer) | Source `enabled: true` + connection reachable. | | `foundry_control` | Agents, runs, evaluation rules via `azure-ai-projects` | `errors` (Foundry runs), `safety` (continuous-eval rules), `operational_excellence` (Foundry config audit) | `enabled: true` + project endpoint set. | | `azure_resources` | Cognitive Services account + diagnostic settings via `azure-mgmt-*` | `posture` (WAF-AI Security pillar) | Enabled by default. Doctor uses explicit config first, then AZD `.azure//.env` when present, then Foundry endpoint/account matching. Reader RBAC is required on the resource group. | Each source **fails open**: if it's not configured, cannot be inferred, or its SDK isn't installed, the Doctor reports it as `skipped` in the diagnostics block with the reason and next setup step. Other checks keep working. ### Why two sources have "wiring" rules Two of the four sources, `azure_monitor` and `foundry_control`, are treated specially: the Doctor also runs a dedicated check on whether that source is actually wired up. The reason: dedicated rules fire when a wiring gap exists, so a project that never even configured App Insights does not show up as "all clear" simply because there is no production monitoring to grade. * `errors.no_runtime_telemetry` fires when `azure_monitor` is skipped (no `app_insights_resource_id`) or returns an empty workspace (zero requests over the lookback window). * `opex.no_foundry_control_configured` fires when `foundry_control` is skipped (no `project_endpoint`) or cannot be read. A reachable Foundry project with zero agents is treated as source context, not a finding, because the agent may be deployed through HTTP, Container Apps, AKS, or another runtime. Both rules stay silent when the source is explicitly `enabled: false`. That is how you tell the Doctor "this project does not use that backend" - the missing backend is treated as a deliberate opt-out rather than a gap. ### AI Landing Zone deployment readiness Doctor also reads local AI Landing Zone signals from the workspace: `azure.yaml`, `manifest.json`, `scripts/Invoke-PreflightChecks.ps1`, generated AgentOps deploy workflows, and common network-isolation markers. When it sees canonical AI Landing Zone evidence, it emits an Operational Excellence summary (`opex.ailz_readiness`) and, if needed, one aggregated warning (`opex.ailz_gaps`) with the missing readiness dimensions. The intent is positive and practical: AgentOps helps move the project toward an AI Landing Zone-ready deployment path by checking that the official preflight, azd/Bicep workflow, AgentOps eval config, private-network runner plan, and post-deploy Doctor/eval evidence are wired together. ### Production release readiness Doctor also emits Operational Excellence findings for the POC-to-production journey: - latest eval evidence exists and passed; - a baseline/comparison exists for regression decisions; - a trace-regression manifest exists when production traces have been promoted; - Foundry continuous evaluation is enabled when the control plane is reachable. These findings feed the optional evidence pack. A `blocked` evidence status means the release reviewer should stop; `ready_with_warnings` means the release can be reviewed with explicit gaps. The underlying Doctor exit code still depends only on `--severity-fail`. ### Extension point: Microsoft 365 Copilot agents The four sources above all target Azure Foundry workloads. **Microsoft 365 Copilot agents** (declarative agents shipped as JSON manifests and custom agents authored in **Copilot Studio**) run on a separate control plane (Microsoft Graph + Power Platform Admin APIs + Microsoft 365 Admin Center), so they are not covered today. The Doctor is designed to grow here without disturbing the existing contract. A future `microsoft365_agents` source would slot in next to `foundry_control`, read tenant-scoped agent metadata, and emit Operational Excellence rules. Candidate auditable signals, all reachable via Graph + Power Platform admin APIs without inspecting agent runtime behaviour: * `opex.no_m365_agents_configured` - source enabled but tenant/environment id not set. * `opex.no_m365_agents` - source connected but no agents registered in the target environment. * `opex.m365_agent_no_publisher_attestation` - agent has no verified publisher / Microsoft Partner Network attestation. * `opex.m365_agent_no_privacy_url` - agent manifest is missing a privacy policy URL (required for tenant-wide distribution). * `opex.m365_agent_unlabeled` - agent has no sensitivity label applied (DLP / Information Protection gap). * `opex.m365_agent_environment_mismatch` - production agent lives in a dev / default Copilot Studio environment instead of a managed one. * `opex.m365_agent_actions_anonymous` - one or more agent actions / connectors call out without authentication, bypassing tenant DLP. The first two are workflow-hygiene gaps; the remaining five are governance signals that fit naturally next to the existing Operational Excellence rules. This is a real follow-up, not a quick add: it brings a new dependency (`msgraph-sdk` or `msal` + raw HTTP), a new auth flow (tenant-level admin consent), and a larger surface of preview APIs (Power Platform agent endpoints are still moving). It is intentionally not in the current release. ## 3. The check families | Check | Category | Headline question | |---|---|---| | `regression` | `quality` | Did any metric drop vs the rolling baseline? | | `latency` | `performance` | Is p95 latency above the threshold? | | `errors` | `reliability` | Are production errors / Foundry failures above threshold? *Or* is telemetry connected but silent? | | `safety` | `responsible_ai` | Three layers: eval content-safety hits, runtime content-filter triggers, missing / disabled continuous-eval rules. | | `posture` | `security` | WAF-AI Security pillar - local-auth, managed identity, diagnostic settings. | | `opex_workspace` | `operational_excellence` | Workspace hygiene - pinning, gates, deploy workflows, results gitignore, dataset/bundle versioning, workflow concurrency / SHA pinning, AI Landing Zone deployment readiness. | | `opex` | `operational_excellence` | Time-based - stale eval runs + flaky-metric drift. | | `spec_conformance` | `operational_excellence` | Does the implementation match the spec? (spec-kit `.specify/`, `AGENTS.md`, Copilot instructions.) | ## 4. The six categories | Category | What good looks like | |---|---| | `quality` | No regression findings - metrics hold against the rolling baseline. | | `performance` | Latency p95 inside the threshold both in production and in eval. | | `reliability` | Error rate under threshold, Foundry runs succeeding, telemetry producing data. | | `security` | WAF-AI Security pillar findings empty - local-auth disabled, MI configured, diagnostic settings flowing. | | `responsible_ai` | No content-filter hits in eval or production, continuous evaluation rules attached and enabled. | | `operational_excellence` | Workspace + CI hygiene clean - versioned datasets / bundles, PR + deploy gates, AI Landing Zone readiness when applicable, no stale evals, no flaky metrics, and the implementation matches the spec. | ## 4b. Spec-conformance rules When the workspace contains spec-driven-development artifacts (`.specify/spec.md`, `AGENTS.md`, `.github/copilot-instructions.md`), the `spec_conformance` check inspects them for drift against the implementation. Pluggable detectors: * `spec-kit` - reads `.specify/spec.md`, `plan.md`, `tasks.md`. * `agents-md` - reads `AGENTS.md`, `.github/copilot-instructions.md`, `.github/instructions.md`, `CLAUDE.md`. Deterministic findings (all `info` / `warning`, never `critical`): | Finding id | Detection | |---|---| | `opex.spec_conformance.spec_missing` | Spec-driven setup detected, but no readable spec body was found; Doctor cannot verify bundles, datasets, tasks, or implementation against intended agent behavior. | | `opex.spec_conformance.tasks_stale` | Unchecked task-list items in the spec have remained open past `stale_after_days`; Doctor treats this as a signal that the implementation plan may be stale, completed work was not checked off, or the spec was not refreshed after agent behavior changed. | | `opex.spec_conformance.tasks_orphaned` | Checked task references a file that doesn't exist. | | `opex.spec_conformance.evaluator_drift` | Spec mentions evaluators absent from `agentops.yaml`. | | `opex.spec_conformance.dataset_drift` | Spec mentions datasets absent from the workspace. | | `opex.spec_conformance.agent_drift` | Spec's agent id doesn't match `agentops.yaml`. | Opt-in LLM gap-analysis (`opex.spec_conformance.llm.implementation_gap`) runs only when both the global `checks.llm_assist.enabled` and `checks.operational_excellence.spec_conformance.llm_assist.enabled` flags are true (and `AGENTOPS_DOCTOR_LLM_ASSIST` is not `0`). The LLM rule never emits `critical`. Configure it under: ```yaml checks: operational_excellence: spec_conformance: enabled: true detectors: [spec-kit, agents-md] stale_after_days: 30 skip: [] llm_assist: enabled: false severity_floor: 0.6 max_input_chars: 30000 max_workspace_paths: 200 ``` ## 5. A typical report - annotated ``` # AgentOps Doctor Report ## Verdict: ⚠️ Warnings found ← top-level summary ## Summary | Severity | Count | ← scan these first; counts feed CI gating |---|---| | 🚨 Critical | 0 | | ⚠️ Warning | 3 | | ℹ️ Info | 0 | ## Sources | Source | Status | Detail | ← which sources actually ran |---|---|---| | results_history | ok | 7 runs loaded | azure_monitor | ok | | foundry_control | skipped | no project_endpoint configured ## Findings ← grouped by category ### Reliability ... ### MLOps / pipeline hygiene ... ``` Each finding has its own detail block with **Severity**, **Category**, **Source**, and - when the finding matches a row in the WAF knowledge base - a **WAF** line linking the pillar / area / public Microsoft Learn page. The detail block also carries the **Recommendation** and an **Evidence** JSON snippet that's copy-paste-ready for a PR or incident. ## 6. Severities and exit codes Severities are **independent of category**: a `quality` finding can be `critical`, `warning`, or `info`. The Doctor's exit codes mirror this: | Exit code | Meaning | |---|---| | `0` | Doctor ran and either found nothing, nothing at or above the configured `--severity-fail` floor, or the finding gate was disabled with `--severity-fail none`. | | `2` | Doctor ran and at least one finding is at or above the floor. Treat as a CI failure. | | `1` | Doctor itself failed (bad config, unreachable source, internal error). | The default `--severity-fail critical` is good for production release gates and is also the default behavior in the AgentOps PR workflow template (set via `agentops workflow generate --doctor-gate critical`). It blocks the PR on critical findings such as regression detection — for example a `regression.groundedness` finding when the metric drops from a 5.0 baseline to 4.0, which would still pass typical `>= 3` thresholds in `agentops.yaml` but is a meaningful drift signal worth catching. `--severity-fail warning` is good for nightly cron jobs that want to catch smaller drift before it gets bad, and matches `agentops workflow generate --doctor-gate warning`. Use `--severity-fail none` (or `--doctor-gate none`) when Doctor should remain evidence-only, such as a PR workflow that delegates the merge decision entirely to the eval step. Runtime or configuration errors still return `1`. ## 7. LLM-judged checks Every deterministic check listed above is fast, reproducible, and free to run in CI. But it leaves a class of signals on the table: anything that needs semantic judgement of the artefacts the project ships - the agent's system prompt, the dataset rows, the bundle's evaluator choice. The Doctor closes that gap with **LLM-judged checks**. They run on every `agentops doctor` invocation by default. The judge model is **auto-discovered from the Foundry project** the first time it runs: the Doctor lists the project's deployments, picks a chat-capable one (preferring mini / cheaper models to keep token cost down), caches the choice, and reuses it on subsequent runs. ### Six advisory rules | Finding id | Category | What it audits | |---|---|---| | `responsible_ai.llm.prompt_transparency` | `responsible_ai` | System prompt discloses AI nature, cites sources, sets a role/scope. | | `responsible_ai.llm.prompt_safety_guardrails` | `responsible_ai` | System prompt has refusal patterns for the four harm categories (violence, self-harm, sexual, hate / unfairness). | | `responsible_ai.llm.prompt_jailbreak_surface` | `responsible_ai` | System prompt resists known trapdoor patterns (override phrasing, embedded secrets, unbounded role-play). | | `responsible_ai.llm.dataset_pii_risk` | `responsible_ai` | Sample of `.agentops/data/*.jsonl` rows scanned for PII (names, emails, phones, ids, addresses, DOBs). | | `responsible_ai.llm.dataset_bias_signals` | `responsible_ai` | Sample of dataset rows judged for demographic / role / domain / tone / happy-path skew. | | `opex.llm.bundle_coverage` | `operational_excellence` | Bundle YAML + agent description compared, missing built-in evaluators flagged. | Findings carry `source: "llm_judge"` and a `[LLM-judged]` prefix in the title. Severity caps at **WARNING** by design - the judge is advisory, never fail-the-build. The judge's `confidence` and short `reasoning` are kept in the finding's evidence so the user can audit the call. ### Tuning (optional) ```yaml # .agentops/agent.yaml checks: llm_assist: enabled: true # default; set false to skip the suite deployment_name: null # explicit override; otherwise auto-discovered project_endpoint_env: AZURE_AI_FOUNDRY_PROJECT_ENDPOINT max_dataset_rows: 50 # cap rows sent to the judge per check min_confidence: 0.6 # findings below this are dropped silently cache_ttl_days: 30 rules: [] # empty = run all; or list rule ids to opt-in ``` If you do not want the LLM-judged suite at all - for example, an ephemeral CI sandbox with no Foundry credentials - set `enabled: false` and only the deterministic checks run. ### Cost guardrails * **Auto-discovery prefers mini models.** When picking a deployment automatically, the Doctor favours `gpt-*-mini` first so judge calls stay cheap by default. * **Cache.** Each judge call hashes its inputs (prompt, dataset bytes, bundle YAML). Results land in `.agentops/cache/llm/.json`. Re-running the Doctor with unchanged inputs costs zero tokens. * **Sampling.** `max_dataset_rows` caps how many rows the dataset rules ship to the judge (default 50). * **Min confidence.** Low-confidence verdicts are dropped before they reach the report, so the only LLM findings you see are ones the judge is willing to stand behind. ### Suggested fixes Every LLM-judged finding asks the judge for **two to four concrete, case-specific fixes** in addition to its risk verdict. Those land in the finding's `evidence.suggestions` list and are spliced into the recommendation block of `report.md`. Cockpit renders them in a collapsible **Suggested fixes** panel next to each finding. The panel is read-only by design - the user reviews and applies; the Doctor itself does not write to files. ## 9. Customising Three knobs: ``` agentops doctor --categories security,responsible_ai # only those buckets agentops doctor --exclude-rules waf.security.diagnostic_settings # silence one rule agentops doctor --workspace ./other-project # point at a different repo ``` For thresholds, source configuration, and check toggles, edit `.agentops/agent.yaml`. The starter template lives in `src/agentops/templates/agent.yaml`. ## 10. The WAF knowledge base (editable CSV) The Doctor ships with a **packaged baseline** at [`src/agentops/agent/knowledge/waf-checklist.csv`](../src/agentops/agent/knowledge/waf-checklist.csv). It maps every Doctor finding id to a row that names its WAF pillar, area, and a public Microsoft Learn reference link. The reporter annotates each finding with a `WAF: / ` line when a match exists. To **add or override** rules in your own project, edit the workspace copy at `.agentops/waf-checklist.csv`. `agentops init` scaffolds a blank version of this file (header + commented examples). The Doctor reads it on every run and merges with the packaged baseline: - Rows with a `doctor_check_id` that **already exists** in the packaged file **override** that packaged row (pillar, area, reference url, etc.). - Rows with a **new** `doctor_check_id` extend the checklist with your own rules. - Lines starting with `#` are treated as comments. Strict rule (same as the packaged file): only items the Doctor can actually *check* belong here. Human-eyeball checklist items are excluded by design. The workspace file is meant to be committed to git alongside the rest of `.agentops/`, so the override is reproducible across team members and CI. ## 10. Standards we anchor to - **Microsoft Well-Architected Framework for AI workloads** - https://learn.microsoft.com/azure/well-architected/ai/. Source of truth for the *categories* of items (security, reliability, performance, operational excellence) and for the WAF pillar / area labels in the knowledge base CSV. - **Microsoft Azure AI Landing Zones Checklist** - https://learn.microsoft.com/azure/cloud-adoption-framework/scenarios/ai/. Source of truth for the curated set of Azure-specific checks that ship in `.agentops/waf-checklist.csv`. Each Doctor finding cites the matching WAF item and links to the Microsoft Learn page. ## 11. Next steps - Walk through a full setup with Azure resources: [end-to-end tutorial](tutorial-end-to-end.md). - Open the workspace command center: `agentops cockpit` shows eval history, Doctor findings, CI/CD status, telemetry readiness, and Foundry/Azure navigation. - Audit a repo from CI: there's a ready-made GitHub Actions cron in the tutorial.