Last chance! 50% off DataCamp Premium
Sale ends in
pypdf या PDFMiner जैसे पारंपरिक PDF एक्सट्रैक्शन टूल आपको सादा टेक्स्ट देते हैं, लेकिन दस्तावेज़ की संरचना खो देते हैं। तालिकाएँ उलझे हुए टेक्स्ट में बदल जाती हैं, हेडर मुख्य सामग्री के साथ घुल-मिल जाते हैं, और छवियाँ गायब हो जाती हैं। RAG सिस्टम में, यह बिखरा हुआ डेटा कमजोर रिट्रीवल और अविश्वसनीय उत्तरों का कारण बनता है। Docling IBM Research का एक ओपन-सोर्स टूलकिट है जो कंप्यूटर विज़न मॉडल्स से दस्तावेज़ लेआउट को समझता है और तालिकाएँ, छवियाँ, हेडिंग्स और संरचना सुरक्षित रखता है। यह पारंपरिक OCR-आधारित विधियों की तुलना में दस्तावेज़ों को 30 गुना तक तेज़ी से प्रोसेस करता है और आपकी मशीन पर स्थानीय रूप से चलता है।
इस ट्यूटोरियल में, हम Docling का उपयोग करके एक Document Intelligence Assistant बनाएंगे — एक Streamlit वेब ऐप जो आपको दस्तावेज़ अपलोड करने, उनकी संरचना विज़ुअलाइज़ करने और RAG-संचालित चैटबॉट से प्रश्न पूछने देता है। आप सीखेंगे कि Docling से मल्टी-फ़ॉर्मैट दस्तावेज़ कैसे प्रोसेस करें, तालिकाएँ और छवियाँ निकालकर दिखाएँ, ChromaDB के साथ वेक्टर स्टोर बनाएँ, और LangGraph से एक वार्तालाप एजेंट तैयार करें। अंत तक, आपके पास एक काम करने वाला एप्लिकेशन होगा जो जटिल दस्तावेज़ों को संरचित डेटा में बदलता है और बुद्धिमान प्रश्न-उत्तर को सक्षम बनाता है।
एप्लिकेशन प्रीव्यू:

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

