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

Perplexity Search API ट्यूटोरियल: अपने ब्राउज़र में रियल-टाइम क्लेम चेकर बनाएं

जानें कि Perplexity Search API का उपयोग करके रियल-टाइम में तथ्यात्मक दावों को सत्यापित करने वाला Chrome एक्सटेंशन कैसे बनाएं। इसमें Node.js बैकएंड सेटअप और API इंटीग्रेशन गाइड शामिल है।
अपडेट किया गया 25 सित॰ 2026  · 10 मि॰ पढ़ें

AI के साथ खोजें

ChatGPTClaudePerplexity

Perplexity ने हाल ही में अपना Perplexity Search API जारी किया है, जिसे सूक्ष्म-स्तरीय वेब रिट्रीवल के लिए डिज़ाइन किया गया है, जो पूरी पेज के बजाय रैंक किए गए, संदर्भ-समृद्ध स्निपेट्स लौटाता है। यह रियल-टाइम उपयोग मामलों—जैसे फैक्ट-चेकिंग, रिसर्च, और RAG पाइपलाइंस—के लिए बनाया गया है जहाँ गति और सटीकता मायने रखते हैं।

इस ट्यूटोरियल में, हम Claim Checker बनाएंगे—एक ब्राउज़र एक्सटेंशन जो किसी भी वेबपेज पर दिए गए तथ्यों की रियल-टाइम में पुष्टि करता है (प्राप्त वेब प्रमाण के आधार पर)। आप देखेंगे कि Perplexity Search API का लगातार अपडेटेड इंडेक्स और लो-लेटेंसी रिट्रीवल स्टैक, सेमान्टिक सिग्नल्स और मल्टी-स्टेज रीरैंकिंग के साथ मिलकर, सबसे प्रासंगिक साक्ष्य तेज़ी से ढूंढना कैसे आसान बनाता है। इस दौरान आप सीखेंगे कि कैसे:

  • Perplexity Search API का उपयोग कर वेब से उच्च-गुणवत्ता, रैंक किए गए साक्ष्य प्राप्त करें और कस्टम सॉर्टिंग एल्गोरिद्म के साथ सर्च परिणाम प्रोसेस करें।
  • अपनी API कुंजी को सुरक्षित रूप से प्रबंधित करने, CORS रिक्वेस्ट संभालने, और डोमेन एक्सट्रैक्शन व डेट-आधारित रैंकिंग के साथ इंटेलिजेंट रिज़ल्ट प्रोसेसिंग लागू करने के लिए एक Node.js बैकएंड विकसित करें।
  • Chrome/Edge एक्सटेंशन बनाएं जिसमें कंटेंट स्क्रिप्ट्स हों जो टेक्स्ट सिलेक्शन, साइड पैनल इंटरफेस, और ब्राउज़र व आपके बैकएंड सर्वर के बीच सहज संचार सक्षम करें।
  • रियल-टाइम क्लेम प्रोसेसिंग के साथ इंटरैक्टिव साइड पैनल में सत्यापन परिणाम दिखाएं, जिनमें स्रोत URLs, शीर्षक, स्निपेट्स, डोमेन्स और प्रकाशन तिथियाँ शामिल हों।

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

आप हमारा Perplexity के Comet Browser का गाइड एक अलग लेख में भी देख सकते हैं। 

Perplexity Search API क्या है?

 Perplexity Search API तेज़ और सूक्ष्म-स्तरीय वेब सर्च परिणाम प्राप्त करने का डेवलपर-फ्रेंडली तरीका है। 

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

  • किफायती: Search API की कीमत 1,000 रिक्वेस्ट पर $5 है और कोई टोकन-आधारित बिलिंग नहीं है, जिससे यह बार-बार या शेड्यूल्ड चेक चलाने वाले ऐप्स के लिए उपयुक्त है।
  • उच्च-गुणवत्ता रिट्रीवल: यह लेक्सिकल और सेमान्टिक रैंकिंग को मल्टी-स्टेज रीरैंकिंग के साथ जोड़ता है, ताकि आपको एक विशाल, लगातार रिफ्रेश होते इंडेक्स (~200M क्वेरी/दिन) से अत्यंत प्रासंगिक और अपडेटेड परिणाम मिलें।
  • लो-लेटेंसी: Perplexity Search API क्वेरियों को तेज़ी से लौटाता है, जिससे आपकी UI कई क्लेम एक साथ वेरिफाई करते समय भी उत्तरदायी बनी रहती है।

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

Perplexity Search API प्रोजेक्ट: ब्राउज़र क्लेम चेकर एक्सटेंशन

