> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hawkings.education/llms.txt
> Use this file to discover all available pages before exploring further.

# AI grading

> How AI grading works, what it returns, and how to keep a human in the loop.

AI grading scores a submission against the assignment's rubric and
returns:

* a numeric grade (`grade_ai`),
* a per-criterion breakdown,
* a markdown rationale (`grading_rationale`),
* optional inline annotations on the student's text.

The grade is **never** committed to the gradebook automatically — there
is always a `grade_manual` field that, when set, overrides the AI grade.

## Calling the grader

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.submissions.gradeWithAi("sub_123");
  ```

  ```php PHP theme={null}
  $hk->submissions->gradeWithAi('sub_123');
  ```
</CodeGroup>

That call is async (it returns immediately with `ai_status: "pending"`).
Wait for completion via [polling or webhook](/concepts/ai-generation).

## What you get back

```json theme={null}
{
  "id": "sub_123",
  "ai_status": "ready",
  "grade_ai": 8.5,
  "grading_rationale": "## Clarity (4/4)\nThe student opens with...\n\n## Accuracy (3/4)\n...",
  "grading_breakdown": [
    { "criterion": "clarity", "score": 4, "max": 4 },
    { "criterion": "accuracy", "score": 3, "max": 4 },
    { "criterion": "sources", "score": 1.5, "max": 2 }
  ],
  "evaluated_at": "2026-05-10T12:34:56Z"
}
```

## Configuring the grader

Per-assignment configuration lives on the `Assignment`:

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.assignments.configure(assignmentId, {
    ai_evaluator: {
      enabled: true,
      model: "claude-sonnet-4-6",
      rubric: {
        criteria: [
          { name: "clarity", weight: 0.4 },
          { name: "accuracy", weight: 0.4 },
          { name: "sources", weight: 0.2 },
        ],
        scale: { min: 0, max: 10 },
      },
      feedback: {
        tone: "encouraging" | "neutral" | "demanding",
        verbosity: "short" | "normal" | "detailed",
        language: "en",
      },
    },
  });
  ```

  ```php PHP theme={null}
  $hk->assignments->configure($assignmentId, [
      'ai_evaluator' => [
          'enabled' => true,
          'model' => 'claude-sonnet-4-6',
          'rubric' => [
              'criteria' => [
                  ['name' => 'clarity', 'weight' => 0.4],
                  ['name' => 'accuracy', 'weight' => 0.4],
                  ['name' => 'sources', 'weight' => 0.2],
              ],
              'scale' => ['min' => 0, 'max' => 10],
          ],
          'feedback' => [
              'tone' => 'encouraging',     // 'encouraging' | 'neutral' | 'demanding'
              'verbosity' => 'normal',     // 'short' | 'normal' | 'detailed'
              'language' => 'en',
          ],
      ],
  ]);
  ```
</CodeGroup>

For one-off rubric overrides on a single submission:

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.submissions.gradeWithAi(submissionId, {
    rubric: { /* override */ },
  });
  ```

  ```php PHP theme={null}
  $hk->submissions->gradeWithAi($submissionId, [
      'rubric' => [ /* override */ ],
  ]);
  ```
</CodeGroup>

## Human-in-the-loop

The recommended workflow:

<CodeGroup>
  ```ts TypeScript theme={null}
  // 1. AI grades
  await hk.submissions.gradeWithAi(submissionId);

  // 2. Teacher reviews in your UI; you show grade_ai + grading_rationale.

  // 3. Teacher confirms (or edits)
  await hk.submissions.update(submissionId, {
    grade_manual: 8.5,            // confirm
    grader_comments: "Solid.",
    human_review_status: "reviewed",
  });
  ```

  ```php PHP theme={null}
  // 1. AI grades
  $hk->submissions->gradeWithAi($submissionId);

  // 2. Teacher reviews in your UI; you show grade_ai + grading_rationale.

  // 3. Teacher confirms (or edits)
  $hk->submissions->update($submissionId, [
      'grade_manual' => 8.5,             // confirm
      'grader_comments' => 'Solid.',
      'human_review_status' => 'reviewed',
  ]);
  ```
</CodeGroup>

`human_review_status` flows: `pending` → `reviewed`. You can require
review per assignment (`human_review: "required"`); when set, the grade
shown to students is `null` until a human marks it reviewed.

## Re-grading

If you change the rubric or want a second opinion, call `gradeWithAi`
again. The new result overwrites the previous AI grade; `grade_manual`
is untouched.

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.submissions.gradeWithAi(submissionId);
  ```

  ```php PHP theme={null}
  $hk->submissions->gradeWithAi($submissionId);
  ```
</CodeGroup>

You can also ask for the AI's reasoning *without* a grade — useful when
the teacher wants explanations on a hand-graded submission:

<CodeGroup>
  ```ts TypeScript theme={null}
  await hk.submissions.explainGrade(submissionId);
  // → fills `grading_rationale` based on the existing manual grade
  ```

  ```php PHP theme={null}
  $hk->submissions->explainGrade($submissionId);
  // → fills `grading_rationale` based on the existing manual grade
  ```
</CodeGroup>

## What AI grading is *not*

* **Not** a replacement for the teacher on consequential grades.
* **Not** stable across model versions: pin a `model` if you need
  reproducibility.
* **Not** suitable for grading code, math proofs, or anything where the
  rubric isn't expressible in natural language. Use a custom grader and
  ingest the score via `submissions.update({ grade_manual })` (or
  `$hk->submissions->update($id, ['grade_manual' => ...])`) instead.

## Auditing

Every AI grade carries the model version, the rubric snapshot, and the
prompt fingerprint in `submission.evaluation_meta`. That makes appeals
and audits feasible.

<CodeGroup>
  ```ts TypeScript theme={null}
  const sub = await hk.submissions.retrieve(submissionId, {
    expand: ["evaluation_meta"],
  });

  // sub.evaluation_meta = { model, rubric, prompt_hash, evaluated_at }
  ```

  ```php PHP theme={null}
  $sub = $hk->submissions->retrieve($submissionId, [
      'expand' => ['evaluation_meta'],
  ]);

  // $sub->evaluation_meta = { model, rubric, prompt_hash, evaluated_at }
  ```
</CodeGroup>
