3. Diagnosing
Use an LLM to analyze errors and produce structured diagnosis.
Now that we can detect errors, let's use the LLM to diagnose them. This is where @Type structured output really shines — the model returns exactly the fields we need, verified by the compiler.
The diagnosis function
Ask your LLM to add this, or write it directly:
let diagnose error_report =
diagnose_model @{severity: String, root_cause: String, suggested_fix: String}
`You are a site reliability engineer. Analyze this error report from a service health check and provide a diagnosis.
Error report:
{{ error_report }}
Respond with:
- severity: "critical", "warning", or "info"
- root_cause: a brief explanation of what's likely wrong
- suggested_fix: a specific, actionable fix`;
A few things to notice:
@{severity: String, root_cause: String, suggested_fix: String}— the model must return exactly these fields. The compiler generates a JSON schema from this type annotation and sends it to the model API. If the model returns something that doesn't match, you getParseError.- Template strings — the backtick string with
{{ error_report }}interpolates the error report into the prompt. The interpolation must produce aString. - The return type is
Result<{severity: String, root_cause: String, suggested_fix: String}, QosmError> ! {LLM.Call}.
Chaining with the monitor
Let's connect the monitoring and diagnosis steps:
let check_and_diagnose _ =
match get_errors ∅ with (
| Ok{value: report} ->
match report with (
| "No errors" -> Ok{value: {severity: "info", root_cause: "none", suggested_fix: "none"}}
| errors -> diagnose errors)
| Err{value: e} -> Err{value: e});
If there are no errors, we short-circuit with an "info" result. Otherwise, we send the error report to the model.
Check the effects
Look at the inferred type:
check_and_diagnose : Unit -> Result<{severity: String, root_cause: String, suggested_fix: String}, QosmError>
! {Http.Get, LLM.Call}
Both effects are there — Http.Get from service_get and LLM.Call from diagnose_model. The type system tracked them through two levels of function composition. This is what we meant when we said effects propagate automatically.
Run it
Run check_and_diagnose from the workspace. If the service has errors, you'll see a structured diagnosis with severity, root cause, and a suggested fix. If not, you'll see the "info" shortcircuit.
The model does the hard reasoning work, but the types guarantee the output shape. No JSON parsing surprises, no missing fields at runtime.