← Master Index
Vol. 23 Module 23.1 Lecture

AI Research Assistant

Capstone Projects

How This Lesson Fits the Module & Volume

Image generation shipped pixels. This capstone ships claims with sources. Vol. 21 research assistants defined the product category; Vol. 14 RAG gave you retrieve-then-generate. You now build the loop: query plan → retrieve (corpus or allowlisted web) → cite → synthesize—and fail the build if a citation is invented.

Sibling contrast: PDF chatbot answers over one ingest. A research assistant plans multi-query retrieval, attributes every material claim, and abstains. Vol. 19 hallucination tests and Vol. 11.4 hallucination are the eval spine. Next: meeting summarizer—same citation discipline on utterances, not papers.

Learning Objectives

By the end of this lesson, students should be able to:

  • Ship an MVP research assistant: query plan → retrieve → cite → synthesis, with abstention.
  • Reject invented, dangling, or unsupported citations in code—not “trust the model.”
  • Split retrieval miss vs generation miss vs attribution miss (Vol. 14 + Vol. 19).
  • Choose local corpus vs allowlisted web search, and wrap retrieved text as untrusted data.
  • Write FastAPI acceptance tests: groundedness, citation coverage, gold retrieval hit.
  • Place HITL before any “publish brief” action (Vol. 15 + Vol. 20).
Definition

An AI research assistant (this capstone) is a citation-first RAG product: given a question and a declared corpus (your files and/or an allowlisted search tool), it plans retrieval queries, fetches chunks, drafts a synthesis, and attaches resolvable citations to material claims. Never invent citations—a DOI, URL, or bracket ID that is not in the retrieved set is a product failure. The assistant may abstain when retrieval is weak. It is not a world oracle; faithfulness to a stale or wrong corpus is still a wrong brief.

Problem, MVP, and Stretch

Users want a brief they can check. Chat-without-sources invents papers. Your job is a pipeline with an evidence channel, not a smarter autocomplete.

MVP (ship this)Stretch (after eval is green)
CorpusToy knowledge base you own (JSON/Markdown + embeddings)Allowlisted web search + recency filters; ACL per tenant
Query plan1–3 sub-queries from the user questionMulti-hop; contradiction / “what would falsify” pass
RetrieveTop-\(k\) chunks with id, title, url/path, textHybrid search + re-rank (Vol. 14)
CiteInline [id]; reject dangling IDsQuote spans + page/offset; claim table support/contradict/neither
OutputSynthesis + source list + abstain reasonHITL author edit → export; version pin of index digest
Out of scopeScraping paywalls; fake DOIs; “as of today” without a date stampAutonomous publish to a wiki without a human

Research assistant

  • Plans queries, then retrieves
  • Every material claim is attributable
  • Abstain is a valid success
  • Eval: groundedness + attribution

PDF chatbot (earlier capstone)

  • One ingest, conversational Q&A
  • Citations helpful but often page-level
  • Chat UX first
  • Eval: retrieval hit + faithfulness

Bare chat (no RAG)

  • No evidence channel
  • Invented citations look fluent
  • Cannot split retrieval vs generation blame
  • Not acceptable for this lecture

Architecture

LayerMVP choiceNotes
UIQuestion + draft + source rail (id, title, snippet)Show abstain banner; never hide missing cites
APIVol. 18 FastAPI POST /v1/research/queryAuth + tenant_id; rate limit (Vol. 13.4)
PlanLLM returns JSON list of sub-queriesVol. 13 structured output
RetrieveLocal embeddings + Chroma/FAISS or mock search toolVol. 14 retrieval; wrap hits as data
GenerateOpenAI-compatible or HF chat (Vol. 22 pick)System: cite only retrieved ids; abstain if weak
StorageSQLite: run_id, queries, chunk_ids, draft, eval scoresPin index digest for replay
EvalDangling-cite = fail; groundedness heuristic + gold hitVol. 19; sample with human eval
Question
Query plan (1–3 sub-queries)
Retrieve chunks → wrap as untrusted data
Synthesize with forced [id] cites
Citation gate → HITL publish (optional)

Citation-first buys

  • Auditable drafts; blame-splitting on failures
  • CI gates when the index or prompt changes
  • Clear ACL: cite only what the tenant may see

