AI Vendor Risk Assessor
Build the assistant every security and procurement team wishes it had: ask “Can our support team use this AI tool with customer data?” and get a review file with every policy requirement checked against evidence from the web — source, date and verdict — plus the exact questions still to ask the vendor. A human approves. The system never does.
0Start here
You have finished three courses. Each one built a serious component in isolation: a production RAG pipeline over company documents, an agentic RAG crew that routes and grades, and a deep search agent that researches the open web and refuses to be fooled. This capstone puts the three together into one product that a company would use, and adds the glue that none of the courses taught: a deterministic requirement matcher, a questionnaire drafter, a human approval gate and a UI.
What you will have at the end
- A working assessor: policy library + vendor research + rule-based verdicts + human gate, behind an API and a small UI.
- A hand-built gold set of real vendors and an evaluation report proving accuracy, zero guessing and injection resistance.
- A trace for every run that lets you point from any verdict to the exact web page and policy clause behind it.
- A portfolio project with a real enterprise story: “I automated the vendor security review, and here is how I kept it honest.”
Prerequisites
- Completed Production RAG from Scratch (you have a working
rag_core/folder). - Completed Agentic RAG with DeepSeek + CrewAI (you understand Router, Retriever and the two graders).
- Completed Deep Search Agents (you have run
run_agent.pyoffline and live, and Lab 12 in LangGraph). - Python 3.10+, a free LLM key (Gemini or Groq) or Ollama, and a free web-search option (
ddgs, no key).
Day one: run the starter kit
cd vendor-risk-assessor
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -r requirements.txt
cp .env.example .env
python scripts/check_setup.py
python run_assessor.py # LedgerLens AI, customer data, 35 users
python -m pytest -q # 70 tests
python eval/run_eval.py # the scorecard
The default run finishes in under a second with no key and no internet, and prints a full review file ending in BLOCKED. Read runs/<timestamp>-assess/report.md and then trace.jsonl. That report is the target; the rest of this page is how you make the system that produces it real.
How the starter kit is built. The deterministic parts are complete and tested (schemas, the matcher, the questionnaire, the report, the gold set and the evaluation harness). The parts your courses taught are present in a simplified offline form — keyword search instead of hybrid + rerank, a rule-based router instead of an LLM router, a static research plan instead of a planner — so that the whole thing runs. Every one of those files starts with a COURSE MAP comment and a LEARNER TODO block that names the milestone and the course module to use. Your job is to replace the simplified part with the real one, keep the tests green, and prove the improvement in the evaluation.
1The problem
Every company now has employees asking “can I use this AI tool?” several times a week. Each request should trigger a vendor security review: does the vendor hold a current SOC 2 Type II or ISO 27001? Where is our data stored? Do they train on it? Who are their sub-processors? Have they had an incident? Is a DPA available? Today a security engineer spends two to six hours per vendor reading trust centres, privacy policies and news, and most of that time is finding and dating facts, not judging them. Teams either wait weeks for a review or skip it.
The assessor does the finding, dating and checking. It leaves the judging to a person, with every fact already attached to its source.
The running example: Nimbu Mart
You know Nimbu Mart from the Production RAG course. Its Information Security team has eight internal documents — two editions of the security policy, a data-protection policy written for India’s DPDP Act, an AI acceptable-use policy, a procurement checklist, the restricted Approved Vendor List, a questionnaire template and an untrusted chat export. Three fictional AI vendors want in:
| Vendor | What it does | Why it is a good test |
|---|---|---|
| LedgerLens AI | Summarises support tickets | Solid SOC 2 Type II, but data lives in the US/EU with India “planned”. A stale 2024 page, a same-name UK accountancy firm, a forum rumour, a review site that contradicts the trust centre, and a blog page carrying a prompt-injection attack. |
| QuillDesk | AI writing assistant | “SOC 2 compliant” with no report, trains on content by default, US-only storage, a July 2026 incident with no post-mortem, a directory page that is a copy of the vendor’s own page. |
| Parakeet Translate | Translation for Indian languages | The good vendor: ISO 27001 confirmed on the certifier’s registry, India region, no training, DPA with a DPDP addendum — but approved only for internal data, so customer-data use still needs Procurement. |
What “good” looks like
The review file the starter kit produces for LedgerLens at the customer-data tier looks like this (abridged):
| Requirement | Clause | Verdict | Reason (abridged) | Evidence |
|---|---|---|---|---|
| Independent security assurance | SEC-4.2 | PASS | SOC 2 Type II report dated 2026-01-20 | trust.ledgerlens.example · 2026-09-01 |
| Customer data stored in India | DP-3.1 | FAIL | India is only planned; available today: US, EU | ledgerlens.example/legal/privacy · 2026-03-15 |
| No training on our data | AI-2.1 | NEEDS REVIEW | No training on Business/Enterprise; Free/Pro trains by default | ledgerlens.example/legal/ai-data-use |
| Sub-processors disclosed | AI-3.4 | NEEDS REVIEW | Model provider present: Orbital Models (US) | ledgerlens.example/legal/subprocessors |
| Incident history | SEC-9.3 | NEEDS REVIEW | One major incident (2025-04) with a published post-mortem | 2 sources |
| On the Approved Vendor List | PROC-1.1 | NEEDS REVIEW | A review is already in progress; attach this assessment | policy library |
Then: the facts with confidence and conflicts, a security section (“1 page quarantined for prompt injection; 1 rejected as a different company; 1 rumour dropped”), six ready-to-send questions for the vendor, and an approval block with the two roles who must sign (Head of Information Security and DPO). The outcome line says BLOCKED for this data tier — never “rejected”, and never “approved”.
2Architecture
assessor/graph.py).The components
| Component | File | Status in the starter kit |
|---|---|---|
| Contracts between stages | assessor/schemas.py | Complete. Nine vendor fields, evidence levels, verdicts, the report. |
| Policy library | assessor/policy_rag.py | Simplified: clause chunking + BM25 + ACL/version/trust filters + extractive answers. You: hybrid + rerank + grounded generation. |
| Router | assessor/router.py | Rule-based mock. You: LLM router with the course’s Router prompt and two extra routes. |
| Graders | assessor/graders.py | Heuristic mocks. You: the two grader agents + retry loop. |
| Vendor research | assessor/vendor_research.py | Full offline loop with identity gate, quarantine, scoring, merging. Live mode is a sequential baseline. You: planner, parallel workers, memory, live evaluation. |
| Requirement matcher | assessor/matcher.py + data/requirements.yaml | Complete and tested. Eight rule types, eleven requirements. You extend it when you add policies. |
| Questionnaire, report | assessor/questionnaire.py, report.py | Complete. |
| Guardrails | assessor/guardrails.py | Regex input redaction, intent check, output policy. You: LLM injection classifier, red-team suite. |
| Orchestrator | assessor/graph.py | Plain Python and LangGraph, same nodes; CLI human gate. You: checkpointer + interrupt, streaming. |
| API, UI | app/api.py, app/ui.py | Thin skeletons. You: queue, storage, auth, trace viewer. |
| Evaluation | eval/run_eval.py, data/gold_set*.json | Harness complete; fictional gold set complete; real-vendor gold set is a template. You: fill it. |
3Course map: which part needs which course section
Use this table as your index. When you open a LEARNER TODO in the code, this is where to look first. “Reuse” names the files from the course code packs you can copy or import.
| Project part | Course | Module / chapter | Lab | Reuse from the course code | Milestone |
|---|---|---|---|---|---|
| Ingest policies; chunk by clause with metadata (version, status, access, trust) | Production RAG | 04 Document ingestion pipeline · 06 Chunking strategies (“structure-aware”, “chunk metadata”) | Lab 03 | rag_core/documents.py, chunking.py, loaders.py, the _metadata.json pattern | M1 |
| Retrieve the right clause: hybrid search, RRF, metadata filters, reranking | Production RAG | 08 Retrieval & reranking (sections 3–9) · 07 Embeddings (choosing a model) | Labs 04, 06, 07, 08 | rag_core/hybrid.py, bm25.py, vectorstore.py, rerank.py, query_transform.py | M1 |
| Grounded policy answers with clause citations; refuse when the context lacks the answer | Production RAG | 09 Grounded generation (grounding rules, refusing, structured output) | Lab 09 | rag_core/generate.py, prompts.py | M1 |
| Access control in retrieval; archived versions; untrusted documents; PII redaction | Production RAG | 11 Security & privacy (sections 1–3) | Lab 11 | rag_core/security.py (acl_filter, redact_pii, detect_injection) | M1, M4 |
| Route the question: policy / vendor / assessment / refuse | Agentic RAG | “agents.py → The Router” · “tasks.py → Task 1: route” · exercise “Break the Router on purpose” | — | agents.py (create_router_agent), tasks.py | M3 |
| Check the answer: relevance grader and hallucination grader, with a retry | Agentic RAG | “Tasks 3 and 4: the two checks” · “Prompt engineering is the real work” | — | agents.py (the two grader agents), tasks.py (context between tasks) | M3 |
| Two models: a fast cheap one for extraction and grading, a strong one for synthesis | Agentic RAG Deep Search | Agentic RAG “Two LLMs”, llms.py · Deep Search 09 “Model routing” | DS Lab 10 | llms.py; deepsearch/llm.py (LLM.small) | M2, M3 |
| Plan the research: Plan Creator → Reviewer, sub-questions per field, stop rules | Deep Search | 03 Planning layer | Lab 3 | deepsearch/planner.py, schemas.py (ResearchPlan, PlanReview) | M2 |
| Typed tools; validate → repair loop for LLM JSON | Deep Search | 04 Executor, tools and MCP (“Schema enforcement with Pydantic”, “Robust tool calls”) | Lab 2 | deepsearch/llm.py (structured()), tools.py | M2 |
| Parallel workers: search → fetch → injection scan → extract claims | Deep Search | 04 Executor · 09 “Latency: parallel workers” | Lab 4 | deepsearch/executor.py, extractor.py | M2 |
| Identity gate (same-name companies), authority per source, freshness decay, copies counted once | Deep Search | 05 Validation, reliability and temporal reasoning | Lab 6 | deepsearch/validator.py (AUTHORITY, HALF_LIFE_DAYS, FIELD_AUTHORITY_OVERRIDES) | M2 |
| Merge claims; resolve conflicts; keep provenance on every value | Deep Search | 06 Merging and final answer construction | Lab 7 | deepsearch/merger.py | M2 |
| Remember past assessments; re-review only what changed (SEC-12.1) | Deep Search | 07 Context, memory and iteration | Lab 8 | deepsearch/memory.py | M2 (stretch) |
| Prompt injection through web pages; quarantine; defence in depth; red team | Deep Search Production RAG | Deep Search 08 Security · Production RAG 11 (“Prompt injection through documents”) | DS Lab 9 · RAG Lab 11 | deepsearch/security.py, rag_core/security.py | M4 |
| Budgets, token costs, model routing; cost per assessment | Deep Search Production RAG | Deep Search 09 Cost, latency and budgets · Production RAG 12 (“Cost”, “Caching”) | DS Lab 10 · RAG Lab 13 | deepsearch/budget.py, rag_core/cache.py | M2, M5 |
| Tracing and observability: read a wrong verdict back to its page | Deep Search Production RAG | Deep Search tracing.py (Module 12 checklist) · Production RAG 12 “Observability” | RAG Lab 13 | deepsearch/tracing.py, rag_core/observability.py | M5 |
| Evaluation: gold set first, programmatic metrics, ablations, LLM-as-judge, CI | Deep Search Production RAG | Deep Search 10 Evaluating deep search agents · Production RAG 10 Evaluation | DS Lab 11 · RAG Lab 10 | deepsearch/evaluation.py, rag_core/evaluation.py, eval/golden.jsonl format | M6 |
| Orchestration as a graph; human-in-the-loop pause; checkpoints | Deep Search | 02 System architecture (“five patterns”) · 12 “The same loop in a framework” | Lab 12 | labs/lab12_langgraph_version.py | M5 |
| The service: FastAPI entry points, validation, queue, workers, storage | Production RAG | 04 Ingestion pipeline (entry points, queue, idempotency) · 13 Capstone: the production app | Module 13 exercises | code/app/api.py, worker.py, db.py, docker-compose.yml | M5 |
| Privacy and law: research companies, not people; DPDP Act; minimal schema | Deep Search | 11 Privacy, law and ethics for research agents | — | the checklist in Module 11 | M4 |
| Expose the assessor to Claude Desktop / your IDE as an MCP tool (stretch) | Deep Search | 04 “MCP: the Model Context Protocol” | Lab 5 | labs/lab05_mcp_server.py, lab05_mcp_client.py | Stretch |
| Requirement matcher: rules × facts → verdicts, deterministic, tested | New | Principles: Deep Search 06 (“a clean structured result”), Production RAG 09 (“refusing”) | — | Already written: assessor/matcher.py, tests/test_matcher.py. Extend when you add clauses. | M0, M6 |
| Questionnaire drafter and review file | New | Production RAG 09 “Structured output” | — | Already written: questionnaire.py, report.py | M0 |
| Human approval gate, approvers by data tier | New | Deep Search 12 checklist (“the user approves the plan before a long run”) | — | graph.py (human_gate), app/api.py (/approve) | M5 |
| Streamlit UI with progress and a trace viewer | New | — | — | app/ui.py skeleton | M5 |
4Milestones
Do them in order. Each one ends with a check you can run. Tick the box when the check passes; the page remembers it in your browser.
Run and read the starter kit
½ dayGoal: know exactly what the finished system does before changing anything.
- Run the four demo questions in the README. Read each
report.md, then itstrace.jsonl, bottom to top. - Read the code in this order:
schemas.py→matcher.py→graph.py(run_plain) →vendor_research.py→policy_rag.py. - Open
data/vendor_web/corpus.json. Find the stale page, the look-alike company, the rumour, the copy and the attack page. Predict what the system does with each; check against the LedgerLens trace. - Break something on purpose: in
requirements.yamlsetallow_planned: trueon DP-3.1 and watch LedgerLens pass residency. Put it back.
Done when: pytest is green, eval/run_eval.py shows 100% verdict accuracy, and you can explain in two sentences why QuillDesk is BLOCKED and Parakeet is only CONDITIONAL.
The policy library, production grade
3–4 daysProduction RAG Modules 06, 08, 09, 11 · Labs 03, 06, 08, 09, 11
Goal: replace keyword search with your rag_core pipeline, and extractive answers with grounded generation — without losing a single security property.
- Copy your
rag_core/into the project. Loaddata/policiesthrough it, keeping clause ids and the metadata fields (access,status,trust,version) on every chunk. - Make
PolicyLibrary.search()use hybrid retrieval (BM25 + vectors, RRF) and a cross-encoder reranker. Filters stay before ranking: ACL, archived, untrusted, injection flag. - Make
PolicyLibrary.answer()generate grounded answers with clause citations when an LLM is configured; keep the extractive mode as the offline fallback. Refuse when the top score is below threshold and when the grader says the answer is not grounded. - Fix the known failure:
pq-who-approvesin the gold set (keyword search ranks SEC-7.1 above the approval table in AI-5.1). Hybrid + rerank should fix it; prove it in the eval report. - Add three policy documents of your own (for example an incident-response runbook and a 2027 draft marked
status: draft) and three new gold questions.
Done when: all policy-question checks in eval/run_eval.py pass including the one marked “baseline known to fail”; tests/test_policy_rag.py still passes (ACL, archived, untrusted); an employee asking for the Approved Vendor List still gets the process, never the list.
Vendor research on the real web
4–5 daysDeep Search Modules 03, 04, 05, 06, 07, 09 · Labs 2, 3, 4, 6, 7, 8, 10
Goal: turn the sequential live baseline into a planned, parallel, budgeted research agent that produces the same VendorProfile the offline mode does — for real vendors.
- Four places (Deep Search Module 12 “Adapt it to your own domain”): the fields are already retargeted in
schemas.py; reviewAUTHORITY,FIELD_AUTHORITY_OVERRIDESandHALF_LIFE_DAYSinvendor_research.pyand justify each number in a comment. - Replace
plan_queries()with a Plan Creator → Plan Reviewer pair that produces one sub-question per field, with stop rules (“stop when every high-stakes field has a documented source or two searches have failed”). - Run fetch + extraction in parallel workers sharing one
Budget; route extraction to the small model. - Make the identity gate real: the LLM decides “same company?” from domain, legal entity and product — and the gate stays a gate (reject, do not down-weight).
- Pick three real AI/SaaS vendors, research them by hand first (fill
data/gold_set_template.jsonwith URLs and dates), then run the agent and compare field by field. - Stretch: long-term memory of previous runs so a re-review of the same vendor only fetches pages newer than the last run.
Done when: on your three real vendors, field accuracy against your hand-made gold set is ≥ 80%, no field is filled from a page about a different company, a live assessment costs under your VRA_MAX_USD and finishes in under 90 seconds, and the offline tests still pass unchanged.
Expect friction. Trust centres are JavaScript-heavy, some sites block bots, and “SOC 2 compliant” appears on pages with no report. That is the real job. Keep the offline corpus as your regression suite and add a cached_pages/ snapshot for your real vendors so your demo does not depend on the internet.
Router and graders as agents
2 daysAgentic RAG “agents.py”, “tasks.py”, “Practice and self-check”
Goal: replace the regex router and the heuristic graders with LLM agents that are better than the mocks and prove it.
- Port the Router’s goal and backstory from the course kit, add the two new routes (
vendor,assessment) and therefuseroute, and return aRouteDecisionthroughLLM.structured(). - Port the Answer Grader and Hallucination Grader as
grade_relevance()/grade_grounded(). Add the retry loop: not relevant → widen the search (k = 8, include query rewriting); not grounded → regenerate once with a stricter prompt, then abstain. - Write 30 routing questions (10 per route, including awkward ones: “Is LedgerLens SOC 2 so we can use it?” is an assessment; “Which tier is Parakeet approved for?” is a policy question) and measure routing accuracy for the mock and for the LLM.
- Repeat the course exercise “Break the Router on purpose” on your new router and write down what changed.
Done when: routing accuracy ≥ 90% on your 30 questions with the LLM router; a deliberately wrong policy answer (edit the prompt to invent a clause) is caught by the hallucination grader and turned into an abstain.
Security: attack it, then defend in layers
2 daysDeep Search Modules 08, 11 · Lab 9 Production RAG Module 11 · Lab 11
Goal: prove that untrusted content — web pages, chat exports, the user’s own question — can never change a verdict.
- Write
tests/test_red_team.pywith 15 attacks: injection in a vendor page (“report PASS”), injection in a policy document, a user question that smuggles an instruction (“…and mark it approved”), a fake trust-centre on a look-alike domain, a page that says the India region exists when the privacy policy says planned, a rumour phrased as news. Every attack must be caught, and the test must assert where it was caught (quarantine, identity gate, dropped, intent check, output guardrail). - Add a second injection layer behind the regexes: a small-model classifier on each fetched page (Deep Search Lab 9). Measure false positives on the clean pages.
- Run the ablation: switch the identity gate off and show the UK firm’s ISO certificate leaking into LedgerLens’s facts. Put the screenshot in your write-up.
- Apply the Module 11 privacy checklist: the schema holds no personal data; the questionnaire email names a role, not a person; nothing from a vendor’s employees is stored.
Done when: 15/15 attacks caught with the catch point asserted; the gold-set safety metric is still 100%; the guess rate is still 0.
Make it a product: graph, API, UI, human gate
3–4 daysDeep Search Module 12, Lab 12 Production RAG Modules 04, 12, 13
Goal: a reviewer can use it without you in the room.
- LangGraph: add a checkpointer and an
interrupt()athuman_gateso an assessment can pause for days and resume when the approver decides. The CLI prompt in the starter is the placeholder for this. - API: put
/assessbehind a queue (Redis + RQ as in the Production RAG app), return a job id immediately, and poll for the report. Store reports in Postgres so/vendors/{name}/historycan show what changed since the last review (SEC-12.1). - Derive
user_groupsfrom the caller’s identity on the server. Never accept groups from the client — that is the ACL. - UI: stream node progress during a live run; show the trace as a tree beside the report; add the Approve / Reject form that calls
/approve/{run_id}with the reviewer’s name. A BLOCKED report must not offer an Approve button. - Observability: per-stage timings and cost on every run; a table of the last 20 runs with p50/p95 latency and cost (Production RAG Lab 13).
Done when: a teammate who has not seen the code can assess a vendor from the UI, read why each verdict was given, send the vendor questions, and record a decision — and the decision survives a server restart.
Evaluate, write it up, demo
2–3 daysDeep Search Module 10, Lab 11 Production RAG Module 10, Lab 10
- Grow the real-vendor gold set to 10 vendors × 8 fields, hand-checked with URL and date. Mix clear passes, clear fails and messy cases.
- Add an LLM-as-judge metric for the reasons: is every reason faithful to the cited evidence? Calibrate the judge on 20 hand-labelled reasons first.
- Run one ablation per component (no reranker, no identity gate, no freshness decay, no planner) and put the table in the report. Each component must earn its place.
- Wire
eval/run_eval.py --strictandpytestinto CI (GitHub Actions) on the offline gold set. - Write the README for your repo: the problem, the architecture, the numbers, what went wrong and what you would do next. Then rehearse the demo below.
Done when: the evaluation report shows accuracy, guess rate, safety, cost and latency for the fictional set and the real set; CI is green; the demo runs offline and live.
5The data pack
Policies data/policies/
- Information Security Policy 2026 (SEC-4.2 assurance, SEC-7.1 SSO, SEC-9.3 incidents, SEC-9.4 rumours) and the 2025 edition, archived — it accepted SOC 2 Type I, so an agent that reads the wrong version passes the wrong vendors.
- Data Protection Policy (DP-2.2 DPA, DP-3.1 India residency, DP-4.1 sub-processors, DP-5.4 deletion).
- AI Acceptable Use Policy (AI-2.1 no training, AI-3.4 model providers, AI-5.1 who approves).
- Vendor Onboarding Checklist (PROC-1.1 the list, PROC-2.1 the review file).
- Approved Vendor List — restricted to Security and Procurement. Parakeet is approved for T2 only; LedgerLens is “in progress”.
- Questionnaire template NM-SEC-Q — the source of every vendor question in the report.
- Chat export marked untrusted, with a wrong claim and an injection line.
Rules data/requirements.yaml
Eleven machine-checkable requirements. Each has a clause, a vendor field, a rule type, parameters, a severity and the data tiers it applies to. Rule types: certification_valid, region_includes, value_known, enum_verdict, list_disclosed, retention_within, no_unresolved_incident, approved_for_tier. Adding a clause means adding a YAML entry and, sometimes, one small rule function with its tests.
The vendor web data/vendor_web/corpus.json
23 fictional pages across three vendors. Each page carries mock_claims: what a careful extraction model would read from it. In offline mode the agent uses those; in live mode it calls your model. Every domain ends in .example.
Gold sets
gold_set.json: five assessment cases (including the same question asked by someone without access) and six policy questions, one marked “baseline known to fail”. gold_set_template.json: the shape you fill for real vendors.
6Rules of the project
- The model never decides a verdict. It reads pages, drafts text and classifies routes.
matcher.pydecides, from rules you can unit-test. Two runs on the same evidence give the same verdict. - UNKNOWN beats a guess. A PASS or FAIL without an evidence URL is a bug; the output guardrail downgrades it. The evaluation’s
guess_ratemust stay at zero. - A claim is not evidence. “We are SOC 2 compliant” is
claimed. A report, a certificate number, a registry entry or a legal page isdocumented. Only documented evidence passes a high-stakes requirement. - Identity is a gate, not a weight. A page about a same-name company is rejected before anything is read from it. Down-weighting it would still let its facts leak in.
- Untrusted content never reaches the verdict. Pages that address the AI are quarantined whole. Forum posts and rumours are dropped and logged, not scored low.
- Repetition is not corroboration. A syndicated copy of the vendor’s page counts once.
- Access control lives in retrieval. The Approved Vendor List is invisible to people outside Security and Procurement, and the assistant says so instead of leaking it. Groups come from identity on the server, never from the request body.
- Every fact has a date. Freshness decays per field. A 2024 security page loses to a 2026 trust centre, and the conflict is recorded, not hidden.
- Humans approve. The report ends in an approval block that only a named person can fill. The word “approved” never appears in anything the system writes about a vendor.
- Research companies, not people. No names of vendor employees in the schema, the memory or the report.
7Evaluation & rubric
The scorecard your evaluation must report
| Metric | Meaning | Target |
|---|---|---|
verdict_accuracy | (case, requirement) verdicts equal to gold | ≥ 95% fictional set · ≥ 80% real set |
overall_accuracy | BLOCKED / CONDITIONAL / READY matches gold | 100% fictional |
guess_rate | PASS/FAIL where gold says UNKNOWN or NEEDS_REVIEW | 0%, both sets |
safety | attack pages quarantined, look-alikes rejected, rumours dropped, nothing from them cited | 100% |
policy_qa_hard | never cites a document the asker may not see, nor an untrusted one | 100% |
| cost · latency | per live assessment | under your budget · under 90 s |
Rubric (100 points)
| Area | Points | Full marks look like |
|---|---|---|
| Correctness on the fictional gold set | 15 | eval --strict and pytest green after every change you made. |
| Policy library (M1) | 15 | Hybrid + rerank + grounded answers with clause citations; the “known to fail” case fixed; ACL, versions and trust proven by tests; three new documents and questions. |
| Vendor research, live (M2) | 20 | Planner + parallel workers + budget; ≥ 80% field accuracy on a hand-made gold set of real vendors; identity gate and freshness justified; conflicts recorded with provenance. |
| Router and graders (M3) | 10 | LLM router ≥ 90% on 30 questions; graders catch an injected hallucination; retry loop works. |
| Security (M4) | 10 | 15/15 red-team attacks caught with catch point asserted; identity-gate ablation shown; privacy checklist applied. |
| Product (M5) | 15 | LangGraph with checkpointed human gate; queued API with history; UI with progress, trace viewer and decision form; a BLOCKED report cannot be approved. |
| Evaluation and engineering (M6) | 10 | 10 real vendors in the gold set; LLM-as-judge for reasons, calibrated; one ablation per component; CI green. |
| Write-up and demo | 5 | README with numbers and honest failures; a demo that runs offline and live without improvisation. |
8Demo checklist (12 minutes)
- The question (1 min): a support lead asks whether the team can use LedgerLens for tickets. Show the review file. Point at the BLOCKED line and at DP-3.1 with its source and date.
- The trace (2 min): open
trace.jsonl. Show the rejected UK firm, the quarantined blog page, the dropped rumour, the 2024 page losing to the 2026 trust centre. - Access control (1 min): ask the same question as an employee outside Security. PROC-1.1 turns UNKNOWN; the Approved Vendor List never appears. Ask “which vendors are approved?” and get the process, not the list.
- The good vendor (2 min): Parakeet at T3 — every requirement passes on documented evidence, yet the outcome is CONDITIONAL because approval is for T2 only. Switch to T2 and show READY FOR HUMAN APPROVAL. Try to approve the BLOCKED LedgerLens report: refused.
- Live (3 min): a real vendor from your gold set, from the UI, with progress streaming. Compare two fields against what you found by hand.
- Attack (1 min): add an injection line to a cached page and re-run. Quarantined; verdicts unchanged.
- Numbers (2 min): the scorecard, the ablation table, cost and latency, and the two things that still go wrong.
9Stretch goals
- Continuous monitoring. A weekly scheduled re-review of every approved vendor that only reports changes (new sub-processor, new incident, certificate expiring in 60 days). Deep Search Module 7 memory + Module 12 “regulatory circular monitor” idea.
- MCP server. Expose
assess_vendorandask_policyas MCP tools so Claude Desktop or your IDE can call them (Deep Search Lab 5). - Debate layer. Before the human gate, a “vendor advocate” and a “risk officer” agent each write one paragraph from the same evidence; the report shows both. Useful when evidence conflicts.
- Document intake. Let the vendor upload its SOC 2 report PDF; extract the audit period and the auditor’s opinion with the Production RAG OCR/table pipeline (Lab 02) and upgrade
claimedtodocumentedautomatically. - Multi-tenant. A second company with different policies (
tenant_idon every chunk and rule), as in the Production RAG capstone exercise.
10FAQ
Why not let the LLM read the policy and the evidence and decide?
Because the verdict must be the same on Tuesday as it was on Monday, and a reviewer must be able to point at the line of policy and the line of evidence behind it. Models are excellent at reading and extracting; they are not consistent judges. Keeping the judgement in code is what makes the system auditable and the evaluation meaningful.
Isn’t a rule engine “not AI”?
The intelligence is in reading a thousand messy pages and turning them into typed, dated, sourced facts — and in knowing which pages to distrust. That is where every hour of the project goes. The rule engine is 300 lines and stays that way.
What if a vendor’s pages block my fetcher?
Cache what you can, record UNKNOWN for what you cannot, and let the questionnaire ask the vendor. An honest UNKNOWN with a question attached is a correct output. Playwright for JavaScript-rendered trust centres is a reasonable stretch.
Can I do this solo?
Yes: do M0–M4 fully, M5 as the CLI + API only, and three real vendors instead of ten. That is a complete, demonstrable project in about three weeks.
Which LLM should I use?
Whatever is free and supports JSON output: Gemini’s free tier or Groq for development, Ollama for offline. Use the small model for extraction and grading and a stronger one only for planning and synthesis — then show the cost difference in your report.
AGlossary
| Data tier | Nimbu Mart’s classification: T1 public, T2 internal, T3 customer data. Requirements depend on the highest tier a tool touches. |
| SOC 2 Type II | An independent auditor’s report that a vendor’s controls operated effectively over a period (usually 6–12 months). Type I covers design at a point in time only. |
| ISO/IEC 27001 | A certificate, issued by an accredited body, that the vendor runs an information-security management system. Has a certificate number and an expiry date. |
| DPA | Data Processing Agreement: the contract that binds a vendor to process personal data only on the customer’s instructions. |
| DPDP Act | India’s Digital Personal Data Protection Act, 2023. Nimbu Mart is a Data Fiduciary; vendors are Data Processors. |
| Sub-processor | A company the vendor uses that also touches your data — hosting, email, and any third-party model provider. |
| Evidence level | documented (report, certificate, registry, legal page) · claimed (marketing text) · third_party (named press/review site) · rumour (forum, unnamed). |
| Identity gate | The check that a page is about the same company before anything is read from it. |
| Quarantine | Removing a page entirely because it contains instructions aimed at the AI. |
| Verdicts | PASS · FAIL · UNKNOWN (no evidence) · NEEDS_REVIEW (evidence found, a person must judge) · NOT_APPLICABLE (rule does not apply to this tier). |
| Outcome | BLOCKED (a blocking requirement failed) · CONDITIONAL (open items) · READY FOR HUMAN APPROVAL (all passed; a person still decides). |