इस सेक्शन में, हम एक ब्राउज़र क्लेम चेकर एक्सटेंशन बनाएंगे जो Perplexity Search API का उपयोग करके तथ्यों की तुरंत पुष्टि करता है। यह प्रोजेक्ट इन चीज़ों को जोड़ता है:

  • एक हल्का Node.js बैकएंड जो चयनित टेक्स्ट को Perplexity Search API तक भेजता है और परिणामों को प्रोसेस करता है
  • सत्यापन विवरण दिखाने के लिए साइड पैनल इंटरफेस वाला Chrome एक्सटेंशन
  • किसी भी वेबपेज से उपयोगकर्ता द्वारा चयनित टेक्स्ट कैप्चर करने वाली कंटेंट स्क्रिप्ट

प्रोजेक्ट के अंत में, आपका रिपॉजिटरी स्ट्रक्चर कुछ इस तरह दिखना चाहिए:

ClaimChecker/
├── server/
│   ├── server.js
│   ├── package.json
│   └── .env
├── extension/
│   ├── manifest.json
│   ├── background.js
│   ├── content.js
│   ├── sidepanel.html
│   ├── sidepanel.js
│   ├── popup.html
│   └── popup.js
└── README.md

आइए इस प्रोजेक्ट के हर हिस्से को देखें और समझें कि वे ब्राउज़र के अंदर भरोसेमंद क्लेम वेरिफिकेशन देने के लिए साथ मिलकर कैसे काम करते हैं।

स्टेप 1: डिपेंडेंसीज़ और लाइब्रेरीज़ 

शुरू करने से पहले, सुनिश्चित करें कि निम्नलिखित पूर्वापेक्षाएँ मौजूद हैं:

  • एक Perplexity खाता। साइन अप या साइन इन करने के बाद, बाएँ साइडबार में API सेक्शन को एक्सपैंड करें और अपना API ग्रुप व संबंधित सेटिंग्स देखें।

  • अपने API पोर्टल में API Billing टैब पर जाएँ और अपना भुगतान तरीका जोड़ें। इस डेमो के लिए, लगभग $10 के क्रेडिट रिक्वेस्ट कवर करने के लिए पर्याप्त हैं।
  • Node.js संस्करण 18 या उससे ऊपर।
  • Chromium-आधारित ब्राउज़र (Google Chrome या Microsoft Edge)।

अब बैकएंड के लिए आवश्यक डिपेंडेंसीज़ इंस्टॉल करें:

cd server
npm install express cors dotenv

सर्वर डायरेक्टरी में एक .env फ़ाइल बनाएँ और अपनी Perplexity API कुंजी जोड़ें:

PERPLEXITY_API_KEY=your_api_key_here

अब हमारी सभी डिपेंडेंसीज़ इंस्टॉल हो गई हैं। आगे, हम अपना बैकएंड सर्वर और एक्सटेंशन बनाएँगे।

स्टेप 2: बैकएंड बनाएं

server.js फ़ाइल Express का उपयोग करके एक हल्का Node.js बैकएंड सेटअप करती है। यह बैकएंड ब्राउज़र एक्सटेंशन और Perplexity Search API के बीच सेतु का काम करता है। यह उपयोगकर्ता द्वारा चुने या दर्ज किए गए क्लेम प्राप्त करता है, Perplexity से संबंधित साक्ष्य क्वेरी करता है, और एक्सटेंशन के UI में प्रदर्शित करने के लिए एक साफ-सुथरा, संरचित रिस्पॉन्स लौटाता है।

