Improve LlmIntentClassifier NLP coverage#579
Conversation
…entClassifier Replace brittle exact-substring matching with compiled regex patterns that support word-distance gaps, plural forms, and broader verb coverage. Add negative context filtering for negations and questions about other tools. Fix "remove card" being matched as "move card" by reordering checks.
Cover natural language phrasing, plural forms, broader verbs, word-distance gaps, negation filtering, other-tool question filtering, edge cases, and backward compatibility with all existing exact-match patterns.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Remove overly broad `move.*to` pattern from CardMovePattern to prevent matching "move on to the next topic". Replace unbounded `.*` in NegationPattern with bounded word-distance to avoid suppressing intents in compound sentences like "stop the sprint and then create cards".
There was a problem hiding this comment.
Code Review
This pull request significantly improves the LlmIntentClassifier by transitioning from basic substring matching to a more robust Regex-based approach. Key enhancements include the ability to detect natural language patterns, handle negations, and filter out questions regarding external tools. It also resolves a classification bug by reordering intent checks. The review feedback suggests simplifying the new helper methods by removing redundant substring checks that are now encompassed by the regex patterns.
| private static bool MatchesCardCreate(string lower) | ||
| { | ||
| // Exact substring matches (backward compatible) | ||
| if (lower.Contains("create card") || lower.Contains("add card") | ||
| || lower.Contains("create a card") || lower.Contains("add a card") | ||
| || lower.Contains("create task") || lower.Contains("add task") | ||
| || lower.Contains("create a task") || lower.Contains("add a task") | ||
| || lower.Contains("new card") || lower.Contains("new task") | ||
| || lower.Contains("make a card") || lower.Contains("make a task") | ||
| || lower.Contains("make card") || lower.Contains("make task")) | ||
| return (true, "card.create"); | ||
| return true; | ||
|
|
||
| if (lower.Contains("move card")) | ||
| return (true, "card.move"); | ||
| // Regex-based natural language matching | ||
| try | ||
| { | ||
| if (CardCreatePattern.IsMatch(lower)) | ||
| return true; | ||
| if (NewCardPattern.IsMatch(lower)) | ||
| return true; | ||
| } | ||
| catch (RegexMatchTimeoutException) | ||
| { | ||
| // Fall through — don't match on timeout | ||
| } | ||
|
|
||
| return false; | ||
| } |
There was a problem hiding this comment.
This method, and the other Matches... methods that follow, can be significantly simplified.
-
The initial
ifblock withlower.Contains(...)is now redundant. The new regular expressions are supersets that cover these exact-match cases while also providing broader NLP capabilities. Removing these checks simplifies the code without losing backward compatibility. -
The
try-catchlogic can be made more direct by combining the regex checks and returningfalsefrom within thecatchblock.
This refactoring makes the intent clearer and reduces code duplication across all the Matches... methods. The same pattern can be applied to MatchesCardMove, MatchesCardArchive, etc.
Here is a suggested simplification for this method:
private static bool MatchesCardCreate(string lower)
{
try
{
// The regex patterns cover both the old exact matches and new NLP variations.
return CardCreatePattern.IsMatch(lower) || NewCardPattern.IsMatch(lower);
}
catch (RegexMatchTimeoutException)
{
// On timeout, treat as a non-match for safety.
return false;
}
}
Self-Review FindingsRegex Performance
False Positive Analysis
False Negative Analysis
Breaking Changes
Test Coverage
Remaining Risk
|
Adversarial Review — PR #579CriticalNone found. MajorM1. Negation test The test at line 175 asserts this returns non-actionable and attributes it to negation. But
Recommendation: Either (a) add gerund forms to M2. Double negatives bypass negation → false positive
M3. This input contains the literal substring Recommendation: Add a test documenting this known false positive, even if you choose not to fix it. Minorm1. Pattern: The m2.
m3. Ordering sensitivity with
m4. No test for regex special characters in input Input like m5. Compiled regex count (10 patterns) — static memory cost 10 compiled regex patterns are held in static fields. Each compiled regex allocates IL code. This is fine for a singleton classifier but worth noting — if this pattern proliferates to other classifiers, consider Nitsn1. The n2. n3. The comment on line 38 of the classifier says "or move + card/task context + to" but that alternation was removed. Stale comment. Overall AssessmentPass with fixes. The implementation is solid overall — bounded quantifiers, compiled regexes with timeouts, timeout catch blocks, backward-compatible exact-match fast paths, and sensible ordering. The self-review was thorough and caught real issues. The Major items are primarily about test accuracy (M1, M3) and documentation of known limitations (M2). M1 is the most concerning because it represents a false sense of security in the negation logic — the test passes by coincidence rather than by the mechanism it claims to test. M3 is a regression in test coverage from the old suite. Recommended before merge:
|
…x stale comment - Split "stop creating cards" out of negation tests into its own test that documents the real reason it's non-actionable (gerund form invisible to both negation and positive patterns, not negation suppression) - Add test for known false positive: "I deleted the create card button" matches card.create due to literal substring "create card" - Fix stale comment on CardMovePattern that referenced removed alternation
Follow-up: Fixes pushed for review findingsCommit M1 — Gerund test accuracyMoved M3 — Missing false-positive documentationAdded n3 — Stale commentFixed the All 87 tests pass. No behavior changes — only test accuracy and documentation improvements. |
|
Addressed Gemini review feedback: removed redundant |
Update two analysis docs (chat-to-proposal gap and manual testing findings) to reflect recent fixes and testing status. Key changes: add Last Updated and status notes; mark Tier 1 improvements shipped (intent classifier regex/stemming/negation fixes, substring ordering bug, PR #579), UX parse hints shipped (PR #582), unit/integration tests shipped (PR #580), and note PR range #578–#582. In manual testing findings mark OBS-2/OBS-3 resolved (PR #581) and BUG-M5 resolved (PR #578), update resolutions and remove duplicate checklist items. Minor editorial clarifications and test counts added.
Summary
cards?,tasks?,items?), and broader verb coverage (generate,build,prepare,set up)don't create task yet) and questions about other tools (how do I create a card in Jira?) are suppressedremove cardwas misclassified ascard.movedue to substring overlapRegexOptions.Compiledand a 100ms timeout to prevent catastrophic backtrackingTest plan