मुख्य सामग्री पर जाएं

Docling: एक डॉक्यूमेंट इंटेलिजेंस ऐप बनाने की चरण-दर-चरण गाइड

Docling का उपयोग करके उलझे PDFs को संरचित, खोजयोग्य डाटा में बदलें। रिट्रीवल-ऑगमेंटेड रीजनिंग के लिए LangGraph और इंटरैक्टिव फ्रंट एंड के लिए Streamlit इंटीग्रेट करें। ऐसा लोकल Document Intelligence ऐप बनाएँ, जाँचे और परिनियोजित करें जो आपके दस्तावेज़ों के लिए आपके निजी ChatGPT जैसा लगे।
अपडेट किया गया 24 सित॰ 2026  · 15 मि॰ पढ़ें

AI के साथ खोजें

ChatGPTClaudePerplexity

pypdf या PDFMiner जैसे पारंपरिक PDF एक्सट्रैक्शन टूल आपको सादा टेक्स्ट देते हैं, लेकिन दस्तावेज़ की संरचना खो देते हैं। तालिकाएँ उलझे हुए टेक्स्ट में बदल जाती हैं, हेडर मुख्य सामग्री के साथ घुल-मिल जाते हैं, और छवियाँ गायब हो जाती हैं। RAG सिस्टम में, यह बिखरा हुआ डेटा कमजोर रिट्रीवल और अविश्वसनीय उत्तरों का कारण बनता है। Docling IBM Research का एक ओपन-सोर्स टूलकिट है जो कंप्यूटर विज़न मॉडल्स से दस्तावेज़ लेआउट को समझता है और तालिकाएँ, छवियाँ, हेडिंग्स और संरचना सुरक्षित रखता है। यह पारंपरिक OCR-आधारित विधियों की तुलना में दस्तावेज़ों को 30 गुना तक तेज़ी से प्रोसेस करता है और आपकी मशीन पर स्थानीय रूप से चलता है।

इस ट्यूटोरियल में, हम Docling का उपयोग करके एक Document Intelligence Assistant बनाएंगे — एक Streamlit वेब ऐप जो आपको दस्तावेज़ अपलोड करने, उनकी संरचना विज़ुअलाइज़ करने और RAG-संचालित चैटबॉट से प्रश्न पूछने देता है। आप सीखेंगे कि Docling से मल्टी-फ़ॉर्मैट दस्तावेज़ कैसे प्रोसेस करें, तालिकाएँ और छवियाँ निकालकर दिखाएँ, ChromaDB के साथ वेक्टर स्टोर बनाएँ, और LangGraph से एक वार्तालाप एजेंट तैयार करें। अंत तक, आपके पास एक काम करने वाला एप्लिकेशन होगा जो जटिल दस्तावेज़ों को संरचित डेटा में बदलता है और बुद्धिमान प्रश्न-उत्तर को सक्षम बनाता है।

एप्लिकेशन प्रीव्यू:

Docling Streamlit ऐप में एक चैट इंटरफ़ेस, जिसमें उपयोगकर्ता दस्तावेज़ से संबंधित प्रश्न पूछ रहा है और संरचित उत्तर प्राप्त कर रहा है

दस्तावेज़ संरचना विज़ुअलाइज़ेशन:

Docling द्वारा दस्तावेज़ संरचना का विज़ुअलाइज़ेशन दिखाने वाला स्क्रीनशॉट, जिसमें सैंपल PDF में पहचानी गई तालिकाएँ, छवियाँ और हेडिंग्स प्रदर्शित हैं

पूर्वापेक्षाएँ

इस ट्यूटोरियल को शुरू करने से पहले, आपके पास ये होना चाहिए:

तकनीकी कौशल: Python की कक्षाएँ (classes), डेकोरेटर्स, टाइप हिंट्स, और कॉन्टेक्स्ट मैनेजर्स के साथ सहजता। हम पूरे समय async ऑपरेशन और फ़ैक्टरी पैटर्न का उपयोग करेंगे। बड़े भाषा मॉडल कैसे प्रॉम्प्ट्स, टोकन और एम्बेडिंग्स के साथ काम करते हैं, इसकी समझ आवश्यक है। रिट्रीवल-ऑगमेंटेड जेनरेशन सिस्टम और वेक्टर डेटाबेस से परिचित होना सहायक है, पर अनिवार्य नहीं — हम निर्माण करते समय मुख्य अवधारणाएँ समझाएँगे।

डेवलपमेंट सेटअप: पैकेज प्रबंधन के लिए pip सहित Python 3.10 या उससे उच्च। VS Code जैसा कोड संपादक अनुशंसित है। आपको OpenAI API कुंजी की आवश्यकता होगी platform.openai.com से — प्रोसेसिंग लागत लगभग $0.10–0.20 प्रति दस्तावेज़।

समय प्रतिबद्धता: ट्यूटोरियल पूरा करने में 60–90 मिनट रखें, जिसमें व्याख्याएँ पढ़ना, कोड लिखना और एप्लिकेशन का परीक्षण शामिल है। यह ट्यूटोरियल मध्यम-स्तर के Python कौशल मानकर चलता है।

Docling को समझना: विशेषताएँ और क्षमताएँ

अधिकांश दस्तावेज़ प्रोसेसिंग टूल PDFs को इमेज फ़ाइल या टेक्स्ट स्ट्रीम की तरह मानते हैं। वे या तो हर पेज पर OCR चलाते हैं या बिना समझे सादा टेक्स्ट निकालते हैं। Docling एक अलग दृष्टिकोण लेता है। यह IBM Research का ओपन-सोर्स टूलकिट है जो कंप्यूटर विज़न मॉडल्स से दस्तावेज़ संरचना को उसी तरह समझता है जैसे कोई इंसान समझे।

जब आप कोई दस्तावेज़ Docling में देते हैं, दो AI मॉडल उसका विश्लेषण करते हैं:

  • लेआउट विश्लेषण: DocLayNet पर प्रशिक्षित मॉडल पेज लेआउट का विश्लेषण करके हेडर, मुख्य टेक्स्ट, तालिकाएँ और छवियों जैसे विभिन्न तत्वों की पहचान करते हैं
  • तालिका संरचना: TableFormer तालिकाओं को संभालता है और उन्हें संरचित डेटा में बदलता है

ये मॉडल समझते हैं कि दस्तावेज़ सिर्फ टेक्स्ट का ढेर नहीं है। इसमें अनुक्रम (हायरार्की), संबंध और अर्थ होते हैं।

RAG सिस्टम के लिए यह संरचनात्मक समझ महत्वपूर्ण है। जब आप रिट्रीवल-ऑगमेंटेड जेनरेशन एप्लिकेशन बना रहे होते हैं, तो आपके दस्तावेज़ प्रोसेसिंग की गुणवत्ता सीधे रिट्रीवल की शुद्धता को प्रभावित करती है। अगर आपका PDF एक्सट्रैक्शन किसी वित्तीय तालिका को उलझे टेक्स्ट में बदल दे, तो आपका वेक्टर सर्च कचरा लौटाएगा। Docling संरचना को सुरक्षित रखता है, ताकि जब आप अपने दस्तावेज़ों को टुकड़ों में बाँटकर एम्बेड करें, तो आप साफ़-सुथरे, व्यवस्थित डेटा के साथ काम करें।

Docling बॉक्स से बाहर कई दस्तावेज़ फ़ॉर्मैट का समर्थन करता है:

  • PDF दस्तावेज़
  • Word दस्तावेज़ (DOCX)
  • PowerPoint प्रस्तुतियाँ (PPTX)
  • Excel स्प्रेडशीट (XLSX)
  • HTML फ़ाइलें
  • छवियाँ

आप स्कैन किए गए दस्तावेज़ों के लिए EasyOCR, Tesseract, या RapidOCR जैसे इंजनों का उपयोग करके OCR भी सक्षम कर सकते हैं। टूलकिट कई फ़ॉर्मैट में एक्सपोर्ट करता है, जिनमें Markdown (LLMs के लिए बढ़िया), JSON (संरचित डेटा पाइपलाइनों के लिए), और DocTags (गणितीय समीकरणों और कोड ब्लॉक्स जैसे जटिल तत्वों को कैप्चर करने वाला फ़ॉर्मैट) शामिल हैं।