const express = require('express');
const cors = require('cors');
require('dotenv').config();
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json());
function extractDomain(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.hostname.replace('www.', '');
  } catch {
    return url;
  }
}
app.post('/check-claim', async (req, res) => {
  try {
    const { claim } = req.body;
    if (!claim) {
      return res.status(400).json({ error: 'Claim is required' });
    }
    if (!process.env.PERPLEXITY_API_KEY) {
      return res.status(500).json({ 
        error: 'Perplexity API key not configured',
        sources: []
      });
    }
    const response = await fetch('https://api.perplexity.ai/search', {
      method: 'POST',
      headers: {
        'Authorization': Bearer ${process.env.PERPLEXITY_API_KEY},
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        query: claim,
        max_results: 20,
        max_tokens_per_page: 1024
      })
    });
    if (!response.ok) {
      throw new Error(Perplexity API error: ${response.status});
    }
    const data = await response.json();
    const searchResults = data.results || [];   
    if (searchResults.length === 0) {
      return res.json({
        claim,
        sources: [],
        totalSources: 0,
        explanation: 'No relevant sources found to verify this claim.',
        summary: 'No sources found for fact-checking.'
      });
    }
    const sortedResults = searchResults.sort((a, b) => {
      const dateA = new Date(a.last_updated || a.date || 0);
      const dateB = new Date(b.last_updated || b.date || 0);
      return dateB - dateA;
    });
    const sources = sortedResults.slice(0, 3).map((result) => ({
      url: result.url,
      title: result.title,
      snippet: result.snippet || '',
      domain: extractDomain(result.url),
      date: result.date || null,
      lastUpdated: result.last_updated || null
    }));
    const result = {
      claim,
      sources,
      totalSources: searchResults.length,
      explanation: ${searchResults.length} sources found, showing top 3.,
      summary: Found ${searchResults.length} relevant sources for fact-checking.
    };
    res.json(result);
  } catch (error) {
    res.status(500).json({ 
      error: error.message,
      sources: []
    });
  }
});
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});
app.listen(PORT, () => {
  console.log(Server running on http://localhost:${PORT});
  console.log('Make sure PERPLEXITY_API_KEY is set in .env file');
});

ऊपर दिया गया कोड Claim Checker एक्सटेंशन के लिए सुरक्षा और लॉजिक लेयर है, जो निम्न कार्य करता है:

  • सुरक्षित और तार्किक बैकएंड: यह POST /check-claim रूट के माध्यम से क्लेम स्वीकार करता है, इनपुट को वैध करता है, और सर्वर में संग्रहीत API कुंजी का उपयोग करके Perplexity Search API को कॉल करता है।
  • प्रोसेसिंग और स्ट्रक्चरिंग: सर्वर संबंधित वेब स्रोतों और स्निपेट्स की सूची प्राप्त करता है, last_updated और date फ़ील्ड के आधार पर उन्हें नवीनता के अनुसार सॉर्ट करता है, शीर्ष तीन का चयन करता है, और एक्सटेंशन के लिए एक साफ JSON रिस्पॉन्स लौटाता है।
  • कुंजी सुरक्षा: यह बैकएंड चलाने से आपकी API कुंजी ब्राउज़र से छिपी रहती है और आपको अतिरिक्त लॉजिक—जैसे रैंकिंग, फ़िल्टरिंग, या कैशिंग—जोड़ने की अनुमति मिलती है, ताकि डेटा उपयोगकर्ता तक पहुँचने से पहले ही परिष्कृत हो।
  • विस्तार योग्य डिज़ाइन: यह तरीका एक्सटेंशन को अधिक सुरक्षित और भविष्य के सुधारों—जैसे उन्नत स्कोरिंग, मल्टी-सोर्स मर्जिंग, या एनालिटिक्स—के लिए आसानी से विस्तार योग्य बनाता है।

अब हमारा सर्वर तैयार है। आइए Chrome एक्सटेंशन बनाते हैं।

स्टेप 3: Chrome एक्सटेंशन बनाना

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

हर फ़ाइल में जाने से पहले, ये कोर कॉम्पोनेंट क्या करते हैं, उसका एक सारांश:

  • background.js: यह स्क्रिप्ट एक सर्विस वर्कर के रूप में चलती है और यूज़र एक्शन सुनना, साइड पैनल खोलना, और सत्यापन के लिए क्लेम को सुरक्षित रूप से बैकएंड तक भेजना जैसे बैकग्राउंड टास्क मैनेज करती है।
  • content.js: यह कंटेंट स्क्रिप्ट सक्रिय वेबपेज के अंदर चलती है। यह दिखाई देने वाले आर्टिकल टेक्स्ट को निकालती है, संभावित तथ्यात्मक क्लेम पहचानती है, और उन्हें डायनेमिक रूप से हाइलाइट करती है ताकि उपयोगकर्ता देख सकें कि क्या जाँचा जा रहा है।
  • sidepanel.js: यह इंटरैक्टिव साइड पैनल UI को नियंत्रित करता है जो एक्सट्रैक्ट किए गए क्लेम और उनके सत्यापन स्टेटस दिखाता है। यह रियल-टाइम में परिणाम अपडेट करता है और उन्हें पेज पर हाइलाइट्स से जोड़ता है।
  • popup.js: यह स्क्रिप्ट तब दिखने वाले क्विक-एक्सेस पॉपअप को संचालित करती है जब उपयोगकर्ता एक्सटेंशन आइकन पर क्लिक करता है। यह साइड पैनल खोले बिना क्लेम चेक करने का हल्का तरीका प्रदान करती है।

Chrome एक्सटेंशन के कुछ अतिरिक्त हेल्पर फ़ाइलें आप GitHub रिपॉजिटरी में पा सकते हैं।

स्टेप 3.1: बैकग्राउंड स्क्रिप्ट और इवेंट हैंडलिंग 

हमारे Chrome एक्सटेंशन सेटअप के आधार पर, background.js फ़ाइल वह केंद्रीय नियंत्रक है जो एक्सटेंशन को उत्तरदायी रखता है। यह उपयोगकर्ता कार्रवाइयों (जैसे एक्सटेंशन आइकन पर क्लिक) को सुनता है, आवश्यकता होने पर साइड पैनल खोलता है, और क्लेम को सत्यापन के लिए बैकएंड तक अग्रेषित करता है।

const SERVER_URL = 'http://localhost:3000';
chrome.action.onClicked.addListener(async (tab) => {
  try {
    await chrome.sidePanel.open({ tabId: tab.id });
  } catch (error) {
    console.error('Error opening side panel:', error);
    chrome.action.setPopup({ popup: 'popup.html' });
  }
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'checkClaim') {
    checkClaim(request.claim)
      .then(result => sendResponse({ success: true, result }))
      .catch(error => sendResponse({ success: false, error: error.message }));
    return true; 
  }
});
async function checkClaim(claim) {
  try {
    const response = await fetch(${SERVER_URL}/check-claim, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ claim })
    });
    if (!response.ok) {
      throw new Error(Server error: ${response.status});
    }
    return await response.json();
  } catch (error) {
    console.error('Error checking claim:', error);
    throw error;
  }
}