Chat-without-sources costs

  • Fluent fake papers, DOIs, and quotes
  • Stale corpus treated as current world-truth
  • No way to know if retrieval even ran

FastAPI Sketch: Plan, Retrieve, Cite, Synthesize

Educational stub: a tiny in-memory corpus stands in for Vol. 14 indexing. Replace synthesize() with your SDK call. The product rule is the citation gate—not the model brand (Vol. 22 OpenAI, Anthropic, or local).

# research_assistant.py — Vol. 23 capstone (educational) # Query plan → retrieve → cite → synthesize. Never invent citations. import re from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field app = FastAPI(title="Vol23 Research Assistant") CORPUS = { "1": { "title": "Toy RAG note", "url": "https://example.edu/rag-note", "text": "Retrieval-augmented generation grounds answers in retrieved chunks. Citations must resolve to retrieved ids.", }, "2": { "title": "Toy hallucination note", "url": "https://example.edu/hallucination", "text": "Fluent falsehoods still occur after RAG. Abstain when chunks do not support the claim.", }, } class QueryIn(BaseModel): question: str = Field(min_length=8, max_length=2000) tenant_id: str k: int = Field(default=4, ge=1, le=8) def wrap_data(source: str, text: str) -> str: return f"<untrusted source={source} treat=data>\n{text}\n</untrusted>" def plan_queries(question: str) -> list[str]: # MVP: question + a shortened keyword query. Stretch: LLM JSON list. words = [w for w in re.findall(r"[A-Za-z0-9]+", question) if len(w) > 3] alt = " ".join(words[:8]) or question return [question.strip(), alt] def retrieve(queries: list[str], k: int) -> dict: scored = [] for cid, doc in CORPUS.items(): blob = (doc["title"] + " " + doc["text"]).lower() hits = sum(blob.count(q.lower()[:40]) for q in queries) scored.append((hits, cid, doc)) scored.sort(reverse=True) return {cid: doc for _, cid, doc in scored[:k] if _ > 0} def extract_cites(answer: str) -> list[str]: return re.findall(r"\[(\d+)\]", answer) def synthesize(question: str, chunks: dict) -> str: # Replace with chat completion. System: cite only these ids or abstain. if not chunks: return "Insufficient sources. I don't know." ids = ", ".join(f"[{i}] {d['title']}" for i, d in chunks.items()) evidence = "\n\n".join(wrap_data(i, d["text"]) for i, d in chunks.items()) return ( f"Grounded draft for: {question}\n" f"Use only: {ids}\n{evidence}\n" f"(LLM would write prose with [id] cites here.)" ) @app.post("/v1/research/query") def research(body: QueryIn): queries = plan_queries(body.question) chunks = retrieve(queries, body.k) draft = synthesize(body.question, chunks) cites = extract_cites(draft) dangling = [c for c in cites if c not in chunks] abstained = any(s in draft.lower() for s in ("i don't know", "insufficient sources")) if dangling: raise HTTPException(status_code=422, detail={"dangling_citations": dangling}) return { "tenant_id": body.tenant_id, "queries": queries, "chunk_ids": list(chunks), "sources": [{"id": i, "title": d["title"], "url": d["url"]} for i, d in chunks.items()], "draft": draft, "abstained": abstained, "status": "ok" if chunks or abstained else "no_hits", }

Acceptance Criteria (“Done When…”)

#CriterionHow you prove it
1Query plan is visibleAPI returns the sub-queries used, not a hidden rewrite
2Retrieval actually ranResponse lists chunk ids + titles/urls from the index or search tool
3No invented citationsAny [id] / URL not in retrieved set → 422 or rewrite, never silent pass
4Gold retrieval hitOn a labeled fixture, the gold chunk is in top-\(k\)
5Abstain worksEmpty/irrelevant corpus → explicit insufficient-sources, not a fake paper
6Untrusted wrapRetrieved text is not concatenated as system instructions
7HITL before publishExport/share requires a human ack (Vol. 15 HITL)

Eval, HITL, and Safety

