Milestone 2 — Classify Extracted Answers
Status: Complete
Completed: 2026-06-07
Goal
Prove that answers are not all the same kind of thing, and that classification improves validation routing.
Stakeholder story: "We can tell the difference between a fact, a procedure, a recommendation, and an unresolved issue."
What Was Built
Horn Type Classifier
A rule-based classifier (RuleBasedHornTypeClassifier) scores each answer text against nine Robert Horn information types using keyword pattern tables and NLTK POS-tag heuristics. The HybridHornTypeClassifier wraps it and provides a BERT fine-tune integration point for future milestones.
Supported types:
| Type | Governance implication |
|---|---|
fact |
Requires assertion subtype and grammar; highest accountability |
concept |
Definition or explanation; no grammar required |
procedure |
Step-by-step; requires validated, tested steps before approval |
process |
System workflow description |
principle |
Normative guidance; must not be treated as a factual claim |
reference |
List or lookup table |
troubleshooting |
Problem–cause–solution |
recommendation |
Context-specific advice |
open_issue |
Unresolved or TBD; always requires review |
Assertion Subtype Classifier
When the primary type is fact, a FactSubtypeClassifier runs all six MVP subtypes and returns the highest-scoring one:
| Subtype | Typical signals |
|---|---|
existence |
"is available", "there is/are", "out of the box" |
capability |
"supports", "can", "allows", "enables" |
configuration |
"defaults to", "set to", "configured", "settings" |
state |
"is currently", "triggered by", "status" |
constraint |
"requires", "must", "maximum", "prevents" |
temporal |
"as of", "since", year patterns, "version X.Y" |
Assertion Grammar Extractor
For fact candidates the AssertionGrammarExtractor populates structured fields:
- Subject — the entity the assertion is about
- Predicate — the relationship or property asserted
- Object — the value or entity on the receiving end
- Scope — boundary within which the assertion holds (detected via trigger words: "when", "if", "within")
- Temporal boundary — time-scoped validity (detected via year patterns and temporal triggers)
Fields the extractor cannot determine are recorded in unknown_fields for validator attention.
ClassificationService
Orchestrates the three-stage pipeline per candidate:
- Horn type classifier → primary type + confidence + alternatives
- (FACT only) Subtype classifier →
MVPAssertionSubtype - (FACT only) Grammar extractor →
AssertionGrammar - Sets
requires_review = Truewhenconfidence < thresholdortype == OPEN_ISSUE - Persists
ClassificationRecordto Postgres - Emits structured log event
ClassificationRecord
The full, immutable audit record of a classification decision:
ClassificationRecord(
record_id="...",
candidate_id="...",
answer_text="The platform supports localized help sites.",
primary_horn_type=HornType.FACT,
primary_confidence=0.81,
assertion_subtype=MVPAssertionSubtype.CAPABILITY,
assertion_grammar=AssertionGrammar(
subject="platform",
predicate="supports",
object_value="localized portal builds",
scope="locale-specific branches configured",
),
requires_review=False,
model_version="rule-based-v1.0",
)
API Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/v1/classify |
Classify a single candidate answer |
POST |
/v1/classify/batch |
Classify up to 100 candidates |
GET |
/v1/classifications/{candidate_id} |
Get latest classification for a candidate |
Quality Gates
| Gate | Criterion | Result |
|---|---|---|
| Golden set precision | ≥ 85% primary Horn type accuracy | See evaluate.py |
| Golden set recall | ≥ 82% | See evaluate.py |
| Assertion subtype accuracy | ≥ 80% on Fact candidates | See evaluate.py |
| Grammar completeness | ≥ 80% of Facts have subject + predicate | See evaluate.py |
| Reproducibility | Same input = same output | Verified by unit tests |
| Derived flag | OPEN_ISSUE always requires review | Enforced in service |
Run the evaluation: python tests/golden/evaluate.py
Running the Evaluation
# Regex fallback (no NLTK data needed)
python tests/golden/evaluate.py
# With NLTK POS heuristics
python tests/golden/evaluate.py --use-nltk