Guard ungrounded answers in sec ask with a grounded flag - #356
Merged
Conversation
… retrieved `sec ask` is the command the re-founding is named for, and on an empty or non-matching index it was neither retrieval-grounded nor about filings. Reproduced against a freshly `db setup` database: eleven sentences of confident, unsourced prose putting Apple's FY2023 revenue at ~$383bn, `references: []`, exit 0. It is the first thing a new user does after `sec setup`. The invariant was expressed once, in a sentence in the system prompt. The default path is a 350M local model — deliberately, so `ask` works with no API key — and it does not honour that sentence. A model instruction is a request; this is the guard. Zero references is now a code outcome in `AskTask.execute`, not the renderer: a check in the CLI group would leave every `--json` consumer with the fabrication. The output carries a `grounded` field, so a caller reads the discriminator instead of inferring that an answer is fiction from an array's length. The message names the recovery, and which recovery depends on the state: `kb.chunkCount()` separates "nothing is indexed" (run `sec index`, or `sec update documents` if nothing is converted) from "indexed, but nothing matched", where telling the operator to build the index would be wrong. The weak-match case comes out of the same mechanism rather than a second threshold: `minScore` is now stated here (0.3) instead of inherited from `AiChatWithKbTask`'s default, so a corpus holding only irrelevant chunks produces no references and takes the same path. `AskTask` had no test file at all — 164 lines, untested. It has one now, covering both ungrounded arms, the grounded passthrough, the stated floor, and every scope flag's contribution to the prompt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LWp6Z6wvAPDaDCjAFcTSj6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
groundedboolean field toAskTaskOutputand prevents the model's unsourced answers from being presented to users when no filing text was retrieved. When retrieval fails, users now see a helpful message directing them to build or expand the index instead of confident-sounding fiction.Changes
Added
groundedfield toAskTaskOutput: A boolean that explicitly signals whether the answer was backed by retrieved filing text, so callers (including--jsonconsumers) don't have to infer it from an emptyreferencesarray.Introduced
ASK_MIN_SCOREconstant: States the score floor (0.3) for a chunk to count as a match, rather than inheriting the library default. This makes the grounding decision explicit and decoupled from upstream changes.Added
ungroundedAnswer()helper: Returns context-aware guidance when retrieval fails:sec indexorsec update documentsModified
AskTask.execute():minScore: ASK_MIN_SCOREtoAiChatWithKbTaskreferences.length === 0after retrievalgrounded: falsefor ungrounded responses,grounded: trueotherwiseUpdated CLI rendering in
ask.ts: Whengroundedis false, prints the guidance message without model attribution and suggests thesec indexcommand as the next step, rather than treating it as a normal answer.Added comprehensive test suite: 140 lines of tests covering the grounding logic, scope flag handling, score floor, and the distinction between empty and unmatched indexes.
Implementation notes
The guard is placed in the task layer rather than the renderer, so both CLI and
--jsonconsumers get the same refusal. This prevents a 350M local model (the default path) from confidently answering questions about companies whose filings the database doesn't contain.https://claude.ai/code/session_01LWp6Z6wvAPDaDCjAFcTSj6