ऊपर दिया गया कोड निम्न बातों को शामिल करके एक्सटेंशन की उत्तरदायित्व पर केंद्रित है:

  • केंद्रीय इवेंट हब: बैकग्राउंड स्क्रिप्ट दो मुख्य लिस्नर्स रजिस्टर करती है—chrome.action.onClicked और chrome.runtime.onMessage। पहला तब प्रतिक्रिया देता है जब उपयोगकर्ता एक्सटेंशन आइकन पर क्लिक करता है और साइड पैनल खोलने की कोशिश करता है। दूसरा कंटेंट स्क्रिप्ट या UI कॉम्पोनेंट्स से आने वाले checkClaim जैसे कस्टम संदेशों को सुनता है।
  • सुरक्षित API हैंडलिंग: checkClaim() फ़ंक्शन fetch API का उपयोग करके चयनित क्लेम को बैकएंड पर http://localhost:3000/check-claim पर भेजता है; क्योंकि API कुंजी सर्वर-साइड पर संग्रहीत है, ब्राउज़र इसे कभी उजागर नहीं करता, जिससे संवेदनशील क्रेडेंशियल्स सुरक्षित रहते हैं।
  • त्रुटि सहनशीलता: दोनों इवेंट लिस्नर्स में रनटाइम त्रुटियों को संभालने के लिए try/catch ब्लॉक्स शामिल हैं। यदि साइड पैनल नहीं खुल सकता तो स्क्रिप्ट पॉपअप सेट करने पर वापस आ जाती है। जब API कॉल विफल होते हैं, तो यह लॉग करता है और UI को संरचित त्रुटि संदेश लौटाता है ताकि उपयोगकर्ता को बिना सूचना के विफलता का सामना न करना पड़े।

जब बैकग्राउंड स्क्रिप्ट संचार और सुरक्षित API कॉल्स संभाल रही है, अगला कदम सीधे वेबपेज के साथ इंटरैक्ट करना है।

स्टेप 3.2: क्लेम निकालना और हाइलाइट करना

content.js फ़ाइल सीधे वेबपेज के अंदर चलती है और आर्टिकल टेक्स्ट निकालने, संभावित तथ्यात्मक क्लेम की पहचान करने, और उन्हें उपयोगकर्ता के लिए दृश्य रूप से हाइलाइट करने की ज़िम्मेदार है। यह पेज कंटेंट और एक्सटेंशन की बैकग्राउंड लॉजिक के बीच सेतु का काम करती है—प्रासंगिक टेक्स्ट कैप्चर करके और यूज़र इंटरैक्शन सक्षम करके।

function extractArticleText() {
    const selectors = [
      'article',
      '[role="article"]',
      '.article-content',
      '.post-content',
      '.entry-content',
      'main'
    ];  
    for (const selector of selectors) {
      const element = document.querySelector(selector);
      if (element) {
        return element.innerText;
      }
    }  
    return document.body.innerText;
  }  
  function extractClaims(text) {
    const sentenceMatches = text.match(/[^.!?]*[.!?]+/g) || [];
    const sentences = sentenceMatches
      .map(s => s.trim())
      .filter(s => s.length > 20 && s.length < 1000);
    const claimKeywords = [
      /\d+%/, 
      /\d+\s+(million|billion|thousand|trillion)/i, 
      /according to/i,
      /study shows/i,
      /research found/i,
      /scientists/i,
      /experts/i,
      /will|would|could|should/i,
      /increase|decrease|rise|fall/i,
      /more than|less than|at least/i,
      /proven|showed|demonstrated/i,
      /investment|invest|create|generate/i,  
      /jobs|employment|economy/i,  
      /ripple effect|impact|effect/i  
    ];  
    const claims = [];    
    for (const sentence of sentences) {
      const hasClaimKeyword = claimKeywords.some(regex => regex.test(sentence));      
      const isValidLength = sentence.length > 30 && sentence.length < 800;
      const notNavigation = !/(click here|read more|subscribe|sign up|menu|home|cookie|privacy)/i.test(sentence);
      const hasCompleteThought = /[.!?]$/.test(sentence) || sentence.length > 50;      
      if (hasClaimKeyword && isValidLength && notNavigation && hasCompleteThought) {
        let cleanSentence = sentence
          .replace(/\s+/g, ' ')  
          .replace(/^\s*[-•]\s*/, '') 
          .trim();        
        if (cleanSentence.length > 30) {
          claims.push(cleanSentence);
        }
      }
    }  
    return claims.slice(0, 10);
  }  
let highlightedElements = new Map();
let claimIdCounter = 0;
function highlightTextOnPage(claim, claimId) {
  const text = document.body.innerText;
  const claimWords = claim.toLowerCase().split(/\s+/).filter(word => word.length > 3);  
  if (claimWords.length < 3) return null;
  const sentences = text.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 20);
  let bestMatch = null;
  let bestScore = 0;  
  for (const sentence of sentences) {
    const sentenceWords = sentence.toLowerCase().split(/\s+/);
    let score = 0;    
    for (const word of claimWords) {
      if (sentenceWords.some(sw => sw.includes(word) || word.includes(sw))) {
        score++;
      }
    }    
    if (score > bestScore && score >= Math.min(3, claimWords.length * 0.5)) {
      bestScore = score;
      bestMatch = sentence;
    }
  }  
  if (!bestMatch) return null;  
  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT,
    null,
    false
  );  
  let node;
  while (node = walker.nextNode()) {
    if (node.textContent.includes(bestMatch)) {
      const parent = node.parentElement;
      if (parent && !parent.classList.contains('claim-highlight')) {
        const highlight = document.createElement('span');
        highlight.className = 'claim-highlight';
        highlight.id = claim-${claimId};
        highlight.style.cssText = 
          background: linear-gradient(120deg, #fbbf24 0%, #f59e0b 100%);
          
          border-radius: 3px;
          box-shadow: 0 1px 3px rgba(0,0,0,0.2);
          cursor: pointer;
          transition: all 0.2s ease;
          position: relative;
        ;        
        const textContent = node.textContent;
        const index = textContent.indexOf(bestMatch);
        if (index !== -1) {
          const before = textContent.substring(0, index);
          const after = textContent.substring(index + bestMatch.length);         
          const beforeNode = document.createTextNode(before);
          const afterNode = document.createTextNode(after);          
          highlight.textContent = bestMatch;
          node.parentNode.insertBefore(beforeNode, node);
          node.parentNode.insertBefore(highlight, node);
          node.parentNode.insertBefore(afterNode, node);
          node.parentNode.removeChild(node);
          highlight.addEventListener('mouseenter', () => {
            highlight.style.transform = 'scale(1.05)';
            highlight.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
          });    
          highlight.addEventListener('mouseleave', () => {
            highlight.style.transform = 'scale(1)';
            highlight.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
          });   
          highlight.addEventListener('click', () => {
            chrome.runtime.sendMessage({
              action: 'highlightClaimInPanel',
              claimId: claimId
            });        
            highlight.style.background = 'linear-gradient(120deg, #10b981 0%, #059669 100%)';
            setTimeout(() => {
              highlight.style.background = 'linear-gradient(120deg, #fbbf24 0%, #f59e0b 100%)';
            }, 1000);
          });     
          highlightedElements.set(claimId, highlight);
          return claimId;
        }
      }
      break;
    }
  }  
  return null;
}
function removeHighlights() {
  highlightedElements.forEach((element, claimId) => {
    if (element && element.parentNode) {
      const parent = element.parentNode;
      const text = element.textContent;
      const textNode = document.createTextNode(text);
      parent.insertBefore(textNode, element);
      parent.removeChild(element);
    }
  });
  highlightedElements.clear();
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'extractClaims') {
    const text = extractArticleText();
    const claims = extractClaims(text);
    sendResponse({ claims, url: window.location.href });
  } else if (request.action === 'highlightClaim') {
    const { claim, claimId } = request;
    const highlightId = highlightTextOnPage(claim, claimId);
    sendResponse({ success: true, highlightId });
  } else if (request.action === 'removeHighlights') {
    removeHighlights();
    sendResponse({ success: true });
  }
  return true;
});

आइए content.js स्क्रिप्ट के प्रमुख हिस्सों को समझें—यह कैसे सार्थक टेक्स्ट निकालती है, संभावित क्लेम का पता लगाती है, और हाइलाइटेड कंटेंट को एक्सटेंशन के UI से जोड़ती है।

  • आर्टिकल टेक्स्ट एक्सट्रैक्शन: extractArticleText() फ़ंक्शन सामान्य आर्टिकल कंटेनरों को स्कैन करके मुख्य पठनीय सामग्री निकालता है। इससे एक्सटेंशन नेविगेशन मेनू या विज्ञापनों के बजाय सार्थक टेक्स्ट पर केंद्रित रहता है।
  • क्लेम डिटेक्शन लॉजिक: extractClaims() फ़ंक्शन टेक्स्ट को वाक्यों में तोड़ता है, लंबाई और संरचना के आधार पर फ़िल्टर लगाता है, और regex-आधारित कीवर्ड्स का उपयोग करके ऐसे कथनों का पता लगाता है जो तथ्यात्मक दावे होने की सबसे अधिक संभावना रखते हैं।
  • डायनेमिक हाइलाइटिंग: highlightTextOnPage() फ़ंक्शन DOM में पहचाने गए दावे को ढूंढकर उसे स्टाइल किए हुए <span> में रैप करता है। यह hover और click इंटरेक्शंस जोड़ता है ताकि यूज़र पेज पर दावों को साइड पैनल में उनकी वेरिफिकेशन रिज़ल्ट्स से दृश्य रूप से जोड़ सकें।
  • मैसेज हैंडलिंग: chrome.runtime.onMessage लिस्नर दावों को निकालने, हाइलाइट जोड़ने और आवश्यकता होने पर उन्हें साफ़ करने जैसी क्रियाओं का समन्वय करता है। इससे पेज और एक्सटेंशन सिंक में रहते हैं और यूज़र इंटरेक्शन स्मूद रहता है।