फ़ॉर्मैट लचीलापन से आगे, Docling प्रदर्शन लाभ देता है। पारंपरिक OCR-आधारित प्रोसेसिंग धीमी होती है क्योंकि वह हर पेज को ऐसे मानती है जैसे उन पर अक्षरों की पहचान करनी पड़े। Docling डिजिटल दस्तावेज़ों के लिए यह चरण छोड़ देता है, और बहुत तेज़ प्रोसेसिंग देता है। यह सामान्य हार्डवेयर पर स्थानीय रूप से चलता है, इसलिए आप API लागत नहीं चुकाते और संवेदनशील दस्तावेज़ थर्ड-पार्टी सेवाओं को नहीं भेजते। प्रोसेसिंग गति दस्तावेज़ की जटिलता, पेज संख्या और हार्डवेयर पर निर्भर करती है; आधुनिक हार्डवेयर पर सामान्यतः प्रति पेज एक सेकंड से कुछ सेकंड तक का समय लगता है।

IBM Docling की क्षमताओं को लगातार बेहतर बना रहा है। उन्होंने Granite-Docling जारी किया है, जो 258-मिलियन पैरामीटर वाला विज़न-लैंग्वेज मॉडल है, जटिल लेआउट में उत्कृष्टता दिखाता है और प्रयोगात्मक बहुभाषी समर्थन देता है (अंग्रेज़ी प्राथमिक भाषा है, और अरबी, चीनी, जापानी के लिए आरंभिक-चरण समर्थन)। टूलकिट अब कॉन्फ़िगरेबल रेज़ोल्यूशन के साथ इमेज एक्सट्रैक्शन का भी समर्थन करता है, जिसे हम अपने एप्लिकेशन में PDFs की टेक्स्ट सामग्री के साथ वास्तविक छवियाँ दिखाने के लिए उपयोग करेंगे।

हमारे उपयोग-केस में, हमें सादा टेक्स्ट नहीं बल्कि संरचित डेटा चाहिए, इसलिए Docling समझ में आता है। यदि आपको केवल बुनियादी टेक्स्ट एक्सट्रैक्शन चाहिए, तो pypdf जैसे साधारण टूल पर्याप्त हो सकते हैं। लेकिन चूँकि हम विश्लेषण और वार्तालाप के लिए दस्तावेज़ प्रोसेस करने वाला AI एप्लिकेशन बना रहे हैं, Docling का संरचना-सचेत प्रोसेसिंग बेहतर विकल्प है। यह विशेष रूप से तब मूल्यवान होता है जब आप तकनीकी दस्तावेज़ों, शोध-पत्रों या व्यावसायिक रिपोर्टों के साथ काम करते हैं, जहाँ तालिकाएँ और लेआउट मायने रखते हैं।

अपना डेवलपमेंट परिवेश सेटअप करना

दस्तावेज़ प्रोसेसर बनाने से पहले, आपको अपनी परियोजना संरचना सेट करनी है और आवश्यक पैकेज इंस्टॉल करने हैं। इस खंड में शुरुआती सेटअप शामिल है: निर्देशिकाएँ बनाना, निर्भरताएँ इंस्टॉल करना और अपनी API कुंजियाँ कॉन्फ़िगर करना।

प्रोजेक्ट संरचना बनाना

अपनी परियोजना के लिए एक नई निर्देशिका बनाकर शुरू करें:

mkdir docling-demo
cd docling-demo

इस निर्देशिका के भीतर, अपने Python मॉड्यूल्स के लिए src/ फ़ोल्डर बनाएँ:

mkdir src
touch src/__init__.py

__init__.py फ़ाइल Python को बताती है कि src/ एक पैकेज है, जिससे from src.document_processor import DocumentProcessor जैसे इम्पोर्ट संभव हो जाते हैं।

आपकी परियोजना संरचना:

docling-demo/
├── src/
│   └── __init__.py

निर्भरताएँ इंस्टॉल करना

अपनी परियोजना रूट में requirements.txt फ़ाइल बनाएँ:

docling>=2.55.0
langchain-docling>=0.1.0
langchain>=0.3.0
langchain-openai>=0.2.0
langgraph>=0.2.0
langchain-chroma>=0.1.0
streamlit>=1.28.0
streamlit-extras>=0.7.0
python-dotenv>=1.0.0
chromadb>=0.4.22
tiktoken>=0.5.0
pandas>=2.0.0
numpy<2

numpy<2 बाधा इसलिए है क्योंकि Docling की निर्भरताएँ (TensorFlow और Transformers) NumPy 1.x की आवश्यकता रखती हैं।

पैकेज इंस्टॉल करें:

pip install -r requirements.txt

पहली इंस्टॉलेशन में कुछ मिनट लगते हैं क्योंकि Docling लगभग 500MB के प्री-ट्रेंड AI मॉडल्स डाउनलोड करता है। ये मॉडल लेआउट विश्लेषण और टेबल स्ट्रक्चर रिकग्निशन को संभालते हैं। ये स्थानीय रूप से कैश हो जाते हैं, इसलिए अगली बार रन तेज़ होते हैं।

पर्यावरण वेरिएबल्स कॉन्फ़िगर करना

अपनी OpenAI API कुंजी स्टोर करने के लिए .env फ़ाइल बनाएँ:

OPENAI_API_KEY=your-openai-api-key-here

platform.openai.com से अपनी API कुंजी लें। यह एम्बेडिंग्स और चैट एजेंट के लिए आवश्यक होगी।

.env.example टेम्पलेट बनाएँ:

OPENAI_API_KEY=your-openai-api-key-here

संवेदनशील डेटा कमिट होने से बचाने के लिए .gitignore जोड़ें:

# Environment variables
.env

# Python
__pycache__/
*.py[cod]
*.so
venv/
*.egg-info/

# Chroma
chroma_db/

मुख्य एप्लिकेशन फ़ाइल बनाना

परियोजना रूट में app.py बनाएँ। हम इस फ़ाइल को ट्यूटोरियल के दौरान धीरे-धीरे बनाते जाएँगे। फिलहाल, मूल इम्पोर्ट्स और कॉन्फ़िगरेशन जोड़ें:

import streamlit as st
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Page configuration
st.set_page_config(
    page_title="Document Intelligence Assistant",
    page_icon="📄",
    layout="wide"
)

st.title("Document Intelligence Assistant")
st.write("Application setup complete. We'll build the functionality next.")

अपना सेटअप जाँचें:

streamlit run app.py

Streamlit http://localhost:8501 पर ब्राउज़र विंडो खोलेगा, जहाँ आपका बेसिक पेज दिखेगा।

आपकी अंतिम परियोजना संरचना (अगले खंडों में नए स्क्रिप्ट जोड़ने से पहले):

docling-demo/
├── .env
├── .env.example
├── .gitignore
├── requirements.txt
├── app.py
└── src/
   └── __init__.py

पर्यावरण तैयार होने के साथ, हम दस्तावेज़ प्रोसेसर बना सकते हैं जो अपलोड की गई फ़ाइलों से Docling का उपयोग करके संरचना निकालता है।

> नोट: नीचे दिए गए खंड एप्लिकेशन स्क्रिप्ट्स को हिस्सों में तोड़ते हैं। इसलिए पूरी तस्वीर देखने और आसानी से साथ चलने के लिए, हम सुझाव देते हैं कि आप इस परियोजना के लिए GitHub रिपॉज़िटरी को अलग टैब में खोलें। 

कोर डॉक्यूमेंट प्रोसेसर बनाना

📄 पूर्ण स्क्रिप्ट: src/document_processor.py

डॉक्यूमेंट प्रोसेसर वह जगह है जहाँ Docling अपना काम करता है। यह घटक अपलोड की गई फ़ाइलों को लेता है और उन्हें संरचित डेटा में बदलता है जिसका उपयोग हम RAG और विज़ुअलाइज़ेशन दोनों के लिए कर सकते हैं। हमें इस प्रक्रिया से दो आउटपुट चाहिए: वेक्टर स्टोर के लिए साफ़-सुथरा मार्कडाउन टेक्स्ट और मूल Docling दस्तावेज़ ऑब्जेक्ट जो तालिकाओं और छवियों जैसी सभी संरचनात्मक जानकारी सुरक्षित रखे।

