Skip to content

feat: local retrieval and inspectable source selection with full regression evidence - #4

Merged
rustfuture merged 7 commits into
mainfrom
feat/real-model-integration-and-eval
Sep 16, 2026
Merged

rustfuture merged 7 commits into
mainfrom
feat/real-model-integration-and-eval

Conversation

@rustfuture

@rustfuture rustfuture commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Problem and behavior

The previous citation guard treated word overlap as factual support. This PR
adds local neural retrieval and now limits CLI answers to source selection:
the model returns evidence IDs, the application renders verbatim excerpts,
and malformed or mixed prose/ID responses are refused atomically.

--answer-json records model calls, raw selection, resolved sources and refusal
reasons. Quotation integrity is explicitly separate from semantic relevance.
The experimental heuristic API is retained but is not used to approve CLI answers.

Validation

  • fmt, all-targets check, clippy -D warnings, 56 Rust tests and 4 evaluator tests pass locally.
  • All 42 previously exposed held-out questions run against installed local
    qwen2.5-coder:1.5b and nomic-embed-text, with model digests and source hashes.
  • 30 answerable: 18 expected source covered, 8 irrelevant selections, 1 partial,
    3 false refusals. 12 traps: 9 correct refusals, 3 false selections.
  • 41 model calls; 1 pre-model refusal. Accepted excerpts match original text.
  • Raw results and manual source checks: evaluation/v3/run-01/.

Limits

This is an inspectable source-selection product, not verified free-form synthesis.
Incorrect or malicious repository claims can still be selected and quoted as data.
No universal prompt-injection defense, 100% refusal, automatic hash fallback,
or semantic entailment guarantee is claimed. Historical v1/v2 metrics do not
describe the current answering contract. The corpus is synthetic regression data.

PR remains open for review; no merge or visibility change.

…uard, demo

## Embedding (Aşama 2)
- OllamaEmbedding: zero-dependency HTTP/1.0 to 127.0.0.1:11434/api/embed
  with L2 normalization and offline HashEmbedding fallback
- default_provider_from_env() supports RI_EMBEDDING_PROVIDER env + --embedding CLI flag
- Index::save_to / load_from serialize provider+dimension; reject dimension mismatch
  with io::ErrorKind::InvalidData

## Answer Generation (Aşama 3-4)
- OllamaProvider: stdin(Stdio::null()) + --nowordwrap + ANSI strip_ansi()
- Strengthened prompt: repository text explicitly marked as untrusted data;
  never instructions; indirect prompt injection neutralized
- Citation guard in main.rs: validates [path:line] / [path:start-end] against
  evidence spans; zero verified citations OR model says 'Insufficient...' → exact refusal

## Evaluation (Aşama 5)
- 42-question benchmark (30 answerable, 12 trap/adversarial)
- New corpus files: retrieval.rs, storage.rs, security.rs
- Results (hybrid_nomic, heldout MRR 0.9750):
  Lexical:         Recall@5 1.000  MRR 1.0000  p50 73ms
  Semantic hash:   Recall@5 1.000  MRR 0.8428  p50 73ms
  Hybrid hash:     Recall@5 1.000  MRR 0.9667  p50 74ms
  Semantic nomic:  Recall@5 1.000  MRR 0.9178  p50 484ms
  Hybrid nomic:    Recall@5 1.000  MRR 0.9667  p50 485ms
  Trap refusal: 12/12 (100%)

## Regression Tests (Aşama 6)
- 5 new tests: dimension_mismatch, dirty_worktree, file_deletion_purge,
  unanswerable_refusal, ollama_embedding_roundtrip_or_offline_fallback
- Total: 25 tests (13 unit + 12 integration) — all pass

## Demo & Docs
- scripts/demo.sh: 7-step reproducible end-to-end demo
  (Ollama health, lexical, semantic, hybrid, answer, refusal+injection, HTTP)
- README.md: full rewrite with benchmark table, model details, demo instructions

Ollama models: nomic-embed-text:latest (768-dim), qwen2.5-coder:1.5b
Zero external crate dependencies (Cargo.toml [dependencies] = empty)
- Create src/citation.rs with strict parser and verifier:
  * Support Evidence IDs [E1], [E2] alongside span citations [path:start-end]
  * Strict line-level citation enforcement; zero uncited claims policy
  * Reject out-of-bounds, reversed ranges (start > end), non-numeric, and mixed valid/invalid citations on single line