जब कंटेंट स्क्रिप्ट पेज पर टेक्स्ट एक्सट्रैक्शन और क्लेम हाइलाइटिंग संभाल लेती है, तो अगला कदम उन दावों और उनकी वेरिफिकेशन का परिणाम यूज़र को दिखाना है। यहीं पर sidepanel.js इंटरफ़ेस काम आता है।

चरण 3.3: साइड पैनल UI

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

let claimCounter = 0;
let currentHighlights = new Map();
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'highlightClaimInPanel') {
    const claimId = request.claimId;
    const claimElement = document.getElementById(sidepanel-${claimId});    
    if (claimElement) {
      claimElement.scrollIntoView({ 
        behavior: 'smooth', 
        block: 'center' 
      });      
      claimElement.classList.add('highlighted');      
      claimElement.style.animation = 'pulse 1s ease-in-out';     
      setTimeout(() => {
        claimElement.classList.remove('highlighted');
        claimElement.style.animation = '';
      }, 3000);
    }
  }
});
document.getElementById('check-btn').addEventListener('click', async () => {
    const button = document.getElementById('check-btn');
    const container = document.getElementById('claims-container');
    const status = document.getElementById('status');   
    button.disabled = true;
    container.innerHTML = '<div class="loading">Extracting claims...</div>';
    status.textContent = '';    
    try {
      const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
      await chrome.tabs.sendMessage(tab.id, { action: 'removeHighlights' });
    } catch (error) {
    }   
    try {
      const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
      const response = await chrome.tabs.sendMessage(tab.id, { action: 'extractClaims' });    
      if (!response || !response.claims || response.claims.length === 0) {
        container.innerHTML = '<div class="no-claims">No claims found on this page.</div>';
        button.disabled = false;
        return;
      }
      status.textContent = Found ${response.claims.length} claims. Checking...;
      container.innerHTML = '';      
      for (let i = 0; i < response.claims.length; i++) {
        const claim = response.claims[i];
        const claimId = claim-${++claimCounter};
        status.textContent = Checking claim ${i + 1} of ${response.claims.length}...;        
        try {
          await chrome.tabs.sendMessage(tab.id, { 
            action: 'highlightClaim', 
            claim, 
            claimId 
          });
        } catch (error) {
        }      
        const claimDiv = document.createElement('div');
        claimDiv.className = 'claim-item';
        claimDiv.id = sidepanel-${claimId};
        claimDiv.innerHTML = 
          <div class="claim-text">${escapeHtml(claim)}</div>
          <div class="loading">Verifying...</div>
        ;
        container.appendChild(claimDiv);
        chrome.runtime.sendMessage(
          { action: 'checkClaim', claim },
          (checkResponse) => {
            if (checkResponse.success) {
              const result = checkResponse.result;      
              const sourcesHtml = result.sources && result.sources.length > 0 ? 
                <div class="claim-sources">
                  ${result.sources.map(s => 
                    <div class="source-item">
                      <a href="${s.url}" target="_blank" class="source-link">
                        ${s.title || s.domain}
                      </a>
                      <div class="source-domain">
                        ${s.domain}
                        ${s.lastUpdated || s.date ?  • ${new Date(s.lastUpdated || s.date).toLocaleDateString()} : ''}
                      </div>
                      ${s.snippet ? <div class="source-snippet">${escapeHtml(s.snippet.substring(0, 150))}${s.snippet.length > 150 ? '...' : ''}</div> : ''}
                    </div>
                  ).join('')}
                </div> : 
                '<div class="claim-sources">No sources found</div>';             
              claimDiv.innerHTML = 
                <div class="claim-text">${escapeHtml(claim)}</div>
                ${sourcesHtml}
              ;          
              currentHighlights.set(claimId, claimDiv);
            } else {
              claimDiv.innerHTML = 
                <div class="claim-text">${escapeHtml(claim)}</div>
                <div class="error">Error: ${checkResponse.error}</div>
              ;
            }
          }
        );  
        if (i < response.claims.length - 1) {
          await new Promise(resolve => setTimeout(resolve, 500));
        }
      }  
      status.textContent = 'All claims checked! Click highlighted text on the page to jump to the corresponding claim details.';
      button.disabled = false; 
    } catch (error) {
      container.innerHTML = <div class="error">Error: ${error.message}</div>;
      button.disabled = false;
      status.textContent = '';
    }
  });  
  function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }

आपके ब्राउज़र में साइड पैनल इस तरह काम करता है:

  • डायनेमिक क्लेम लिस्टिंग: जब यूज़र “Check Claims” बटन पर क्लिक करता है, तो स्क्रिप्ट सक्रिय टैब से दावे मंगाती है, पुराने हाइलाइट्स साफ़ करती है, और प्लेसहोल्डर स्टेट्स के साथ दावों की सूची बनाती है।
  • रियल-टाइम वेरिफिकेशन अपडेट्स: हर दावा checkClaim एक्शन के साथ chrome.runtime.sendMessage का उपयोग करके बैकग्राउंड स्क्रिप्ट को भेजा जाता है। बैकएंड से परिणाम मिलते ही UI तुरंत सोर्सेज, डोमेन्स और स्निपेट्स के साथ अपडेट हो जाता है।
  • इंटरैक्टिव लिंकिंग: स्क्रिप्ट highlightClaimInPanel मैसेजेज़ सुनती है, ताकि पेज पर हाइलाइट किए गए टेक्स्ट पर क्लिक करने से साइड पैनल में संबंधित क्लेम डिटेल्स तक स्क्रॉल किया जा सके। इससे पेज हाइलाइट्स और वेरिफिकेशन डेटा के बीच स्मूद नेविगेशन लूप बनता है।

अगले चरण में, हम एक क्विक-एक्सेस पॉप-अप जोड़ेंगे ताकि पिछली स्टेप्स में बनी एक्सटेंशन को लॉन्च और उपयोग करना आसान हो जाए।

चरण 3.4: क्विक एक्सेस पॉपअप UI

popup.js एक हल्का पॉप-अप इंटरफ़ेस है जो यूज़र के एक्सटेंशन आइकन पर क्लिक करने पर दिखाई देता है। साइड पैनल के विपरीत, यह पॉप-अप बिना मौजूदा पेज छोड़े या पूरा इंटरैक्टिव पैनल खोले, ऑन-डिमांड तेज़ी से दावों की जांच करने का तरीका प्रदान करता है।

document.getElementById('check-btn').addEventListener('click', async () => {
    const button = document.getElementById('check-btn');
    const container = document.getElementById('claims-container');
    const status = document.getElementById('status');   
    button.disabled = true;
    container.innerHTML = '<div class="loading">Extracting claims...</div>';
    status.textContent = '';  
    try {
      const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });     
      const response = await chrome.tabs.sendMessage(tab.id, { action: 'extractClaims' });  
      if (!response || !response.claims || response.claims.length === 0) {
        container.innerHTML = '<div class="error">No claims found on this page.</div>';
        button.disabled = false;
        return;
      }   
      status.textContent = Found ${response.claims.length} claims. Checking...;
      container.innerHTML = '';
      for (let i = 0; i < response.claims.length; i++) {
        const claim = response.claims[i];
        status.textContent = Checking claim ${i + 1} of ${response.claims.length}...;   
        const claimDiv = document.createElement('div');
        claimDiv.className = 'claim-item';
        claimDiv.innerHTML = 
          <div class="claim-text">${escapeHtml(claim)}</div>
          <div class="loading">Verifying...</div>
        ;
        container.appendChild(claimDiv);      
        chrome.runtime.sendMessage(
          { action: 'checkClaim', claim },
          (checkResponse) => {
            if (checkResponse.success) {
              const result = checkResponse.result;
              const confidenceBar = result.confidence ? 
                <div class="confidence-bar">
                  <div class="confidence-fill" style="width: ${result.confidence}%"></div>
                  <span class="confidence-text">${result.confidence}% confidence</span>
                </div> : '';     
              const explanation = result.explanation ? 
                <div class="claim-explanation">${escapeHtml(result.explanation)}</div> : '';    
              const rebuttal = result.rebuttal ? 
                <div class="claim-rebuttal">
                  <strong>Counter-argument:</strong> ${escapeHtml(result.rebuttal)}
                </div> : '';  
              const sourcesHtml = result.sources && result.sources.length > 0 ? 
                <div class="claim-sources">
                  <strong>Sources (${result.sources.length}):</strong>
                  ${result.sources.map(s => 
                    <div class="source-item">
                      <a href="${s.url}" target="_blank" class="source-link">
                        ${s.domain || s.title}
                      </a>
                      ${s.snippet ? <div class="source-snippet">${escapeHtml(s.snippet)}</div> : ''}
                    </div>
                  ).join('')}
                </div> : 
                '<div class="claim-sources">No sources found</div>'; 
              claimDiv.innerHTML = 
                <div class="claim-text">${escapeHtml(claim)}</div>
                <div class="claim-verdict verdict-${result.verdict.toLowerCase()}">
                  ${result.verdict}
                </div>
                ${confidenceBar}
                ${explanation}
                ${rebuttal}
                ${sourcesHtml}
              ;
            } else {
              claimDiv.innerHTML = 
                <div class="claim-text">${escapeHtml(claim)}</div>
                <div class="error">Error: ${checkResponse.error}</div>
              ;
            }
          }
        );       
        if (i < response.claims.length - 1) {
          await new Promise(resolve => setTimeout(resolve, 500));
        }
      }  
      status.textContent = 'All claims checked!';
      button.disabled = false;      
    } catch (error) {
      container.innerHTML = <div class="error">Error: ${error.message}</div>;
      button.disabled = false;
      status.textContent = '';
    }
  }); 
  function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
  }