इसे बनाने के लिए अपने src/ डायरेक्टरी में document_processor.py नामक नई फ़ाइल बनाएँ। हम DocumentProcessor क्लास बनाएँगे जो Docling की प्रोसेसिंग पाइपलाइन कॉन्फ़िगर करता है और फ़ाइल अपलोड सँभालता है।

पाइपलाइन विकल्प कॉन्फ़िगर करना

Docling आपको पाइपलाइन विकल्पों के ज़रिए नियंत्रित करने देता है कि वह दस्तावेज़ों को कैसे प्रोसेस करे। PDFs के लिए, आप स्कैन किए गए दस्तावेज़ों हेतु OCR चालू कर सकते हैं, तालिका संरचना पहचान सक्रिय कर सकते हैं, और छवियाँ निकाल सकते हैं:

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from langchain_core.documents import Document

class DocumentProcessor:
    def __init__(self):
        # Configure pipeline options for PDF processing
        pipeline_options = PdfPipelineOptions()
        pipeline_options.do_ocr = True
        pipeline_options.do_table_structure = True
        pipeline_options.generate_picture_images = True
        pipeline_options.images_scale = 2.0

हम PdfPipelineOptions ऑब्जेक्ट बनाते हैं ताकि यह कॉन्फ़िगर करें कि Docling PDF फ़ाइलों को कैसे प्रोसेस करे। हर विकल्प एक विशिष्ट क्षमता नियंत्रित करता है: do_ocr स्कैन दस्तावेज़ों के लिए ऑप्टिकल कैरेक्टर रिकग्निशन सक्षम करता है, do_table_structure तालिका डिटेक्शन और पार्सिंग सक्रिय करता है, generate_picture_images Docling को एम्बेडेड छवियाँ PIL ऑब्जेक्ट्स के रूप में निकालने को कहता है, और images_scale निकाली गई छवियों के रेज़ोल्यूशन गुणक को सेट करता है।

# Initialize converter with PDF options
self.converter = DocumentConverter(
    format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)}
)

DocumentConverter Docling का मुख्य प्रोसेसिंग इंजन है। हम इसे अपने PDF पाइपलाइन विकल्पों के साथ PdfFormatOption में रैप करके इनिशियलाइज़ करते हैं, जो इन सेटिंग्स को खास तौर पर PDF इनपुट फ़ाइलों से जोड़ता है।

आइए देखें हर पाइपलाइन विकल्प क्या करता है। do_ocr फ़्लैग ऑप्टिकल कैरेक्टर रिकग्निशन सक्रिय करता है। एम्बेडेड टेक्स्ट लेयर वाले डिजिटल PDFs प्रोसेस करते समय, Docling समय बचाने के लिए स्वचालित रूप से OCR छोड़ देता है। स्कैन दस्तावेज़ों या टेक्स्ट वाली छवियों के लिए, यह सेटिंग Docling को टेक्स्ट निकालने के लिए विज़न मॉडल चलाने को कहती है।

do_table_structure विकल्प तालिका संरचना पहचान सक्षम करता है। इसके बिना, तालिकाएँ सादे टेक्स्ट की तरह निकाली जाती हैं और फ़ॉर्मैटिंग खो जाती है। इसे सक्षम करने पर, Docling अपना TableFormer AI मॉडल उपयोग करके पंक्तियाँ, स्तंभ, हेडर और सेल संबंधों की पहचान करता है। यह संरचित प्रतिनिधित्व आपको बाद में pandas DataFrames के रूप में तालिकाओं को एक्सपोर्ट करने देता है, तालिकात्मक फ़ॉर्मैट सुरक्षित रखते हुए।

generate_picture_images को True पर सेट करने से इमेज एक्सट्रैक्शन सक्षम होता है। डिफ़ॉल्ट रूप से, Docling वास्तविक छवियाँ निकाले बिना केवल उनकी लोकेशन रिकॉर्ड करता है। इसे सक्षम करने से आपको PIL इमेज ऑब्जेक्ट्स मिलते हैं जिन्हें आप अपने UI में दिखा सकते हैं या विज़न मॉडल्स से प्रोसेस कर सकते हैं। images_scale पैरामीटर एक्सट्रैक्शन रेज़ोल्यूशन नियंत्रित करता है—2.0 का मान रेज़ोल्यूशन दुगुना कर देता है, जिससे छवियाँ दिखाने या अतिरिक्त विश्लेषण चलाने पर गुणवत्ता बेहतर रहती है।

अपलोड की गई फ़ाइलें प्रोसेस करना

पाइपलाइन कॉन्फ़िगर होने के साथ, हम वह मेथड जोड़ सकते हैं जो Streamlit के ज़रिए अपलोड की गई फ़ाइलों को प्रोसेस करता है। यह मेथड Streamlit के फ़ाइल ऑब्जेक्ट्स सँभालता है, उन्हें अस्थायी रूप से सेव करता है, Docling का कन्वर्ज़न चलाता है, और RAG के लिए मार्कडाउन तथा विज़ुअलाइज़ेशन के लिए Docling दस्तावेज़ दोनों लौटाता है:

import os
import tempfile
from typing import List, Any

def process_uploaded_files(self, uploaded_files) -> tuple[List[Document], List[Any]]:
    documents = []
    docling_docs = []
    temp_dir = tempfile.mkdtemp()

    try:
        for uploaded_file in uploaded_files:
            # Save uploaded file to temporary location
            temp_file_path = os.path.join(temp_dir, uploaded_file.name)
            with open(temp_file_path, "wb") as f:
                f.write(uploaded_file.getbuffer())

Streamlit अपलोड की गई फ़ाइलों को इन-मे मोरी फ़ाइल ऑब्जेक्ट्स के रूप में देता है, लेकिन Docling को प्रोसेस करने के लिए डिस्क पर वास्तविक फ़ाइलें चाहिए। हम अस्थायी निर्देशिका बनाते हैं और हर अपलोड की गई फ़ाइल को उसमें मूल फ़ाइलनाम सुरक्षित रखते हुए लिखते हैं।

# Process the document with Docling
result = self.converter.convert(temp_file_path)
          
# Export to markdown
markdown_content = result.document.export_to_markdown()

converter.convert() कॉल Docling की पूरी दस्तावेज़ विश्लेषण पाइपलाइन चलाती है। यह दस्तावेज़ लेआउट की पहचान करता है, ज़रूरत होने पर OCR लागू करता है, तालिकाएँ और छवियाँ पहचानता है, और एक संरचित प्रतिनिधित्व बनाता है। छवियाँ न निकालने पर लगभग 20–30 सेकंड और छवियाँ सक्षम होने पर 40–60 सेकंड का समय अपेक्षित है।

कन्वर्ज़न पूरा होने के बाद, हम मार्कडाउन फ़ॉर्मैट में एक्सपोर्ट करते हैं, जो साफ़, LLM-अनुकूल टेक्स्ट देता है और उचित फ़ॉर्मैटिंग सुरक्षित रखता है — हेडर हेडर ही रहते हैं, सूचियाँ संरचित रहती हैं, और तालिकाएँ मार्कडाउन टेबल्स में बदल जाती हैं।

# Create LangChain document
doc = Document(
    page_content=markdown_content,
    metadata={
        "filename": uploaded_file.name,
        "file_type": uploaded_file.type,
        "source": uploaded_file.name,
    }
)

documents.append(doc)

# Store the Docling document for structure visualization
docling_docs.append({
    "filename": uploaded_file.name,
    "doc": result.document
})

हम हर प्रोसेस किए गए दस्तावेज़ की दो अभिव्यक्तियाँ बनाते हैं। LangChain का Document ऑब्जेक्ट page_content में मार्कडाउन टेक्स्ट और संबंधित मेटाडेटा रखता है—यह RAG के लिए वेक्टर स्टोर में जाता है। मूल Docling दस्तावेज़ ऑब्जेक्ट फ़ाइलनाम के साथ अलग से स्टोर होता है, जिससे बाद में विज़ुअलाइज़ेशन के लिए सभी संरचनात्मक जानकारी (तालिकाएँ, छवियाँ, हायरार्की) सुरक्षित रहती है।

finally:
    import shutil
    shutil.rmtree(temp_dir)