- Make EmbeddingProvider return io::Result<Vec<f32>>:
  * Check expected dimension, reject NaN/inf vectors
  * Reject HTTPS endpoint on local stream; enforce connect (5s), read (30s), write (10s) timeouts
  * Escape tabs and all control characters in JSON serialization
- Guard child processes in OllamaProvider:
  * 60s timeout with explicit kill, bounded stdout (64KB) and stderr (4KB)
- Prevent index file from indexing itself by ignoring .ri extension
- Add comprehensive regression tests in tests/ri_regression.rs:
  * Mixed valid and invalid citation rejection
  * Out of bounds and reversed range rejection
  * Single line uncited claim rejection
  * Prompt injection fixture neutralization
  * HTTPS endpoint rejection and provider dimension mismatch
- Add scripts/reproducible_lifecycle_demo.sh:
  * 8-step lifecycle demo using isolated git workspace
  * Initial indexing, hybrid retrieval, grounded real Qwen answer with citation verification
  * Exact refusal on unanswerable trap
  * In-place file modification with old terms purged
  * File deletion with chunks purged
  * Clean teardown and git snapshot verification
- Add evaluation/v2/ suite:
  * 52-question dataset (10 dev, 42 heldout, 12 traps)
  * 4-mode comparative benchmarking (lexical, hash_baseline, neural_nomic, hybrid_nomic)
  * Real LLM answering and citation guard evaluation
…t attribution

- Implement resolve_citation for application-side source mapping of Evidence IDs and spans
- Implement evidence_supports_claim to verify substantive token overlap between assertion and cited chunk:
  * Detect and refuse irrelevant citations where span exists but does not support claim (e.g. reload endpoint citing block_symlink_traversal)
  * Ensure zero uncited claims policy
  * Prevent counter corruption on mixed valid/invalid citations on single line
- Add regression test answer_command_rejects_irrelevant_citation_even_if_span_exists
- Update evaluation/v2 results with 100% trap refusal
@rustfuture rustfuture changed the title feat: real local model integration, 42-question benchmark, citation guard & demo feat: local retrieval and inspectable source selection with full regression evidence Sep 15, 2026
…tegrity tooling

Follow-up to the extractive-selection change. The old 12/12 trap-refusal figure
was still reachable through evaluate_modes.py and the smoke scripts, and the v3
report was a bare summary dump.

- evaluation/evaluate_modes.py: trap outcomes are now classified as
  pre_model_refusal (no lexical anchor, model never called), model refusal,
  guard rejection, accepted-on-unanswerable, or provider error. No single
  'refusal accuracy' number is reported; the script skips when Ollama is absent
  instead of silently falling back to an external provider. results_modes.json
  regenerated: 12 traps -> 8 pre-model refusals, 4 guard rejections, 0 false
  accepts.
- evaluation/v3/evaluate.py: reports are rendered from raw.jsonl with a
  per-question verdict table, true numerators/denominators, and derived
  false_accepts / false_rejects / model_called / pre_model_refusals. Added
  --render-only to re-render a run with no model calls; run-01 regenerated.
- tests/ri_regression.rs: assert every evidence chunk's stored text matches its
  declared path:start-end span (quotation integrity).
- scripts/validate_citations.py: validates the extractive contract (accepted
  decision, in-range citations, exact verbatim quotation) instead of requiring
  concept words in free-form output; corpus-relative path base fixed.
- scripts/llm_answer_smoke.sh, scripts/prompt_injection_smoke.sh, scripts/demo.sh:
  check only model-authored output for the injected sentinel and stop presenting
  concept-word presence or a single fixture as injection immunity.
- docs/llm-validation.md rewritten for extractive-selection-v1; README,
  RELEASE_NOTES.md, evaluation/v2/README.md and evaluation/v3/README.md record
  that v1/v2 claims are historical and must not be pooled with v3.
@rustfuture
rustfuture merged commit 5dfec93 into main Sep 16, 2026
2 checks passed
@rustfuture
rustfuture deleted the feat/real-model-integration-and-eval branch September 16, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant