document_agent / docs /OVERVIEW.md
Jai-rathore29's picture
Deploy: DocAgent backend (deterministic date-anomaly fix)
f65e025
|
Raw
History Blame Contribute Delete
14.6 kB

DocAgent β€” Project Overview, Research, Design & How It Works

Project: Document Processing AI Agent (chat-based, 40/60 UI) Org: Monkhub Innovations One-line: Upload any document β†’ it parses, classifies, extracts structured data, summarizes, answers grounded questions, flags anomalies, and exports clean data.

This single document brings together what we researched, what we chose and why, the comparison, the feature set, and exactly how the agent works end-to-end. For the full deep-dive research see RESEARCH.md; for the layered architecture record see ARCHITECTURE.md; to deploy see DEPLOY.md.


1. Research β€” what we evaluated

We needed a foundation for a custom, chat-based, production document-processing agent with a 40/60 split UI and a free-LLM-first model strategy. We evaluated the five leading systems, one per relevant archetype:

Archetype System Why it represents the category
Full open-source app RAGFlow A complete chat-over-documents product with deep understanding, citations, and an agent layer β€” the closest existing thing to our target.
LLM app platform Dify Reference for clean platform architecture, visual orchestration, observability, polished UI/UX.
Extraction toolkit Docling (IBM / LF AI & Data) Best-in-class open document parsing β€” layout, tables, OCR, VLM. The extraction backbone.
Workflow automation n8n The "glue" archetype: trigger β†’ OCR β†’ LLM extract β†’ validate β†’ route, assembled visually.
Agentic framework LlamaIndex ADW Reference for stateful, multi-step document agents and schema-based structured extraction.

Method: sequential, single-researcher web research; each system and source read in full. Full notes and sources in RESEARCH.md.


2. Comparison table & scoring

Rubric (1–5; 5 = best) β€” scored for our specific goal, not in the abstract:

Code Parameter
A Feature completeness (OCR, layout, tables, KV, entities, classify, summarize, Q&A, export)
B Architecture quality & extensibility
C Ease of customisation to our UI/flows
D Production-readiness (auth, scaling, observability, deploy)
E UI/UX quality for document work
F Cost / open-source friendliness (license, no per-page cost, self-host)
G Community & documentation
System A B C D E F G Total /35
Dify 3 5 5 5 5 4 5 32
RAGFlow 5 4 3 4 4 5 5 30
Docling 5 5 4 4 1 5 5 29
LlamaIndex ADW 4 5 4 4 2 3 5 27
n8n workflows 3 3 5 4 2 3 5 25

How to read it. Dify wins on raw total, but its lead comes from platform polish, not document intelligence (its weakest axis, A=3). RAGFlow and Docling lead on the axis this project lives or dies on β€” document depth (A=5). The honest conclusion is that no single system is simultaneously the deepest extractor, the cleanest platform, and a ready-made 40/60 chat UI. So we composed.


3. What we chose & why

Decision: a composed custom stack β€” not a fork of any one system.

Borrowed from What we took
RAGFlow The end-to-end pipeline blueprint (deep-understanding-at-ingestion β†’ grounded, citable answers) and citation UX.
Docling The actual extraction engine β€” MIT, local, layout + TableFormer tables + OCR, zero per-page cost.
LlamaIndex ADW The agent loop pattern: parse β†’ maintain state β†’ retrieve β†’ reason β†’ surface for validation.
Dify Separation-of-concerns discipline β€” thin routes, services, provider abstraction.
Our own FastAPI backend + Next.js/React 40/60 UI tailored to the brief.

Why composed instead of forking RAGFlow (the top product): its infra (MinIO + Elasticsearch + MySQL + Redis) and opinionated UI are too heavy to host our bespoke 40/60 experience, and the footprint (β‰₯4 cores / β‰₯16 GB / β‰₯50 GB) is unjustified at our stage. Composing gives us RAGFlow's proven flow, Docling's best-in-class and free/local extraction, LlamaIndex's stateful agent patterns, and Dify's architectural cleanliness β€” with none of the licensing, cost, or lock-in downsides.

Chosen stack at a glance