return documents, docling_docs

finally ब्लॉक यह सुनिश्चित करता है कि प्रोसेसिंग सफल हो या विफल, अस्थायी फ़ाइलें साफ़ हो जाएँ। हम दोनों दस्तावेज़ अभिव्यक्तियों वाला ट्यूपल लौटाते हैं: RAG सिस्टम के लिए LangChain दस्तावेज़ और संरचना विज़ुअलाइज़ेशन के लिए Docling दस्तावेज़।

डॉक्यूमेंट प्रोसेसर अब तैयार है। अगला, हम Streamlit इंटरफ़ेस बनाएँगे जो उपयोगकर्ताओं को फ़ाइलें अपलोड करने और प्रोसेसिंग स्थिति देखने देता है।

दस्तावेज़ संरचना विज़ुअलाइज़ेशन लागू करना

📄 पूर्ण स्क्रिप्ट: src/structure_visualizer.py

अब जब हम Docling से दस्तावेज़ प्रोसेस कर सकते हैं, हमें निकाले गए कंटेंट को विज़ुअलाइज़ करने का तरीका चाहिए। मूल Docling दस्तावेज़ ऑब्जेक्ट में समृद्ध संरचनात्मक जानकारी (हेडिंग्स, तालिकाएँ, छवियाँ और मेटाडेटा) होती है, पर अपनी कच्ची अवस्था में वह उपयोगकर्ता-अनुकूल नहीं है। हम एक विज़ुअलाइज़ेशन लेयर बनाएँगे जो इन डाटा को चार व्यू में एक इंटरैक्टिव इंटरफ़ेस में बदल दे: एक समरी डैशबोर्ड, एक हायरार्किकल आउटलाइन, इंटरैक्टिव टेबल्स, और निकाली गई छवियाँ।

अपने src/ डायरेक्टरी में structure_visualizer.py नामक नई फ़ाइल बनाएँ। यह घटक Docling के दस्तावेज़ स्ट्रक्चर को पार्स करेगा और डिस्प्ले के लिए व्यवस्थित करेगा।

विज़ुअलाइज़र क्लास बनाना

Docling दस्तावेज़ को रैप करने और विभिन्न संरचनात्मक तत्व निकालने के मेथड्स देने वाली एक क्लास बनाकर शुरू करें:

from typing import List, Dict, Any
import pandas as pd
from docling_core.types.doc import DoclingDocument

class DocumentStructureVisualizer:
    def __init__(self, docling_document: DoclingDocument):
        self.doc = docling_document

इनिशियलाइज़र एक DoclingDocument ऑब्जेक्ट लेता है (वही ऑब्जेक्ट जो DocumentConverter.convert() से लौटता है)। इस ऑब्जेक्ट में Docling द्वारा निकाली गई सभी चीजें होती हैं। texts एट्रिब्यूट में सभी टेक्स्ट एलिमेंट्स उनके लेबल और पोजिशन के साथ होते हैं, tables में संरचना जानकारी सहित टेबल डाटा होता है, pictures इमेज मेटाडेटा और वास्तविक इमेज डाटा स्टोर करता है, और pages पेज-स्तरीय जानकारी देता है।

दस्तावेज़ हायरार्की निकालना

दस्तावेज़ों में सिर्फ अनुच्छेदों से परे संरचना होती है। हेडिंग्स हायरार्की बनाती हैं जो पाठकों को सामग्री में नेविगेट करने में मदद करती हैं। इसे निकालने का तरीका यहाँ है:

def get_document_hierarchy(self) -> List[Dict[str, Any]]:
    hierarchy = []

    if not hasattr(self.doc, "texts") or not self.doc.texts:
        return hierarchy

    for item in self.doc.texts:
        label = getattr(item, "label", None)

        if label and "header" in label.lower():
            text = getattr(item, "text", "")
            prov = getattr(item, "prov", [])
            page_no = prov[0].page_no if prov else None

            hierarchy.append({
                "type": label,
                "text": text,
                "page": page_no,
                "level": self._infer_heading_level(label)
            })

    return hierarchy

हम दस्तावेज़ के सभी टेक्स्ट आइटम्स पर इटरेट करते हैं, और जिनके लेबल में header शामिल है उन्हें फ़िल्टर करते हैं। हर टेक्स्ट आइटम में एक label एट्रिब्यूट होता है जिसे Docling लेआउट विश्लेषण के दौरान सौंपता है। सामान्य लेबल्स में section_header, page_header, title, और सामान्य text शामिल हैं। prov (provenance) में पोजिशनिंग जानकारी होती है, जिसमें वह पेज नंबर भी शामिल है जहाँ एलिमेंट आता है। हम हेडिंग टेक्स्ट, उसका पेज नंबर निकालते हैं और लेबल से उसका हायरार्किकल स्तर निकालते हैं।

आउटलाइन दिखाते समय, हेडिंग स्तर इंडेंटेशन तय करता है। एक हेल्पर मेथड लेबल प्रकारों को संख्यात्मक स्तरों पर मैप करता है:

def _infer_heading_level(self, label: str) -> int:
    if "title" in label.lower():
        return 1
    elif "section" in label.lower():
        return 2
    elif "subsection" in label.lower():
        return 3
    else:
        return 4

यह ऐसी हायरार्की बनाता है जहाँ दस्तावेज़ शीर्षक स्तर 1, सेक्शन हेडर स्तर 2, उप-सेक्शन स्तर 3, और अन्य हेडर डिफ़ॉल्ट रूप से स्तर 4 होते हैं।

तालिकाओं को DataFrames में बदलना

दस्तावेज़ हायरार्की निकालने के बाद, अब तालिकाओं को सँभालें। साधारण टेक्स्ट एक्सट्रैक्शन तालिकाओं को उलझे स्ट्रिंग्स में बदल देता है, जबकि Docling उनकी संरचना सुरक्षित रखता है—यह इसकी सबसे मूल्यवान विशेषताओं में से एक है:

def get_tables_info(self) -> List[Dict[str, Any]]:
    tables_info = []

    if not hasattr(self.doc, "tables") or not self.doc.tables:
        return tables_info

    for i, table in enumerate(self.doc.tables, 1):
        try:
            df = table.export_to_dataframe(doc=self.doc)

            prov = getattr(table, "prov", [])
            page_no = prov[0].page_no if prov else None

            caption_text = getattr(table, "caption_text", None)
            caption = caption_text if caption_text and not callable(caption_text) else None

            tables_info.append({
                "table_number": i,
                "page": page_no,
                "caption": caption,
                "dataframe": df,
                "shape": df.shape,
                "is_empty": df.empty
            })
        except Exception as e:
            print(f"Warning: Could not process table {i}: {e}")
            continue

    return tables_info

table.export_to_dataframe(doc=self.doc) मेथड Docling की टेबल अभिव्यक्ति को pandas DataFrame में बदल देता है। हम उपलब्ध होने पर कैप्शन और पेज नंबर निकालते हैं।

PIL के साथ छवियाँ निकालना

तालिकाओं से आगे, Docling दस्तावेज़ों से वास्तविक इमेज डाटा भी निकाल सकता है। यह एक नई सुविधा है जो केवल इमेज पोजिशन ट्रैक करने से आगे बढ़कर वास्तविक इमेज बाइट्स प्राप्त करती है ताकि आप उन्हें दिखा सकें। (हमने इसे दस्तावेज़ प्रोसेसर की पाइपलाइन विकल्पों में सक्षम किया था।)

def get_pictures_info(self) -> List[Dict[str, Any]]:
    pictures_info = []

    if not hasattr(self.doc, "pictures") or not self.doc.pictures:
        return pictures_info

    for i, pic in enumerate(self.doc.pictures, 1):
        prov = getattr(pic, "prov", [])

        if prov:
            page_no = prov[0].page_no
            bbox = prov[0].bbox

            caption_text = getattr(pic, "caption_text", None)
            caption = caption_text if caption_text and not callable(caption_text) else None

            pil_image = None
            try:
                if hasattr(pic, "image") and pic.image is not None:
                    if hasattr(pic.image, "pil_image"):
                        pil_image = pic.image.pil_image
            except Exception as e:
                print(f"Warning: Could not extract image {i}: {e}")

            pictures_info.append({
                "picture_number": i,
                "page": page_no,
                "caption": caption,
                "pil_image": pil_image,
                "bounding_box": {
                    "left": bbox.l,
                    "top": bbox.t,
                    "right": bbox.r,
                    "bottom": bbox.b
                } if bbox else None
            })

    return pictures_info

