Ask the alignment grader whether the SKILL.md body + references actually contain enough substance to answer the question. This is the orthogonal check to :func:`grade_one`. A skill can have a perfectly-firing description and still be a hollow shell — this catches that. Returns ``"su
(
skill_content: str,
question: str,
*,
model: str,
)
| 319 | |
| 320 | |
| 321 | async def grade_coverage( |
| 322 | skill_content: str, |
| 323 | question: str, |
| 324 | *, |
| 325 | model: str, |
| 326 | ) -> tuple[Literal["supported", "unsupported", "ambiguous"], str]: |
| 327 | """Ask the alignment grader whether the SKILL.md body + references |
| 328 | actually contain enough substance to answer the question. |
| 329 | |
| 330 | This is the orthogonal check to :func:`grade_one`. A skill can have a |
| 331 | perfectly-firing description and still be a hollow shell — this catches |
| 332 | that. Returns ``"supported"``, ``"unsupported"``, or ``"ambiguous"`` |
| 333 | (parser couldn't extract a verdict from the grader's output) plus a |
| 334 | one-line reason. Callers should NOT collapse ``"ambiguous"`` into |
| 335 | ``"unsupported"`` — see :class:`EvalResult.coverage_ambiguous`. |
| 336 | """ |
| 337 | instructions = ( |
| 338 | "You are auditing a skill for content quality. You will be given " |
| 339 | "the skill's body (SKILL.md without frontmatter) and any " |
| 340 | "reference excerpts, plus a user question that the skill's " |
| 341 | "description claims to handle. Decide whether the body has " |
| 342 | "substantive material to answer the question.\n\n" |
| 343 | "Answer with EXACTLY this two-line shape:\n" |
| 344 | "VERDICT: SUPPORTED (or UNSUPPORTED)\n" |
| 345 | "REASON: <one short sentence>\n\n" |
| 346 | f"{skill_content}" |
| 347 | ) |
| 348 | agent = Agent( |
| 349 | name="coverage-grader", |
| 350 | instructions=instructions, |
| 351 | model=f"litellm/{model}", |
| 352 | model_settings=ModelSettings( |
| 353 | extra_headers=get_extra_headers() or None, |
| 354 | extra_args=get_timeout_extra_args(), |
| 355 | ), |
| 356 | ) |
| 357 | try: |
| 358 | result = await Runner.run(agent, f"Question: {question}", max_turns=2) |
| 359 | except MaxTurnsExceeded as exc: |
| 360 | raise RuntimeError( |
| 361 | f"Coverage grader hit the max-turn cap on question: {question!r}. " |
| 362 | f"Try a more capable model." |
| 363 | ) from exc |
| 364 | raw = (result.final_output or "").strip() |
| 365 | upper = raw.upper() |
| 366 | verdict: Literal["supported", "unsupported", "ambiguous"] |
| 367 | if "UNSUPPORTED" in upper: |
| 368 | verdict = "unsupported" |
| 369 | elif "SUPPORTED" in upper: |
| 370 | verdict = "supported" |
| 371 | else: |
| 372 | # Grader didn't emit a parseable verdict — surface as a distinct |
| 373 | # state so callers can report grader-malfunction separately from |
| 374 | # "the body is hollow." See ``EvalResult.coverage_ambiguous``. |
| 375 | verdict = "ambiguous" |
| 376 | reason = "" |
| 377 | for line in raw.splitlines(): |
| 378 | stripped = line.strip() |