Layer Choice Why
Extraction Docling (layout, TableFormer, EasyOCR) Best open accuracy; MIT; local; $0 per page; offline-capable.
LLM (default) Gemini Flash Generous free tier; ~1M-token context for long docs; native vision for scans.
LLM (fallback) GPT-4o-mini Cheap, reliable tool-calling; switchable per-request behind a provider interface.
Backend FastAPI (async, Pydantic v2) Clean route→service layering; SSE streaming; production-ready.
Frontend Next.js (App Router) + React + TS + Tailwind 40/60 layout, document viewer with bounding-box overlays, streaming chat.
Vector store Pure-Python numpy cosine (.npz/.json on disk) Dependency-light, no native build/service; chunks carry page+bbox for citations. Swappable for pgvector/Chroma.
Metadata store SQLite (JSON blobs) Zero external services; access funnelled through one module β†’ easy Postgres swap.

Local-first & ~zero marginal cost: the whole app runs with only a free Gemini key. Nothing imports a vendor SDK except llm/*, so providers swap by env, per request, or from the UI.


4. Features β€” the 7 core functions

# Function What it does Agent tool
1 Ingest Parse PDF, DOCX, PPTX, XLSX, HTML, images with Docling (layout, tables, OCR) β†’ unified Markdown + page images + provenance-carrying chunks. (pipeline)
2 Extract Adaptive, template-free structured data: fields / records / tables / entities, each with a confidence score and best-effort citation. get_extracted_data
3 Classify Zero-shot document-type detection with confidence + rationale. classify_document
4 Summarize Map-reduce summary (TL;DR + key points) over chunks. summarize_document
5 Q&A Conversational, agentic-RAG answers grounded with citations you can click to highlight on the page. query_document
6 Flag anomalies Missing fields, low-confidence values, generic arithmetic reconciliation, multi-record sanity, LLM consistency review. flag_anomalies
7 Export Serialize extracted data to JSON / CSV / Excel (records become a table). (API)

The headline feature: adaptive, domain-agnostic extraction

Extraction does not rely on per-domain templates. For any document β€” any layout, any label convention, known type or not β€” it produces a stable, consistently-keyed shape by:

  • Layout detection β€” the model decides whether the doc is one entity (single_record, e.g. an invoice), many similar entities (multi_record, e.g. an employee roster or transaction list), or a header + repeated sub-list (mixed, e.g. invoice + line items).
  • Schema induction β€” fields are derived from the actual content (a known type only suggests fields; the model may add/drop to fit reality).
  • Canonical keys β€” inconsistent source labels ("Staff Number" / "Personnel ID" / "ID") are normalised to one snake_case key, identical across every record.
  • Grounding β€” values come only from the document body (never file metadata); dates and amounts are normalised (ISO dates, currency); citations are attached by matching values back to provenance-carrying chunks.

5. How it works β€” architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  FRONTEND  (Next.js + React + TypeScript + Tailwind)          β”‚
β”‚  40% Chat (streaming agent)  β”‚  60% Workspace                 β”‚
β”‚                              β”‚  Tabs: Viewer Β· Fields Β·       β”‚
β”‚                              β”‚  Classify Β· Summary Β· Export   β”‚
β”‚                              β”‚  Viewer w/ bounding-box cites   β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚  REST + SSE (token streaming)
β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  BACKEND  (FastAPI, async)                                    β”‚
β”‚  api/        documents Β· chat(SSE) Β· actions Β· meta(health)   β”‚
β”‚  agent/      orchestrator (tool-calling loop) + prompts       β”‚
β”‚  llm/        provider abstraction: Gemini(default) ⇄ OpenAI   β”‚
β”‚  services/   ingestion(Docling) Β· extraction Β· classification β”‚
β”‚              Β· summary Β· qa(RAG) Β· anomaly Β· export Β·          β”‚
β”‚              vectorstore Β· storage Β· pipeline                  β”‚
β”‚  schemas/    Pydantic contracts (documents, optional hints)   β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚                           β”‚
β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Docling   β”‚            β”‚ Storage (/data)    β”‚
β”‚ (local    β”‚            β”‚ β€’ files on disk    β”‚
β”‚  extract) β”‚            β”‚ β€’ SQLite metadata  β”‚
β”‚ models    β”‚            β”‚ β€’ numpy vectors    β”‚
β”‚ baked in  β”‚            β”‚   (.npz/.json)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚ β€’ rendered pages   β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Discipline (borrowed from Dify): routes are thin β†’ call services; the agent orchestrates services as tools; LLM access only through llm/; storage only through services/storage + services/vectorstore; schemas are shared.


6. Working β€” the two flows

Flow A β€” Ingestion pipeline (runs once per upload, in the background)

Implemented in services/pipeline.py. State is written back at every milestone so the UI can poll status and render results progressively.

upload β†’ [Stage 1: INGEST]──fail?──► status=failed (hard fail; only this stage can)
            β”‚ Docling parse β†’ Markdown + page images + chunks(page,bbox)
            β–Ό
        [index]      embed chunks β†’ numpy vector store        ┐
        [classify]   LLM zero-shot β†’ type + confidence        β”‚ Stages 2–6 are
        [extract]    adaptive induction β†’ fields/records/...   β”‚ best-effort: a
        [summarize]  map-reduce β†’ TL;DR + key points           β”‚ failure (e.g. no
        [anomalies]  rule + LLM checks (needs extraction)      β”‚ LLM key) degrades
            β–Ό                                                  β”˜ gracefully
        status=ready  (viewer always works, even if AI steps were skipped)

Key robustness property: only ingestion can hard-fail a document. Everything else is wrapped so a missing API key or a flaky step still yields a ready document with a working viewer and a note about what was skipped.

Flow B β€” Chat agent (per user message)

Implemented in agent/orchestrator.py β€” a bounded tool-calling loop, then a streamed final answer.

user message + history + doc context
        β”‚
        β–Ό
  Phase 1: TOOL ROUNDS  (up to MAX_TOOL_ROUNDS = 4, non-streamed reasoning)
     llm.complete_tools(messages, TOOL_SPECS)
        β”‚
        β”œβ”€β”€ model returns tool_calls?  ──yes──► run each tool ─► append results ─► loop
        β”‚        tools: query_document Β· classify_document Β·
        β”‚               get_extracted_data Β· summarize_document Β· flag_anomalies
        β”‚
        └── no tool_calls (ready to answer) ─► exit loop
        β–Ό
  Phase 2: FINAL ANSWER  (streamed token-by-token over SSE, temperature 0.4)
        β”‚
        β–Ό
  client renders answer + citation chips β†’ click a chip β†’ highlight the
  page region (numpy store returned page+bbox with each retrieved chunk)

query_document runs agentic RAG: retrieve the most similar chunks, answer from them, and return grounding passages whose page+bbox the UI turns into clickable highlights. The orchestrator collects citations in its ToolContext so the API can emit them alongside the streamed answer.


7. End-to-end walkthrough (a user's view)

  1. Drag a document into the left rail (or click New document).
  2. The pipeline runs: pages render in the viewer first, then classification, fields, summary, and anomaly flags stream into the workspace tabs as each stage finishes.
  3. Chat on the left β€” e.g. "Summarize this", "What's the total?", "Any missing fields?" The agent calls the right tools, then streams a grounded answer. Click any citation chip to highlight its exact source on the page.
  4. Review & edit low-confidence fields inline (human-in-the-loop) before exporting.
  5. Export from the Export tab as JSON / CSV / Excel.

8. Why this design holds up

  • Document depth where it matters β€” Docling gives best-in-class layout/table/OCR locally and for free, the axis on which generic platforms (Dify, n8n) are weakest.
  • Truly document-agnostic β€” extraction induces its own schema, so a new document type needs no code or template changes.
  • Grounded & trustworthy β€” every chunk carries page + bbox, so answers and fields point at their exact source; the single most trust-building UX feature.
  • Cheap and portable β€” runs on a free Gemini key with no external services; SQLite and the numpy store are single-file and swappable for Postgres/pgvector at scale via one module each.
  • Graceful degradation β€” the app stays useful even when AI steps fail.
  • Clean seams for growth β€” provider abstraction, storage/vectorstore interfaces, and a single-orchestrator agent that can grow more tools without a rewrite.