हर चित्र में प्रोवेनेंस जानकारी होती है, जिसमें पेज नंबर और बाउंडिंग बॉक्स कोऑर्डिनेट्स शामिल हैं। बाउंडिंग बॉक्स पेज पर छवि की स्थिति को बाएँ, ऊपर, दाएँ, और नीचे निर्देशांकों से परिभाषित करता है।

जब इमेज एक्सट्रैक्शन सक्षम होता है, तो पिक्चर ऑब्जेक्ट में वास्तविक इमेज डाटा युक्त image एट्रिब्यूट होता है। हम PIL इमेज को picture.image.pil_image के माध्यम से एक्सेस करते हैं, जो PIL Image ऑब्जेक्ट लौटाता है जिसे Streamlit सीधे st.image() से दिखा सकता है। try-except ब्लॉक उन स्थितियों को संभालता है जहाँ इमेज एक्सट्रैक्शन विफल हो जाए या सक्षम न हो—और गरिमापूर्ण ढंग से केवल मेटाडेटा दिखाने पर वापस लौट आता है।

दस्तावेज़ समरी बनाना

विज़ुअलाइज़र को एक और मेथड चाहिए: एक उच्च-स्तरीय समरी जो उपयोगकर्ताओं को दस्तावेज़ संरचना का अवलोकन दे:

def get_document_summary(self) -> Dict[str, Any]:
    pages = getattr(self.doc, "pages", {})
    texts = getattr(self.doc, "texts", [])
    tables = getattr(self.doc, "tables", [])
    pictures = getattr(self.doc, "pictures", [])

    text_types = {}
    for item in texts:
        label = getattr(item, "label", "unknown")
        text_types[label] = text_types.get(label, 0) + 1

    return {
        "name": self.doc.name,
        "num_pages": len(pages) if pages else 0,
        "num_texts": len(texts),
        "num_tables": len(tables),
        "num_pictures": len(pictures),
        "text_types": text_types
    }

हम दस्तावेज़ में पेज, टेक्स्ट एलिमेंट्स, तालिकाओं और छवियों की संख्या गिनते हैं। text_types डिक्शनरी टेक्स्ट एलिमेंट्स को उनके लेबल के हिसाब से तोड़ती है, दिखाती है कि Docling ने कितने टाइटल, हेडर, पैराग्राफ और अन्य एलिमेंट्स पहचाने। यह उपयोगकर्ताओं को दस्तावेज़ की संरचना और जटिलता का त्वरित अंदाज़ा देता है।

Streamlit में विज़ुअलाइज़र जोड़ना

विज़ुअलाइज़र तैयार होने के साथ, आइए इसे Streamlit में चार टैब के साथ इंटीग्रेट करें: Summary, Hierarchy, Tables और Images।

def render_structure_viz():
    st.title("📊 Document Structure")

    if not st.session_state.docling_docs:
        st.info("👈 Please upload and process your documents first!")
        return

    doc_names = [doc["filename"] for doc in st.session_state.docling_docs]
    selected_doc_name = st.selectbox("Select document to analyze:", doc_names)

    selected_doc_data = next(
        (doc for doc in st.session_state.docling_docs if doc["filename"] == selected_doc_name),
        None
    )

    if not selected_doc_data:
        return

    visualizer = DocumentStructureVisualizer(selected_doc_data["doc"])

    tab1, tab2, tab3, tab4 = st.tabs(["📑 Summary", "🏗️ Hierarchy", "📊 Tables", "🖼️ Images"])

हम उपयोगकर्ताओं को यह चुनने देने के लिए एक ड्रॉपडाउन बनाते हैं कि किस दस्तावेज़ का विश्लेषण करना है (एक से अधिक फ़ाइलें अपलोड होने पर उपयोगी)। सत्र अवस्था से चुना गया Docling दस्तावेज़ लेने के बाद, हम विज़ुअलाइज़र इनस्टैंस बनाते हैं और चार टैब बनाते हैं।

with tab1:
    st.subheader("Document Summary")
    summary = visualizer.get_document_summary()

    col1, col2, col3, col4 = st.columns(4)
    with col1:
        st.metric("Pages", summary["num_pages"])
    with col2:
        st.metric("Tables", summary["num_tables"])
    with col3:
        st.metric("Images", summary["num_pictures"])
    with col4:
        st.metric("Text Items", summary["num_texts"])

    st.subheader("Content Types")
    text_types_df = pd.DataFrame([
        {"Type": k, "Count": v}
        for k, v in sorted(summary["text_types"].items(), key=lambda x: -x[1])
    ])
    st.dataframe(text_types_df, use_container_width=True)

with tab2:
    st.subheader("Document Hierarchy")
    hierarchy = visualizer.get_document_hierarchy()

    if hierarchy:
        for item in hierarchy:
            indent = "  " * (item["level"] - 1)
            st.markdown(f"{indent}**{item['text']}** _(Page {item['page']})_")
    else:
        st.info("No hierarchical structure detected")

with tab3:
    st.subheader("Tables")
    tables_info = visualizer.get_tables_info()

    if tables_info:
        for table_data in tables_info:
            st.markdown(f"### Table {table_data['table_number']} (Page {table_data['page']})")

            if table_data["caption"]:
                st.caption(table_data["caption"])

            if not table_data["is_empty"]:
                st.dataframe(table_data["dataframe"], use_container_width=True)
            else:
                st.info("Table is empty")

            st.divider()
    else:
        st.info("No tables found in this document")

with tab4:
    st.subheader("Images")
    pictures_info = visualizer.get_pictures_info()

    if pictures_info:
        for pic_data in pictures_info:
            st.markdown(f"**Image {pic_data['picture_number']}** (Page {pic_data['page']})")

            if pic_data["caption"]:
                st.caption(pic_data["caption"])

            if pic_data["pil_image"] is not None:
                st.image(pic_data["pil_image"], use_container_width=True)
            else:
                st.info("Image data not available")

            if pic_data["bounding_box"]:
                bbox = pic_data["bounding_box"]
                with st.expander("📐 Position Details"):
                    st.text(
                        f"Position: ({bbox['left']:.1f}, {bbox['top']:.1f}) - "
                        f"({bbox['right']:.1f}, {bbox['bottom']:.1f})"
                    )

            st.divider()
    else:
        st.info("No images found in this document")

Document Structure टैब का स्क्रीनशॉट, जिसमें चारों सब-टैब दिख रहे हैं: समरी में दस्तावेज़ मेट्रिक्स, हायरार्की में इंडेंटेड हेडिंग्स, टेबल्स में इंटरैक्टिव DataFrames, और इमेजेस में कैप्शन सहित निकली हुई तस्वीरें

संरचना विज़ुअलाइज़र पूरा हो गया है। उपयोगकर्ता दस्तावेज़ अपलोड कर सकते हैं और तुरंत उसकी रचना देख सकते हैं — कितने पेज, तालिकाएँ और छवियाँ हैं; हेडिंग और सेक्शंस के साथ हायरार्किकल संरचना; इंटरैक्टिव तालिकाएँ; और दस्तावेज़ से निकली वास्तविक छवियाँ। यह पारदर्शिता उपयोगकर्ताओं को दिखाती है कि Docling ने क्या निकाला और सिस्टम में भरोसा बनाती है।

दस्तावेज़ प्रोसेसिंग और विज़ुअलाइज़ेशन काम करने के साथ, अब हम RAG सिस्टम बनाएँगे जो इन प्रोसेस्ड दस्तावेज़ों पर प्रश्न-उत्तर सक्षम करता है।

RAG वेक्टर स्टोर और सर्च टूल बनाना

📄 पूर्ण स्क्रिप्ट्स: src/vectorstore.py | src/tools.py

