← Applied AI
Applied AI· 2 min read

Advanced Legal Retrieval

.NETAI EngineeringIndian Case Law

Summary

Keyword, hashing-vector, and hybrid search on the same question, plus the SQL a pgvector store would run.

Companion code: 03-advanced-retrieval in indian-legal-ai-lab.

Previous: Part 2: Simple RAG over Indian Court Judgments

One similarity score is a weak search over case law. Court, year, and the words in the query are part of the request.

Filters run before scoring

`SearchFilters` has court, year, case id, and a synthetic flag. `IndexedChunkFilter.Apply` drops rows that do not match. A year filter cannot be rescued by a high vector score, because those rows are already gone. `Year_filter_excludes_other_years` searches `freedom speech` with `Year = 2024` and expects every hit to be from 2024, which in this corpus means the fictional fixtures.

Three scorers, one index

`LegalIndex.Search` accepts `keyword`, `vector`, or `hybrid`.

`KeywordScorer` counts how many distinct query tokens appear in the chunk. `ScoreVector` embeds the query and calls `VectorMath.Cosine`. Hybrid divides each keyword score by the max in the candidate set, then mixes them:

var keyword = max <= 0 ? 0 : keywordScores[i] / max;
var vector = _math.Cosine(embedded, rows[i].Vector);
yield return ((0.55 * keyword) + (0.45 * vector), rows[i].Chunk, rows[i].Record);

Anything with a score of 0 is dropped. `PassageFormatter` flattens whitespace and cuts the display text at 420 characters. The chunk offsets stay on the object.

What the lab printed

`dotnet run --project labs/03-advanced-retrieval` asked `Kharak Singh domiciliary visits`.

MethodTop caseScore
keyword1962 INSC 3894.000
vectorSYN-INJECT-0010.316
hybrid1962 INSC 3890.631

`SYN-INJECT-001` is a fictional fixture. Vector-only search ranked it first. Hybrid search put Kharak Singh first for this query. That is one query, not a general proof that hybrid search is always better.

PostgreSQL, not executed here

`PgVectorSql.InitScript` creates the `vector` extension and a `judgment_chunks` table with `embedding vector(64)`. `SearchSql` filters on court and year and orders by cosine distance. The unit test only checks that the SQL text contains those parameters. Docker was not available when this was built, so the statement was not run.

BM25

`Bm25Scorer` is a fourth method, `bm25`. On `Kharak Singh domiciliary visits` it returned `1962 INSC 389`. That is the same top case as keyword and hybrid for this query, and it is one query.

Next

Part 4: Function Calling and Legal Research Tools

Verified in this revision

BM25 remains a separate method (`K1` 1.2, `B` 0.75). Hybrid is still 0.55 keyword plus 0.45 hash cosine. The semantic HTTP embedder does not replace the index unless a caller uses `EmbeddingService` and rebuilds vectors. pgvector SQL is still text that this machine has not executed.

More in Applied AI