यहाँ बताया गया है कि popup.js स्क्रिप्ट सीधे एक्सटेंशन के टूलबार से ऑन-डिमांड क्लेम चेकर को कैसे पावर देती है।

  • पेज स्कैन और क्लेम डिस्प्ले: जब यूज़र “Check” बटन पर क्लिक करता है, तो पॉप-अप कंटेंट स्क्रिप्ट के ज़रिए सक्रिय टैब से टेक्स्ट मांगता है, संभावित दावों को जल्दी से निकालता है और उन्हें पॉप-अप विंडो में सूचीबद्ध कर देता है।
  • बैकएंड वेरिफिकेशन: हर दावा बैकग्राउंड स्क्रिप्ट को /check-claim API रूट कॉल करने के लिए भेजा जाता है। सर्वर से लौटे वर्डिक्ट्स, कॉन्फिडेंस बार्स, व्याख्याएँ, प्रति-तर्क और सोर्स लिंक्स के साथ पॉप-अप रियल-टाइम में अपडेट होता है।

अब हमारी एक्सटेंशन परीक्षण के लिए तैयार है। 

ध्यान दें: आप इस प्रोजेक्ट की सभी कोड फ़ाइलें GitHub रिपॉज़िटरी में पा सकते हैं।

चरण 4: ऐप चलाना

बैकएंड सर्वर को निम्न कमांड चलाकर शुरू करें:

npm start

सर्वर चलने के बाद, Chrome एक्सटेंशन लोड करें:

  • Chrome खोलें और “Extensions” पर क्लिक करें (सर्च बार के दाईं ओर)।
  • “Developer mode” ऑन करें और “Load unpacked” पर क्लिक करें (ऊपर बाएँ)। 
  • इसके बाद, सभी फ़ाइलों वाला हमारा extensions फ़ोल्डर लोड करें और पुष्टि का इंतज़ार करें।

  • Chrome द्वारा एक्सटेंशन जोड़ने की पुष्टि के बाद, किसी भी वेबपेज पर Claim Checker आइकन पर क्लिक करें, वह टेक्स्ट चुनें जिसे आप सत्यापित करना चाहते हैं, और क्लेम चेक चलाएँ।

आइए परिणाम देखें:

निष्कर्ष

अब आपके पास पूरी तरह Perplexity के Search API द्वारा संचालित एक तेज़ और किफायती क्लेम चेकर है (किसी LLM कॉल की आवश्यकता नहीं)। यह डेमो API की प्रमुख खूबियों को उजागर करता है, जैसे ताज़ा और विश्वसनीय सर्च परिणाम, स्निपेट-स्तरीय प्रासंगिकता, कम विलंबता, और पूर्वानुमेय मूल्य निर्धारण।

यहाँ से, आप उन्नत फ़िल्टर्स जोड़कर, स्वचालित जाँच शेड्यूल करके, या इसे RAG पाइपलाइनों के लिए प्री-फ़िल्टर के रूप में एकीकृत करके प्रोजेक्ट का विस्तार कर सकते हैं। और आगे बढ़ने के लिए, Perplexity API डॉक्स और क्विकस्टार्ट गाइड्स देखें ताकि अधिक उन्नत सर्च और फ़िल्टरिंग क्षमताओं को अनलॉक किया जा सके।


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

मैं एमएल (Gen AI) में Google Developers Expert, Kaggle 3x Expert, और Women Techmakers एंबेसडर हूँ, तथा तकनीक क्षेत्र में 3+ वर्षों का अनुभव रखती हूँ। मैंने 2020 में एक हेल्थ-टेक स्टार्टअप की सह-स्थापना की और वर्तमान में Georgia Tech से कंप्यूटर विज्ञान में मास्टर कर रही हूँ, जिसमें मेरा विशेषज्ञता क्षेत्र मशीन लर्निंग है।

विषय
कृत्रिम बुद्धिमत्ता
एआई एजेंट्स

Top DataCamp Courses

कोर्स

AI Agents परिचय

1 घंटा 30 मिनट
143.7K
AI एजेंटों की मूल बातें, उनके घटक और वास्तविक उपयोग सीखें—कोडिंग की जरूरत नहीं।
विवरण देखेंRight Arrow
पाठ्यक्रम शुरू करें
और देखेंRight Arrow