दस्तावेज़ प्रोसेसिंग और विज़ुअलाइज़ेशन पूर्ण होने के बाद, हम Q&A क्षमताएँ बना सकते हैं। RAG (Retrieval Augmented Generation) उपयोगकर्ताओं को अपने दस्तावेज़ों के बारे में प्रश्न पूछने देता है। सिस्टम दस्तावेज़ों को एम्बेडिंग्स में बदलता है, उन्हें वेक्टर डेटाबेस में स्टोर करता है, और प्रश्नों के उत्तर के लिए प्रासंगिक खंड रिट्रीव करता है।

इस खंड में दो घटक शामिल हैं: एक वेक्टर स्टोर मैनेजर जो दस्तावेज़ों को टुकड़ों में बाँटता और एम्बेड करता है, और एक सर्च टूल जो प्रासंगिक जानकारी रिट्रीव करता है।

वेक्टर स्टोर सेटअप करना

वेक्टर स्टोर वह जगह है जहाँ दस्तावेज़ एम्बेडिंग्स रहती हैं। जब उपयोगकर्ता प्रश्न पूछता है, तो हम इसी स्टोर में प्रासंगिक टुकड़े खोजते हैं और LLM को संदर्भ के रूप में देते हैं।

src/ डायरेक्टरी में vectorstore.py बनाएँ:

from typing import List
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

ये इम्पोर्ट्स LangChain के घटकों को लाते हैं: दस्तावेज़ हैंडलिंग, टेक्स्ट स्प्लिटिंग, एम्बेडिंग्स जेनरेशन, और ChromaDB वेक्टर स्टोरेज। ये साथ मिलकर इस तरह काम करते हैं:

class VectorStoreManager:
    def __init__(self):
        self.embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
        self.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,
            chunk_overlap=100,
            length_function=len,
        )

हम दो प्रमुख घटक इनिशियलाइज़ करते हैं। OpenAI का text-embedding-3-small मॉडल टेक्स्ट को वेक्टर में बदलता है। यह text-embedding-3-large से छोटा और तेज़ है, जो सैकड़ों टुकड़ों को एम्बेड करते समय मायने रखता है। RecursiveCharacterTextSplitter दस्तावेज़ों को 1000-कैरेक्टर के टुकड़ों में 100-कैरेक्टर ओवरलैप के साथ बाँटता है, ताकि सीमा पर मौजूद महत्वपूर्ण जानकारी वाक्य के बीच में कट न जाए।

1000 कैरेक्टर क्यों?

1000 कैरेक्टर सटीकता और संदर्भ के बीच संतुलन देता है — छोटे टुकड़े सटीक रिट्रीवल देते हैं पर संदर्भ खो देते हैं, बड़े टुकड़े संदर्भ बचाते हैं पर प्रासंगिकता कमजोर करते हैं।

चंकिंग मेथड जोड़ें:

def chunk_documents(self, documents: List[Document]) -> List[Document]:
    """Split documents into smaller chunks for better retrieval."""
    chunks = self.text_splitter.split_documents(documents)
    return chunks

स्प्लिटर मेटाडेटा को स्वचालित रूप से सँभालता है, मूल दस्तावेज़ों की जानकारी सुरक्षित रखते हुए। हर टुकड़ा जानता है कि वह किस फ़ाइल से आया है, जो एजेंट के उत्तरों में स्रोत उद्धरण देते समय महत्वपूर्ण होता है।

अब वह मेथड जोड़ें जो वेक्टर स्टोर बनाता है:

def create_vectorstore(self, chunks: List[Document]) -> Chroma:
    """Create a Chroma vector store from document chunks."""
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=self.embeddings,
        collection_name="documents"
    )
    return vectorstore

ChromaDB भारी काम सँभालता है। यह हर टुकड़े को हमारे एम्बेडिंग्स मॉडल से एम्बेड करता है और वेक्टर को मेमोरी में स्टोर करता है। जब आप सिमिलैरिटी सर्च चलाते हैं, तो ChromaDB क्वेरी वेक्टर और सभी दस्तावेज़ वेक्टरों के बीच कोसाइन सिमिलैरिटी निकालता है और सबसे नज़दीकी मैच लौटाता है।

सर्च टूल बनाना

वेक्टर स्टोर एम्बेडिंग्स सँभालने के लिए तैयार है, अब हमें एजेंट को इसे क्वेरी करने का तरीका देना है। टूल वे फ़ंक्शंस होते हैं जिन्हें LLM तब कॉल कर सकता है जब उसे ऐसी जानकारी चाहिए जो उसके पास नहीं है।

src/ डायरेक्टरी में tools.py बनाएँ:

from typing import Annotated
from langchain_core.tools import tool

def create_search_tool(vectorstore):
    """Create a search tool that has access to the vector store."""

यह फ़ैक्टरी फ़ंक्शन एक क्लोज़र पैटर्न का उपयोग करता है: यह वेक्टर स्टोर लेता है और ऐसा टूल लौटाता है जो उसे सर्च कर सके। टूल ग्लोबल वेरिएबल्स की ज़रूरत के बिना वेक्टर स्टोर तक पहुँच बनाए रखता है।

@tool
def search_documents(query: Annotated[str, "The search query or question about the documents"]) -> str:
    """Search the uploaded documents for relevant information."""

@tool डेकोरेटर इस फ़ंक्शन को ऐसा LangChain टूल में बदल देता है जिसे एजेंट कॉल कर सकता है। Annotated टाइप हिंट पैरामीटर का वर्णन करता है, जिससे LLM को समझने में मदद मिलती है कि टूल कॉल करते समय क्या पास करना है।

try:
   results = vectorstore.similarity_search(query, k=8)

   if not results:
      return "No relevant information found in the documents for this query."

हम वेक्टर स्टोर से 8 समान टुकड़े (k=8) रिट्रीव करते हैं। यह LLM को पर्याप्त संदर्भ देता है बिना उसे अनावश्यक जानकारी से भर दिए। सही संख्या आपके दस्तावेज़ प्रकार पर निर्भर करती है—घने तकनीकी दस्तावेज़ कम टुकड़ों (k=4–6) से बेहतर काम कर सकते हैं, जबकि कथात्मक दस्तावेज़ अधिक (k=10–12) से लाभान्वित हो सकते हैं।

context_parts = []
for i, doc in enumerate(results, 1):
    source = doc.metadata.get("filename", doc.metadata.get("source", "Unknown source"))
    content = doc.page_content.strip()

    context_parts.append(
        f"[Source {i}: {source}]\n"
        f"Content: {content}\n"
    )

return "\n---\n".join(context_parts)

हम हर टुकड़े को उसके स्रोत फ़ाइलनाम के साथ फ़ॉर्मैट करते हैं, फिर उन्हें सेपरेटर से जोड़ते हैं। LLM को यह संरचित संदर्भ मिलता है और वह इसका उपयोग स्रोतों का हवाला देते हुए उत्तर बनाने में करता है।

except Exception as e:
    return f"Error searching documents: {str(e)}"

return search_documents

एरर हैंडलिंग सुनिश्चित करती है कि सर्च विफल होने पर एजेंट को स्पष्ट संदेश मिले, क्रैश होने के बजाय। फ़ंक्शन कॉन्फ़िगर किए गए टूल को लौटाता है, जो एजेंट द्वारा उपयोग के लिए तैयार है।

वेक्टर स्टोर और सर्च टूल अब तैयार हैं। अगला, हम LangGraph एजेंट बनाएँगे जो रिट्रीवल और जेनरेशन को समन्वित करके उपयोगकर्ता प्रश्नों का उत्तर देता है।

स्ट्रीमिंग के साथ LangGraph एजेंट बनाना

📄 पूर्ण स्क्रिप्ट: src/agent.py

सर्च टूल वेक्टर स्टोर को क्वेरी कर सकता है, पर उसे एक बुद्धिमान समन्वयक चाहिए। LangGraph एजेंट तय करता है कि कब सर्च करना है, नतीजों की व्याख्या करता है, और स्वाभाविक भाषा में उत्तर बनाता है। हम रीयल-टाइम प्रगति दिखाने के लिए स्ट्रीमिंग भी लागू करेंगे।

LangGraph एजेंट बनाना

एजेंट प्रश्न प्राप्त करता है, तय करता है कि कब दस्तावेज़ सर्च करने हैं, और रिट्रीव किए गए संदर्भ के आधार पर उत्तर बनाता है।

src/ डायरेक्टरी में agent.py बनाएँ:

