Orchestrata Ultimate › Plans › intent-nl-classifier-design

Extensible NL intent parser + evaluator — design contract (SPEC-003 US-001 extension)

Source: plans/intent-nl-classifier-design.md at 8a2cb42ad · last changed 2026-08-19 · first committed 2026-07-11 · 86 lines.

Prose pending.

Plan text

Extensible NL intent parser + evaluator — design contract (SPEC-003 US-001 extension)

Origin: user directive — "create an intent parser and evaluator step and make it so we can add vocabulary, keywords and a mapper to categories and workflows." This is the honest NL→structured-Intent bridge that lets the real IntentBroker be fed structured intents instead of the gateway's coarse action="create" heuristic (which has no resource_path and doesn't use the IntentAction/IntentTarget taxonomy).

Reuse (do NOT reinvent)

Borrowed architecture — dr-hannah NLQ cascade (~/workspace/dr-hannah/src/dr_hannah/nlq.py + prolog/nlq_grammar.pl)

Three-tier parse cascade with confidence reflecting the backend (borrow this shape):

  1. Prolog DCG grammar (PREFERRED, ~0.9–0.95): a .pl DCG (parse_intent_string(String, Term)) produces a compound term like work_intent(fix, code, Path); composable, testable grammar rules
    • a lexicon (action_word/target_word/priority_word). Extensible by ADDING grammar rules. Mirrors dr-hannah's query(Q) --> task_query(Q) | ... + parse_query_string/2 + phrase/2.
  2. Vocabulary/keyword scoring (FALLBACK, ~0.5–0.7): the data-driven IntentVocabulary below — runtime-addable keywords. Used when no Prolog bridge is wired OR the grammar returns unknown.
  3. Both tiers emit the same ClassifiedIntent; NER (nlp.py) supplies resource_path in both. The classifier mirrors dr-hannah's NaturalLanguageQueryParser(prolog_engine=None) — try Prolog, gracefully fall back — and a _prolog_term_to_classified() (mirrors _prolog_term_to_query).

Components (new)

0. orchestrator/prolog/intent_grammar.pl — DCG intent grammar (borrowed shape)

parse_intent_string(+String, -Term): tokenize (split_string) + phrase(intent(Term), Tokens), Term = unknown on no match. intent(work_intent(WorkType, Target, Rest)) composed from lexicon rules — action/verb words → WorkType (fix/feature/refactor/test/documentation/review/analysis/ spec/dependency/performance), target words → IntentTarget, priority words → priority. Keep it a consulted .pl (variable-bearing rules live in .pl; only GROUND facts ever assertz'd — project rule).

1. orchestrator/intent_vocabulary.py — data-driven, extensible

class IntentVocabulary holds keyword→category registries, all runtime-mutable + config-loadable:

2. orchestrator/intent_classifier.py

Spec + traces

Add REQ-003-US-001-040..049 to specs/003-intent/checklists/requirements.md (extensible vocabulary, keyword→category mapping, category→workflow mapping, evaluator confidence/disambiguation, NER-sourced resource_path). @traces(spec="003", story="US-001", reqs=[...]) on every new class.

Tests (real, no doubles)

tests/orchestrator/test_intent_vocabulary.py + test_intent_classifier.py: default vocab classifies canonical phrases to the right WorkType/action/target/priority; NER extracts a real path from "fix the bug in src/foo.py"; add-a-keyword changes classification (extensibility); from_dict/to_dict round-trip; WorkflowMapper → real PipelineRouter stages; evaluator confidence

Out of scope for THIS build (separate follow-up)

Wiring the evaluator into the gateway/kernel ingress + the broker. Build the parser/evaluator as a self-contained, tested unit first; wire second.