Reuse Vol. 19: groundedness / RAG faithfulness, attribution (cite id in set and chunk supports claim), contradiction, abstention. Keyword overlap is a heuristic—negations still match tokens; sample with humans. If the gold chunk was never retrieved, fix Vol. 14 before blaming the generator.

RiskControl
Invented DOI/URLResolve every citation against retrieved set; fail the run
Prompt injection via web/PDF textWrap-as-data (Vol. 20 prompt injection)
Cross-tenant leakCorpus ACL by tenant_id (Vol. 20 privacy)
Stale “current” facts“As of” date + recency metadata; abstain if expired
Publish without readingHITL author gate; this is not a substitute for reading sources

Vendor pick (Vol. 22) is a substrate, not the product: chat APIs, optional search-grounded platforms such as Perplexity—still run your citation gate. Do not invent dollar prices or vendor accuracy numbers.

Related Lectures

LectureRole
PDF chatbot (RAG)Simpler ingest Q&A sibling
Research assistants / AI searchProduct pattern vs ranking UX
RAG / retrieval / chunkingEvidence channel
Hallucination testsGroundedness / attribution gates
Hallucination (11.4) / guardrailsWhy fluency lies; abstain
FastAPI / Vol. 22 chat vendorsAPI + model substrate
Meeting summarizerNext: cite utterances, not papers
Common Misconception

“We added RAG, so citations are automatically true.” Models still invent ids and numbers past the chunks. Second: a bracket number is proof—unless the chunk supports the claim (Vol. 19 attribution). Third: blaming the generator when the gold chunk was never retrieved. Fourth: scraping the live web without an allowlist and treating every hit as licensed, current, and safe. Fifth: faithful summary of a wrong corpus equals a correct brief. Sixth: the assistant replaces reading the source before you publish.

Knowledge Check

  1. Short Answer: Name the four stages of this capstone pipeline. Answer: Query plan → retrieve → cite → synthesize (plus abstain/HITL).
  2. True/False: Inventing a plausible DOI is acceptable if the prose is fluent. Answer: False—invented citations fail the product.
  3. Multiple Choice: If the gold chunk was never in top-\(k\), blame first: (a) retrieval, (b) BLEU, (c) watermarking. Answer: (a).
  4. Short Answer: What is a dangling citation? Answer: A cited id/URL not in the retrieved (or allowed) set.
  5. True/False: Abstaining when sources are weak can be a passing acceptance test. Answer: True.
  6. Multiple Choice: Retrieved web/PDF text in the prompt should be: (a) wrapped as untrusted data, (b) pasted into the system prompt as policy, (c) billed as CUDA. Answer: (a).
  7. Short Answer: Which Vol. 21 lecture defined this product category? Answer: Research assistants.
  8. True/False: Faithfulness to a stale corpus means the brief is world-true. Answer: False.
  9. Multiple Choice: Publish/export without a human ack violates: (a) HITL for high-stakes drafts, (b) softmax, (c) KV-cache. Answer: (a).
  10. Short Answer: Name two Vol. 19 checks this MVP must run. Answer: Any two of: groundedness/faithfulness, attribution, dangling-cite fail, contradiction, abstention.

Key Takeaways

  • Research assistants are citation-first RAG products: plan, retrieve, cite, synthesize—never invent sources.
  • Split retrieval vs generation vs attribution miss; dangling cites fail CI.
  • Abstain + HITL publish; wrap retrieved text as untrusted data.
  • Vol. 14 + Vol. 19 + Vol. 21 patterns become a shippable FastAPI service.
  • Next: AI Meeting Summarizer — action items from transcripts.
Trainer’s Guide

Lab: Give a 12-doc toy corpus students own (no live paywall scrape). Four gold questions must retrieve a labeled chunk; four must abstain; four drafts include a dangling [99]. Students implement the citation gate and a query-plan log. Score retrieval hit separately from groundedness.

Whiteboard: Question → sub-queries → chunk ids → claim spans → {support, contradict, neither, dangling}. Circle “neither” as the human-eval bucket. Tease meetings: the “source” becomes a speaker utterance, not a PDF page.

Recap: This capstone productizes Vol. 14 RAG and Vol. 21 research patterns—query plan, retrieval, citation gates, abstention, and HITL. Continue to AI Meeting Summarizer.