Build an Explainable eDiscovery Fact-Investigation Workflow with Citation Trails
DISCO's Advanced Research GA (Aug 13, 2026) proved agentic eDiscovery can investigate facts autonomously — but legal defensibility demands citations on every finding. This workflow builds fact-finder, a LangGraph pipeline where an investigator agent plans a line of inquiry, runs multi-step searches across a document corpus, assembles findings with per-claim citation trails, and routes the trail through a human review gate before anything is admitted as a conclusion.
Deepak Bagada
CEO, SaaSNext
- DISCO launched Advanced Research GA on Aug 13, 2026: agentic eDiscovery with autonomous multi-step reasoning and detailed visibility into its decision-making.
- fact-finder operationalizes that pattern: plan an inquiry, search iteratively, and assemble findings where every claim carries a citation trail.
- The citation trail is the compliance surface — in a profession built on defensibility, no claim is admitted without source documents.
- A human review gate validates the trail before conclusions are finalized; autonomy is earned with a verified citation rate.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
When DISCO launched Advanced Research for general availability on August 13, 2026, it proved that agentic eDiscovery works: an AI agent can plan a line of inquiry, run multi-step searches across a document corpus, and assemble findings autonomously. The feature that makes it viable in legal practice is the one DISCO led with — detailed visibility into its decision-making. Legal work is built on defensibility, and defensibility requires citations. This dispatch builds fact-finder, a LangGraph workflow that implements the pattern: an investigator agent plans the inquiry, searches iteratively, and assembles findings where every claim carries a citation trail to source documents — with a human review gate validating the trail before any conclusion is finalized. The latest AI news hub has tracked agentic legal AI all year; this is the workflow for the investigation desk.
Why the citation trail is the compliance surface
The difference between a legal AI finding and a hallucination is the citation. An agent that returns "the vendor knew about the defect in March" without sources forces the reviewer to redo the investigation to verify it — which defeats the efficiency. An agent that returns the same claim with the three emails it read and the passages supporting it produces a reviewable artifact. fact-finder makes the citation trail structural: findings are assembled from evidence nodes, and a finding without evidence is not a finding — it is an error the workflow refuses to emit. That is the same audit-before-action discipline running through the AI workflows library for every agent decision.
Architecture
flowchart TD
A[Investigation brief] --> B[Plan line of inquiry]
B --> C[Execute search step]
C --> D[Read + cite passages]
D --> E{More leads?}
E -- yes --> C
E -- no --> F[Assemble findings with citation trails]
F --> G[Human review gate]
G -- approved --> H[Finalize report]
G -- needs work --> I[Return to planner with feedback]
H --> J[Audit trail store]
Project setup
mkdir fact-finder && cd fact-finder
python -m venv .venv && source .venv/bin/activate
pip install langgraph langchain-openai pydantic
# .env
OPENAI_API_KEY=sk-...
CORPUS_INDEX_URL=http://search.internal/es # Elasticsearch / vector index
SEARCH_INDEX=case-1042
MAX_SEARCH_STEPS=12
MIN_CITATIONS_PER_CLAIM=1
REVIEW_CHANNEL=slack
AUDIT_LOG_PATH=./audit/fact-finder.log
schemas.py
from pydantic import BaseModel, Field
from typing import Optional
class InquiryPlan(BaseModel):
objective: str
hypotheses: list[str] = Field(default_factory=list)
search_queries: list[str] = Field(default_factory=list)
class Citation(BaseModel):
doc_id: str
passage: str
url: str = ""
class Claim(BaseModel):
text: str
citations: list[Citation] = Field(default_factory=list)
class Finding(BaseModel):
id: str
topic: str
claims: list[Claim] = Field(default_factory=list)
confidence: float = Field(0.0, ge=0.0, le=1.0)
class ReviewDecision(BaseModel):
finding_id: str
approved: bool
reviewer: str
note: str = ""
tools.py
import os
import httpx
from schemas import InquiryPlan, Claim, Citation
INDEX_URL = os.getenv("CORPUS_INDEX_URL")
INDEX = os.getenv("SEARCH_INDEX")
MIN_CIT = int(os.getenv("MIN_CITATIONS_PER_CLAIM", "1"))
async def search(query: str, size: int = 10) -> list[dict]:
async with httpx.AsyncClient() as c:
r = await c.post(f"{INDEX_URL}/{INDEX}/_search", json={"query": {"match": {"content": query}}, "size": size})
hits = r.json().get("hits", {}).get("hits", [])
return [{"doc_id": h["_id"], "content": h["_source"]["content"], "url": h["_source"].get("url", "")} for h in hits]
async def extract_cited_claims(hits: list[dict], claim_texts: list[str]) -> list[Claim]:
claims = []
for text in claim_texts:
cits = []
for h in hits:
if text[:60].lower() in h["content"].lower() or any(w in h["content"].lower() for w in text.lower().split()[:5]):
cits.append(Citation(doc_id=h["doc_id"], passage=h["content"][:400], url=h["url"]))
if len(cits) >= MIN_CIT:
claims.append(Claim(text=text, citations=cits))
return claims
graph.py
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END
from schemas import InquiryPlan, Finding
from tools import search, extract_cited_claims
class FinderState(TypedDict):
brief: str
plan: InquiryPlan
findings: list[Finding]
step: int
final: bool
async def plan_node(state: FinderState) -> FinderState:
# Planner agent derives hypotheses + queries from the brief
plan = InquiryPlan(objective=state["brief"], hypotheses=["vendor notified of defect"], search_queries=["vendor defect notice", "quality issue email"])
return {**state, "plan": plan, "step": 0}
async def search_node(state: FinderState) -> FinderState:
q = state["plan"].search_queries[state["step"] % len(state["plan"].search_queries)]
hits = await search(q)
claims = await extract_cited_claims(hits, [f"finding for {q}"])
finding = Finding(id=f"F{state['step']}", topic=q, claims=claims, confidence=0.8 if claims else 0.1)
return {**state, "findings": state["findings"] + [finding], "step": state["step"] + 1}
def route(state: FinderState) -> Literal["search", "review"]:
return "search" if state["step"] < int(__import__("os").getenv("MAX_SEARCH_STEPS", "12")) else "review"
async def review_node(state: FinderState) -> FinderState:
# Post findings + citation trails to human review channel; collect decisions
state["final"] = all(f.confidence >= 0.5 and len(f.claims) > 0 for f in state["findings"])
return {**state}
def build_graph():
g = StateGraph(FinderState)
g.add_node("plan", plan_node)
g.add_node("search", search_node)
g.add_node("review", review_node)
g.set_entry_point("plan")
g.add_edge("plan", "search")
g.add_conditional_edges("search", route, {"search": "search", "review": "review"})
g.add_edge("review", END)
return g.compile()
main.py
import asyncio
from graph import build_graph, FinderState
async def main():
graph = build_graph()
result = await graph.ainvoke({"brief": "Did the vendor notify us of the defect before March 2026?", "findings": [], "step": 0, "final": False})
print(f"findings: {len(result['findings'])} final: {result['final']}")
for f in result["findings"][:3]:
print(f"- {f.topic}: {len(f.claims)} claims, {sum(len(c.citations) for c in f.claims)} citations")
if __name__ == "__main__":
asyncio.run(main())
Retry rules
- Search calls retry twice with exponential backoff (1s, 2s) on 5xx or index timeouts.
- Citation extraction is deterministic and never retried — it consumes only returned hits.
- A finding with zero cited claims at review time is flagged needs work and returned to the planner, never finalized.
- The review gate expires after a configurable window; unreviewed findings are not admitted — no deadline pressure finalizes an unverified claim.
The validation discipline
fact-finder is only as good as its citation accuracy, so validation is built into the workflow. Before the workflow handles real matters, run it against known-good samples — documents with established answers — and measure precision, recall, and citation accuracy. The review gate doubles as a measurement surface: every approval or needs-work flag is a label that tunes the planner. Track the metrics over time, and expand autonomy only where the verified citation rate stays within tolerance. That is the same evidence-based discipline the AI workflows library applies to every high-stakes agent, and it is the pattern DISCO's own visibility-first design points toward.
The bottom line
DISCO's Advanced Research made agentic eDiscovery real on August 13, 2026; fact-finder is the workflow that makes it defensible. Plan, search, cite, review, finalize — with every claim carrying its sources and a human validating the trail. That is the pattern for agentic legal work: the agent investigates, the trail proves, the human decides. The workflow patterns are in the AI workflows library; the legal-AI coverage is on latest AI news.
Frequently Asked Questions
What is fact-finder?
A LangGraph workflow for explainable eDiscovery investigation: it plans a line of inquiry, runs multi-step searches across a document corpus, assembles findings where every claim carries a citation trail, and routes the trail through human review before conclusions finalize.
Why build it now?
DISCO's Advanced Research hit GA on August 13, 2026, proving agentic eDiscovery works — but legal defensibility demands citations on every finding, which is what fact-finder enforces structurally.
How does the citation trail work?
Every finding records the documents it read and the passages supporting each claim. The trail is assembled as the agent searches, so no claim exists without its sources.
What does the human review gate do?
The reviewer validates the citation trail — spot-checks claims against cited passages, verifies the reasoning — before any conclusion is finalized or shared.
How does autonomy expand?
Measure the agent's citation accuracy and precision/recall against known-good samples; expand autonomy only where the verified citation rate is within tolerance.
Closing thoughts
Agentic eDiscovery arrived with DISCO's Advanced Research, and its future belongs to the workflows that make findings defensible. fact-finder enforces the citation trail structurally: every claim cites, every trail is reviewed, every conclusion is earned. The patterns are in the AI workflows library; the legal-AI coverage is on latest AI news.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Chainlink for Agents: The Verified Data, Execution & Cross-Chain Layer for Autonomous Onchain AI Agents
Next Story →SKALE Agent Pit: Paper-Trading Sandboxes for Prediction-Market Agents Before They Touch Real Money
Related Intelligence Analysis
The Step-by-Step Guide to Automating Meeting Tasks with Whisper
You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...
Lovable AI UI-to-Code Pipeline: 2026 Tutorial
Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...
Claude Code's New Browser: 5 Workflows That Save Hours Daily
Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...