from typing import List
from langchain_core.tools import BaseTool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver

हम टूल हैंडलिंग, OpenAI के चैट मॉडल्स, LangGraph के ReAct एजेंट इम्प्लीमेंटेशन, और वार्तालाप मेमोरी के लिए घटक इम्पोर्ट करते हैं।

SYSTEM_PROMPT = """You are a helpful document intelligence assistant. You have access to documents that have been uploaded and processed.

GUIDELINES:
- Use the search_documents tool to find relevant information
- Keep it simple: one well-crafted search is usually sufficient
- Only search again if the first results are clearly incomplete
- Provide clear, accurate answers based on the document contents
- Always cite your sources with filenames
- If information isn't found, say so clearly
- Be concise but thorough

When answering:
1. Search the documents with a focused query
2. Synthesize a clear answer from the results
3. Include source citations (filenames)
4. Only search again if absolutely necessary
"""

def create_documentation_agent(tools: List[BaseTool], model_name: str = "gpt-4o-mini"):
    """Create a document intelligence assistant agent using LangGraph."""
    llm = ChatOpenAI(model=model_name, temperature=0)
    memory = MemorySaver()

हम gpt-4o-mini का प्रयोग करते हैं, gpt-5 के बजाय, क्योंकि यह तेज़ और सस्ता है, फिर भी दस्तावेज़ Q&A को अच्छी तरह सँभालता है। टेम्परेचर 0 पर सेट है ताकि उत्तर सुसंगत और तथ्यपरक रहें। MemorySaver एजेंट को वार्तालाप मेमोरी देता है, ताकि वह सत्र के भीतर पिछले आदान-प्रदान याद रखे।

agent = create_react_agent(
    llm,
    tools=tools,
    prompt=SYSTEM_PROMPT,
    checkpointer=memory
)

return agent

LangGraph का create_react_agent ReAct पैटर्न (Reasoning + Acting) लागू करता है। एजेंट सोचता है कि उसे क्या करना है, टूल्स का उपयोग करके कार्रवाई करता है, परिणामों का निरीक्षण करता है, और उत्तर मिलने तक दोहराता है। यह पैटर्न RAG के लिए अच्छा काम करता है क्योंकि एजेंट तय कर सकता है कि कब सर्च करना है और रिट्रीव किए संदर्भ का इस्तेमाल कैसे करना है।

स्ट्रीमिंग प्रतिक्रियाएँ लागू करना

अब एजेंट सर्च कर सकता है और उत्तर बना सकता है, पर उपयोगकर्ताओं को 10 सेकंड तक खाली स्क्रीन नहीं दिखनी चाहिए। स्ट्रीमिंग रीयल-टाइम में प्रगति दिखाती है — पहले “सोच रहा है” इंडिकेटर, फिर “सर्च कर रहा है”, फिर उत्तर टोकन-बाय-टोकन प्रकट होता है।

अपने app.py में render_chat() फ़ंक्शन अपडेट करें ताकि हम पहले TODO से चिह्नित किए गए रेस्पॉन्स हैंडलिंग को पूरा कर सकें:

if prompt:
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

हम उपयोगकर्ता का संदेश वार्तालाप इतिहास में जोड़ते हैं और उसे चैट इंटरफ़ेस में दिखाते हैं।

with st.chat_message("assistant"):
    status_placeholder = st.empty()
    message_placeholder = st.empty()

    try:
        config = {"configurable": {"thread_id": "document_chat"}}

हम स्थिति संकेतक और उत्तर संदेश के लिए प्लेसहोल्डर बनाते हैं। कॉन्फ़िग में थ्रेड ID शामिल है, जिसका उपयोग LangGraph वार्तालाप मेमोरी बनाए रखने के लिए करता है।

def generate_response():
    """Generator that yields tokens from LangGraph stream."""
    status_placeholder.markdown("🤔 **Thinking...**")
    first_content_token = True
    tool_call_detected = False
    final_answer_started = False

यह जेनरेटर फ़ंक्शन LangGraph के स्ट्रीम आउटपुट को प्रोसेस करता है। हम एजेंट की प्रगति के अनुरूप उपयुक्त स्थिति संदेश दिखाने के लिए फ़्लैग्स से स्टेट ट्रैक करते हैं।

for msg, metadata in st.session_state.agent.stream(
    {"messages": [HumanMessage(content=prompt)]},
    config=config,
    stream_mode="messages",
):
    langgraph_node = metadata.get("langgraph_node", "")

stream_mode="messages" पैरामीटर हमें अंतिम आउटपुट नहीं, बल्कि जेनरेट होते हुए वास्तविक LLM टोकन देता है। LangGraph एजेंट के निष्पादन के दौरान इवेंट्स एमिट करता है—जब वह सोचना शुरू करता है, जब वह टूल कॉल करता है, और जब वह टेक्स्ट जेनरेट करता है।

if "tools" in langgraph_node.lower() or "tool" in langgraph_node.lower():
    if not tool_call_detected:
        status_placeholder.markdown("🔍 **Searching documents...**")
        tool_call_detected = True
    continue

if "agent" in langgraph_node.lower() and hasattr(msg, "content"):
    content = msg.content

if content:
    if first_content_token:
        status_placeholder.markdown("💬 **Generating answer...**")
        first_content_token = False
        final_answer_started = True

if final_answer_started:
    yield content

जब एजेंट नोड अंतिम उत्तर जेनरेट करना शुरू करता है, तो हम स्थिति अपडेट करते हैं और कंटेंट टोकन यील्ड करना शुरू करते हैं। हर टोकन तुरंत दिखाया जाता है, जिससे स्मूद टाइपिंग इफ़ेक्ट बनता है।

status_placeholder.empty()

with message_placeholder.container():
    full_response = st.write_stream(generate_response())

स्ट्रीमिंग पूरी होने के बाद, हम स्थिति संकेतक साफ़ कर देते हैं। Streamlit का st.write_stream() टोकन-बाय-टोकन डिस्प्ले स्वचालित रूप से सँभालता है, टोकन एकत्र करता है और UI को स्मूदली अपडेट करता है। परिणाम एक चैट अनुभव है जो उत्तरदायी लगता है और उपयोगकर्ताओं को भरोसा देता है कि सिस्टम काम कर रहा है।

st.session_state.messages.append({"role": "assistant", "content": full_response})

except Exception as e:
    st.error(f"Error generating response: {str(e)}")

हम पूर्ण उत्तर को संदेश इतिहास में सहेजते हैं और स्ट्रीमिंग के दौरान होने वाली किसी भी त्रुटि को सँभालते हैं।

RAG सिस्टम पूरा हो गया है। अब उपयोगकर्ता दस्तावेज़ अपलोड कर सकते हैं, उन्हें Docling से प्रोसेस कर सकते हैं, उनकी संरचना की जाँच कर सकते हैं, और सामग्री के बारे में स्वाभाविक वार्तालाप कर सकते हैं। एजेंट बुद्धिमानी से सर्च करता है, स्रोत उद्धृत करता है, और उत्तर स्ट्रीम करता है, जिससे अनुभव स्मूद बनता है।

परीक्षण और मान्यकरण

आपका Document Intelligence Assistant तैयार है। परिनियोजन या साझा करने से पहले, आपको यह सत्यापित करने के लिए एप्लिकेशन का परीक्षण करना चाहिए कि सब कुछ सही से काम कर रहा है।

एप्लिकेशन चलाना

परियोजना रूट से एप्लिकेशन शुरू करें:

streamlit run app.py

Streamlit http://localhost:8501 पर ब्राउज़र विंडो खोलेगा। आपको साइडबार में अपलोड कंट्रोल्स और मुख्य क्षेत्र में दो टैब दिखने चाहिए।

दस्तावेज़ प्रोसेसिंग पाइपलाइन का परीक्षण

पूरी प्रोसेसिंग क्षमताओं का परीक्षण करने के लिए तालिकाओं और छवियों वाला कोई सैंपल PDF अपलोड करें:

  1. साइडबार में “Upload Documents” पर क्लिक करें
  2. कोई PDF फ़ाइल चुनें (बेहतर है कि उसमें तालिकाएँ, छवियाँ और साफ़ हेडिंग्स हों)
  3. “Process & Index” पर क्लिक करें