इस ट्यूटोरियल को शुरू करने से पहले, आपके पास ये होना चाहिए:
तकनीकी कौशल: Python की कक्षाएँ (classes), डेकोरेटर्स, टाइप हिंट्स, और कॉन्टेक्स्ट मैनेजर्स के साथ सहजता। हम पूरे समय async ऑपरेशन और फ़ैक्टरी पैटर्न का उपयोग करेंगे। बड़े भाषा मॉडल कैसे प्रॉम्प्ट्स, टोकन और एम्बेडिंग्स के साथ काम करते हैं, इसकी समझ आवश्यक है। रिट्रीवल-ऑगमेंटेड जेनरेशन सिस्टम और वेक्टर डेटाबेस से परिचित होना सहायक है, पर अनिवार्य नहीं — हम निर्माण करते समय मुख्य अवधारणाएँ समझाएँगे।
डेवलपमेंट सेटअप: पैकेज प्रबंधन के लिए pip सहित Python 3.10 या उससे उच्च। VS Code जैसा कोड संपादक अनुशंसित है। आपको OpenAI API कुंजी की आवश्यकता होगी platform.openai.com से — प्रोसेसिंग लागत लगभग $0.10–0.20 प्रति दस्तावेज़।
समय प्रतिबद्धता: ट्यूटोरियल पूरा करने में 60–90 मिनट रखें, जिसमें व्याख्याएँ पढ़ना, कोड लिखना और एप्लिकेशन का परीक्षण शामिल है। यह ट्यूटोरियल मध्यम-स्तर के Python कौशल मानकर चलता है।
अधिकांश दस्तावेज़ प्रोसेसिंग टूल PDFs को इमेज फ़ाइल या टेक्स्ट स्ट्रीम की तरह मानते हैं। वे या तो हर पेज पर OCR चलाते हैं या बिना समझे सादा टेक्स्ट निकालते हैं। Docling एक अलग दृष्टिकोण लेता है। यह IBM Research का ओपन-सोर्स टूलकिट है जो कंप्यूटर विज़न मॉडल्स से दस्तावेज़ संरचना को उसी तरह समझता है जैसे कोई इंसान समझे।
जब आप कोई दस्तावेज़ Docling में देते हैं, दो AI मॉडल उसका विश्लेषण करते हैं:
ये मॉडल समझते हैं कि दस्तावेज़ सिर्फ टेक्स्ट का ढेर नहीं है। इसमें अनुक्रम (हायरार्की), संबंध और अर्थ होते हैं।
RAG सिस्टम के लिए यह संरचनात्मक समझ महत्वपूर्ण है। जब आप रिट्रीवल-ऑगमेंटेड जेनरेशन एप्लिकेशन बना रहे होते हैं, तो आपके दस्तावेज़ प्रोसेसिंग की गुणवत्ता सीधे रिट्रीवल की शुद्धता को प्रभावित करती है। अगर आपका PDF एक्सट्रैक्शन किसी वित्तीय तालिका को उलझे टेक्स्ट में बदल दे, तो आपका वेक्टर सर्च कचरा लौटाएगा। Docling संरचना को सुरक्षित रखता है, ताकि जब आप अपने दस्तावेज़ों को टुकड़ों में बाँटकर एम्बेड करें, तो आप साफ़-सुथरे, व्यवस्थित डेटा के साथ काम करें।
Docling बॉक्स से बाहर कई दस्तावेज़ फ़ॉर्मैट का समर्थन करता है:
आप स्कैन किए गए दस्तावेज़ों के लिए 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 होते हैं।
दस्तावेज़ हायरार्की निकालने के बाद, अब तालिकाओं को सँभालें। साधारण टेक्स्ट एक्सट्रैक्शन तालिकाओं को उलझे स्ट्रिंग्स में बदल देता है, जबकि 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 में बदल देता है। हम उपलब्ध होने पर कैप्शन और पेज नंबर निकालते हैं।
तालिकाओं से आगे, 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 में चार टैब के साथ इंटीग्रेट करें: 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")