प्रोसेसिंग संकेतकों पर ध्यान दें:

  • “Processing documents with Docling…” (दस्तावेज़ के आकार पर निर्भर करते हुए 20–60 सेकंड)
  • “Chunking documents…”
  • “Creating vector store…”
  • “Creating agent…”

पूरा होने पर, आपको स्थिति सेक्शन में “✅ Ready to chat!” दिखेगा।

दस्तावेज़ संरचना विज़ुअलाइज़ेशन का परीक्षण

“Document Structure” टैब पर स्विच करें और सत्यापित करें कि Docling ने सब कुछ सही निकाला:

  1. Summary टैब: गिनतियाँ आपके दस्तावेज़ से मेल खाती हों (पेज, तालिकाएँ, छवियाँ, टेक्स्ट तत्व)
  2. Hierarchy टैब: हेडिंग्स सही क्रम में और उचित इंडेंटेशन के साथ दिखें
  3. Tables टैब: तालिकाएँ उलझे टेक्स्ट के बजाय इंटरैक्टिव DataFrames के रूप में दिखें
  4. Images टैब: छवियाँ सही रेंडर हों (यदि इमेज एक्सट्रैक्शन सक्षम था)

यदि तालिकाएँ खाली दिखें या छवियाँ न दिखें, तो जाँचें कि आपने पाइपलाइन विकल्पों में do_table_structure=True और generate_picture_images=True सक्षम किए हैं।

Q&A सिस्टम का परीक्षण

“Chat” टैब पर लौटें और इन नमूना प्रश्नों से एजेंट का परीक्षण करें:

अच्छे प्रारंभिक प्रश्न:

  • “यह दस्तावेज़ किस बारे में है?”
  • “कवर किए गए मुख्य विषयों का सारांश दें”
  • “इस दस्तावेज़ में कौन-सी तालिकाएँ शामिल हैं?”
  • “किसी भी फ़िगर या छवियों को उनके कैप्शन सहित सूचीबद्ध करें”

दस्तावेज़-विशिष्ट प्रश्न (अपने दस्तावेज़ के अनुसार समायोजित करें):

  • “टेबल 1 में कौन-सा डाटा दिखाया गया है?”
  • “सेक्शन 3 में वर्णित कार्यप्रणाली समझाएँ”
  • “मुख्य निष्कर्ष क्या हैं?”
  • “लेखक कौन हैं?”

फ़ॉलो-अप प्रश्न वार्तालाप मेमोरी जाँचने के लिए:

  • किसी तालिका के बारे में पूछने के बाद: “यह डाटा हमें क्या बताता है?”
  • उत्तर मिलने के बाद: “क्या आप इसे और सरल शब्दों में समझा सकते हैं?”

किन बातों पर ध्यान दें

अपने दस्तावेज़ से सीधे उत्तर देखें जिनमें स्रोत उद्धरण हों, स्मूद स्ट्रीमिंग टोकन, और स्थिति संकेतक (“Thinking…”, “Searching documents…”, “Generating answer…” )। इस बात पर नज़र रखें: आपके दस्तावेज़ से न आए सामान्य उत्तर, गायब स्रोत उद्धरण, बिना स्थिति संकेतकों के धीमे उत्तर, या API कुंजी गायब होने की त्रुटियाँ।

आम समस्याएँ और समाधान

“No module named ‘docling’” त्रुटि

pip install docling langchain langchain-openai langchain-chroma langgraph streamlit streamlit-extras pandas python-dotenv

“OpenAI API key not found” त्रुटि

  • सत्यापित करें कि .env फ़ाइल मौजूद है और उसमें OPENAI_API_KEY=your-key-here है

  • Streamlit को पुनः प्रारंभ करें

प्रोसेसिंग 60 सेकंड से अधिक ले रही है

  • पहली बार के लिए सामान्य (Docling ~500MB मॉडल्स डाउनलोड करता है) या बड़े दस्तावेज़ों के लिए

एजेंट सामान्य उत्तर देता है

  • सत्यापित करें कि दस्तावेज़ प्रोसेस हुए और वेक्टर स्टोर बना
  • अधिक विशिष्ट प्रश्न आज़माएँ

तालिकाएँ खाली DataFrames के रूप में दिखती हैं

  • पुष्टि करें कि PdfPipelineOptions में do_table_structure=True है

  • नेटिव तालिकाओं वाले किसी अलग PDF से प्रयास करें

एप्लिकेशन अब मान्य है और उपयोग के लिए तैयार है। आप इसे अपने दस्तावेज़ों के साथ परीक्षण कर सकते हैं या दूसरों के उपयोग के लिए परिनियोजित कर सकते हैं।

निष्कर्ष

अब आपके पास काम करने वाला Document Intelligence Assistant है जो PDFs, Word दस्तावेज़ों, PowerPoint प्रस्तुतियों और HTML फ़ाइलों को उनकी संरचना सुरक्षित रखते हुए प्रोसेस करता है। Docling टेक्स्ट, तालिकाएँ, छवियाँ और दस्तावेज़ हायरार्की निकालता है, जिन्हें उपयोगकर्ता इंटरैक्टिव टैब्स से एक्सप्लोर कर सकते हैं। RAG सिस्टम, ChromaDB और LangGraph के साथ मिलकर, स्रोत उद्धरणों के साथ रीयल-टाइम स्ट्रीम्ड वार्तालाप Q&A सक्षम करता है। यह दिखाता है कि संरचना-सचेत दस्तावेज़ प्रोसेसिंग, बेसिक टेक्स्ट एक्सट्रैक्शन की तुलना में रिट्रीवल गुणवत्ता कैसे सुधारती है। पूरा सोर्स कोड यहाँ उपलब्ध है: GitHub रिपॉज़िटरी में।

यह नींव कई दिशाओं में विस्तार के अवसर खोलती है:

  • बैच प्रोसेसिंग और स्थायी वेक्टर स्टोरेज जोड़ें
  • स्कैन दस्तावेज़ों के लिए OCR सक्षम करें और विभिन्न इंजनों के साथ प्रयोग करें
  • Streamlit Cloud पर परिनियोजित करें या Docker से कंटेनराइज़ करें
  • निकाले गए चार्ट्स का विश्लेषण करने के लिए विज़न-लैंग्वेज मॉडल्स इंटीग्रेट करें
  • डोमेन-विशिष्ट एक्सट्रैक्शन नियम बनाएँ या एक्सपोर्ट फ़ंक्शनैलिटी जोड़ें

RAG सिस्टम्स और LLM एप्लिकेशन की गहरी पड़ताल के लिए, हमारा AI Engineering ट्रैक यहाँ प्रयुक्त अवधारणाएँ और पैटर्न कवर करता है।


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

मैं 2 से अधिक वर्षों के अनुभव वाला डेटा साइंस कंटेंट क्रिएटर हूँ और Medium पर सबसे बड़े फॉलोइंग्स में से एक रखता हूँ। मुझे एआई और एमएल पर विस्तार से लेख लिखना पसंद है, जिसमें हल्का-सा व्यंग्यात्मक अंदाज रहता है—क्योंकि कुछ तो करना पड़ता है ताकि वे कम उबाऊ लगें। मैंने 130 से अधिक लेख और एक DataCamp कोर्स तैयार किया है, और एक और बन रहा है। मेरा कंटेंट 50 लाख से अधिक लोगों ने देखा है, जिनमें से 20 हज़ार लोग Medium और LinkedIn पर फॉलोअर्स बन गए। 

विषय
कृत्रिम बुद्धिमत्ता

DataCamp के साथ सीखें

कोर्स

LangChain के साथ LLM एप्लिकेशन विकसित करना

3 घंटा
51.6K
जानें कि LangChain में LLMs, prompts, chains, और agents का उपयोग करके AI-संचालित applications कैसे बनाएं।
विवरण देखेंRight Arrow
पाठ्यक्रम शुरू करें

लर्निंग पाथ

Python डेवलपर

28 घंटा
कोड का परीक्षण करने और संस्करण नियंत्रण लागू करने से लेकर वेब स्क्रैपिंग और पैकेज विकसित करने तक, अपने Python डेवलपर सफर में अगला कदम बढ़ाएँ!
और देखेंRight Arrow