संरचना विज़ुअलाइज़र पूरा हो गया है। उपयोगकर्ता दस्तावेज़ अपलोड कर सकते हैं और तुरंत उसकी रचना देख सकते हैं — कितने पेज, तालिकाएँ और छवियाँ हैं; हेडिंग और सेक्शंस के साथ हायरार्किकल संरचना; इंटरैक्टिव तालिकाएँ; और दस्तावेज़ से निकली वास्तविक छवियाँ। यह पारदर्शिता उपयोगकर्ताओं को दिखाती है कि Docling ने क्या निकाला और सिस्टम में भरोसा बनाती है।
दस्तावेज़ प्रोसेसिंग और विज़ुअलाइज़ेशन काम करने के साथ, अब हम 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 कैरेक्टर सटीकता और संदर्भ के बीच संतुलन देता है — छोटे टुकड़े सटीक रिट्रीवल देते हैं पर संदर्भ खो देते हैं, बड़े टुकड़े संदर्भ बचाते हैं पर प्रासंगिकता कमजोर करते हैं।
चंकिंग मेथड जोड़ें:
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 एजेंट बनाएँगे जो रिट्रीवल और जेनरेशन को समन्वित करके उपयोगकर्ता प्रश्नों का उत्तर देता है।
📄 पूर्ण स्क्रिप्ट: src/agent.py
सर्च टूल वेक्टर स्टोर को क्वेरी कर सकता है, पर उसे एक बुद्धिमान समन्वयक चाहिए। 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 अपलोड करें:
प्रोसेसिंग संकेतकों पर ध्यान दें:
पूरा होने पर, आपको स्थिति सेक्शन में “✅ Ready to chat!” दिखेगा।
“Document Structure” टैब पर स्विच करें और सत्यापित करें कि Docling ने सब कुछ सही निकाला:
यदि तालिकाएँ खाली दिखें या छवियाँ न दिखें, तो जाँचें कि आपने पाइपलाइन विकल्पों में do_table_structure=True और generate_picture_images=True सक्षम किए हैं।
“Chat” टैब पर लौटें और इन नमूना प्रश्नों से एजेंट का परीक्षण करें:
अच्छे प्रारंभिक प्रश्न:
दस्तावेज़-विशिष्ट प्रश्न (अपने दस्तावेज़ के अनुसार समायोजित करें):
फ़ॉलो-अप प्रश्न वार्तालाप मेमोरी जाँचने के लिए:
अपने दस्तावेज़ से सीधे उत्तर देखें जिनमें स्रोत उद्धरण हों, स्मूद स्ट्रीमिंग टोकन, और स्थिति संकेतक (“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 सेकंड से अधिक ले रही है
एजेंट सामान्य उत्तर देता है
तालिकाएँ खाली DataFrames के रूप में दिखती हैं
पुष्टि करें कि PdfPipelineOptions में do_table_structure=True है
नेटिव तालिकाओं वाले किसी अलग PDF से प्रयास करें
एप्लिकेशन अब मान्य है और उपयोग के लिए तैयार है। आप इसे अपने दस्तावेज़ों के साथ परीक्षण कर सकते हैं या दूसरों के उपयोग के लिए परिनियोजित कर सकते हैं।
अब आपके पास काम करने वाला Document Intelligence Assistant है जो PDFs, Word दस्तावेज़ों, PowerPoint प्रस्तुतियों और HTML फ़ाइलों को उनकी संरचना सुरक्षित रखते हुए प्रोसेस करता है। Docling टेक्स्ट, तालिकाएँ, छवियाँ और दस्तावेज़ हायरार्की निकालता है, जिन्हें उपयोगकर्ता इंटरैक्टिव टैब्स से एक्सप्लोर कर सकते हैं। RAG सिस्टम, ChromaDB और LangGraph के साथ मिलकर, स्रोत उद्धरणों के साथ रीयल-टाइम स्ट्रीम्ड वार्तालाप Q&A सक्षम करता है। यह दिखाता है कि संरचना-सचेत दस्तावेज़ प्रोसेसिंग, बेसिक टेक्स्ट एक्सट्रैक्शन की तुलना में रिट्रीवल गुणवत्ता कैसे सुधारती है। पूरा सोर्स कोड यहाँ उपलब्ध है: GitHub रिपॉज़िटरी में।
यह नींव कई दिशाओं में विस्तार के अवसर खोलती है:
RAG सिस्टम्स और LLM एप्लिकेशन की गहरी पड़ताल के लिए, हमारा AI Engineering ट्रैक यहाँ प्रयुक्त अवधारणाएँ और पैटर्न कवर करता है।
मैं 2 से अधिक वर्षों के अनुभव वाला डेटा साइंस कंटेंट क्रिएटर हूँ और Medium पर सबसे बड़े फॉलोइंग्स में से एक रखता हूँ। मुझे एआई और एमएल पर विस्तार से लेख लिखना पसंद है, जिसमें हल्का-सा व्यंग्यात्मक अंदाज रहता है—क्योंकि कुछ तो करना पड़ता है ताकि वे कम उबाऊ लगें। मैंने 130 से अधिक लेख और एक DataCamp कोर्स तैयार किया है, और एक और बन रहा है। मेरा कंटेंट 50 लाख से अधिक लोगों ने देखा है, जिनमें से 20 हज़ार लोग Medium और LinkedIn पर फॉलोअर्स बन गए।
DataCamp के साथ सीखें
कोर्स
कोर्स
लर्निंग पाथ