افزونه هوش مصنوعی کروم؛ معرفی کاربردها و آموزش ساخت با API درواره

در این راهنما با کاربردهای افزونه‌های هوش مصنوعی کروم آشنا می‌شوید و یک افزونه واقعی می‌سازید که متن انتخاب‌شده یا محتوای صفحه را خلاصه، ترجمه، بازنویسی و تحلیل می‌کند. پروژه شامل کد کامل Manifest V3، JavaScript، رابط فارسی و Backend متصل به API درواره است.

Share
افزونه هوش مصنوعی کروم؛ معرفی کاربردها و آموزش ساخت با API درواره

افزونه هوش مصنوعی کروم می‌تواند قابلیت‌هایی مانند خلاصه‌سازی مقاله، ترجمه متن، بازنویسی محتوا، توضیح اصطلاحات، استخراج اطلاعات و پاسخ‌گویی درباره صفحه فعلی را مستقیماً به مرورگر اضافه کند.

کاربر به‌جای کپی‌کردن متن سایت و انتقال آن به یک ابزار جداگانه، بخشی از صفحه را انتخاب می‌کند و نتیجه را داخل همان افزونه دریافت می‌کند.

در این آموزش یک افزونه هوش مصنوعی Chrome می‌سازیم که می‌تواند:

  • متن انتخاب‌شده در صفحه را دریافت کند.
  • در صورت انتخاب‌نشدن متن، محتوای اصلی صفحه را بخواند.
  • مقاله یا صفحه وب را خلاصه کند.
  • متن را به فارسی ترجمه کند.
  • یک پاراگراف را بازنویسی کند.
  • مفهوم متن را به زبان ساده توضیح دهد.
  • سؤال کاربر را درباره صفحه پاسخ دهد.
  • برای تولید نتیجه به یکی از مدل‌های API درواره متصل شود.

این پروژه با JavaScript ساده، Chrome Extensions Manifest V3، FastAPI و API سازگار با OpenAI درواره ساخته می‌شود.

افزونه هوش مصنوعی کروم چیست؟

Chrome Extension برنامه کوچکی است که به مرورگر Google Chrome یا مرورگرهای مبتنی بر Chromium قابلیت جدید اضافه می‌کند.

افزونه می‌تواند یک Popup، منوی کلیک راست، پنل کناری، میان‌بر صفحه‌کلید یا رابطی داخل صفحات وب داشته باشد.

وقتی این افزونه به مدل زبانی متصل شود، می‌تواند محتوای صفحه را پردازش و نتیجه‌ای متناسب با درخواست کاربر تولید کند.

نمونه کاربرد:

کاربر یک مقاله طولانی را باز می‌کند.
        ↓
روی آیکون افزونه کلیک می‌کند.
        ↓
گزینه «خلاصه‌سازی» را انتخاب می‌کند.
        ↓
افزونه متن صفحه را استخراج می‌کند.
        ↓
Backend متن را به API درواره می‌فرستد.
        ↓
خلاصه فارسی داخل Popup نمایش داده می‌شود.

گوگل در مستندات رسمی توضیح می‌دهد که فایل manifest.json تنظیمات و قابلیت‌های اصلی افزونه را تعریف می‌کند و Popup نیز می‌تواند هنگام انتخاب آیکون افزونه نمایش داده شود. آموزش رسمی ساخت اولین افزونه Chrome

افزونه‌های هوش مصنوعی کروم چه کاربردهایی دارند؟

خلاصه‌سازی صفحات وب

یک مقاله طولانی، گزارش، خبر یا صفحه مستندات را می‌توان به نکات اصلی تبدیل کرد.

ترجمه هوشمند متن

کاربر بخشی از صفحه را انتخاب می‌کند و ترجمه‌ای متناسب با مفهوم متن دریافت می‌کند.

بازنویسی محتوا

متن می‌تواند رسمی‌تر، ساده‌تر، کوتاه‌تر یا مناسب شبکه‌های اجتماعی بازنویسی شود.

توضیح اصطلاحات

افزونه می‌تواند یک مفهوم تخصصی، کد، فرمول یا پاراگراف پیچیده را به زبان ساده توضیح دهد.

دستیار مطالعه

از محتوای صفحه می‌توان پرسش، فلش‌کارت، خلاصه آموزشی یا نکات کلیدی ساخت.

دستیار برنامه‌نویسی

توسعه‌دهنده می‌تواند خطاها، مستندات فنی یا قطعه‌کد انتخاب‌شده را برای توضیح یا اصلاح به مدل بفرستد.

استخراج اطلاعات

امکان استخراج نام‌ها، تاریخ‌ها، قیمت‌ها، ویژگی‌های محصول، آدرس‌ها یا داده‌های ساختاریافته وجود دارد.

پاسخ‌گویی درباره صفحه

کاربر می‌تواند درباره مقاله یا سند بازشده سؤال بپرسد و پاسخ مبتنی بر همان محتوا دریافت کند.

تولید پیام و پاسخ

متن یک پیام یا ایمیل انتخاب می‌شود و افزونه پیش‌نویس پاسخ را آماده می‌کند.

تحلیل محصولات فروشگاهی

افزونه می‌تواند ویژگی‌های محصول را خلاصه یا چند کالای بازشده را با یکدیگر مقایسه کند.

انواع رابط کاربری افزونه Chrome

رابطکاربرد
Popupنمایش یک پنجره کوچک با کلیک روی آیکون افزونه
Side Panelپنل بزرگ‌تر در کنار مرورگر
Context Menuاجرای دستور با کلیک راست
Content Scriptافزودن رابط یا قابلیت داخل صفحه
Options Pageصفحه تنظیمات افزونه
Keyboard Shortcutاجرای سریع با میان‌بر
Omniboxدریافت دستور از نوار آدرس

برای نسخه اولیه، Popup ساده‌ترین انتخاب است. در نسخه پیشرفته‌تر می‌توان گفتگو و تاریخچه را به Side Panel منتقل کرد.

نمونه افزونه‌های هوش مصنوعی مرورگر

افزونه‌های موجود معمولاً در یکی از دسته‌های زیر قرار می‌گیرند:

دستهنمونه ابزار
دستیار عمومی صفحاتMonica، Sider، Merlin و MaxAI
نگارش و ویرایشGrammarly و QuillBot
خلاصه‌سازی و یادداشتGlasp
اتوماسیون مرورگرHARPA AI
دستیار مدل محلیPage Assist
اتصال گفتگو به صفحاتChatGPTBox
اسکریپت‌های سفارشیTampermonkey همراه با API اختصاصی

امکانات، مدل‌های قابل استفاده و شرایط دسترسی این ابزارها ممکن است تغییر کند. اگر کنترل روی مدل، هزینه، رابط فارسی یا منطق برنامه اهمیت داشته باشد، ساخت افزونه اختصاصی انتخاب مناسب‌تری است.

چرا افزونه اختصاصی بسازیم؟

افزونه آماده برای نیازهای عمومی مناسب است، اما افزونه اختصاصی مزایای متفاوتی دارد:

  • رابط کاملاً فارسی و راست‌چین
  • اتصال به API درواره
  • انتخاب مدل بر اساس کاربرد
  • تعریف پرامپت اختصاصی
  • هماهنگی با گردش کار شرکت
  • کنترل طول ورودی و خروجی
  • امکان ارائه افزونه به مشتریان
  • ثبت مصرف هر قابلیت
  • اتصال به حساب کاربران سایت
  • پشتیبانی از اصطلاحات و داده‌های تخصصی

برای مثال، یک شرکت می‌تواند افزونه‌ای بسازد که توضیحات محصول را از سایت تأمین‌کننده استخراج و به قالب استاندارد فروشگاه خودش تبدیل کند.

معماری صحیح اتصال افزونه به مدل

قرار دادن کلید اصلی API داخل افزونه Chrome مناسب نیست؛ زیرا فایل‌های افزونه روی دستگاه کاربر قرار می‌گیرند و قابل مشاهده‌اند.

معماری پیشنهادی:

صفحه وب
   ↓
افزونه Chrome
   ↓
Backend برنامه شما
   ↓
کنترل ورودی و دسترسی
   ↓
API درواره
   ↓
مدل انتخاب‌شده
   ↓
Backend
   ↓
نمایش نتیجه در افزونه

آدرس پایه درواره:

https://api.darvareh.ir/v1

افزونه فقط به Backend شما متصل می‌شود و Backend درخواست را به مدل می‌فرستد.

فناوری‌های پروژه

در این آموزش از ابزارهای زیر استفاده می‌کنیم:

بخشفناوری
افزونه مرورگرHTML، CSS و JavaScript
استاندارد افزونهManifest V3
دسترسی به صفحهactiveTab و chrome.scripting
BackendPython و FastAPI
اتصال به مدلOpenAI Python SDK
سرویس مدلAPI درواره
تنظیمات Backendمتغیر محیطی

ساختار پوشه‌ها

دو بخش مستقل خواهیم داشت:

darvareh-ai-chrome-extension/
├── extension/
│   ├── manifest.json
│   ├── popup.html
│   ├── popup.css
│   ├── popup.js
│   └── icons/
│       ├── icon16.png
│       ├── icon48.png
│       └── icon128.png
│
└── backend/
    ├── app.py
    ├── requirements.txt
    └── .env

مرحله اول: ساخت Manifest افزونه

فایل extension/manifest.json:

{
  "manifest_version": 3,
  "name": "دستیار هوش مصنوعی درواره",
  "description": "خلاصه‌سازی، ترجمه و تحلیل صفحات وب با هوش مصنوعی",
  "version": "1.0.0",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "http://localhost:8000/*"
  ],
  "action": {
    "default_title": "دستیار هوش مصنوعی درواره",
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

مقدار manifest_version باید برابر ۳ باشد. مجوز activeTab پس از اقدام مستقیم کاربر، دسترسی موقت به تب فعال ایجاد می‌کند و با رفتن کاربر به سایت دیگری از بین می‌رود. مستندات رسمی activeTab

مجوز scripting نیز برای اجرای تابع استخراج متن در صفحه فعلی استفاده می‌شود. طبق مستندات Chrome، استفاده از chrome.scripting به مجوز scripting و دسترسی موقت یا دائمی به صفحه نیاز دارد. مستندات Scripting API

در محیط عملی باید آدرس Backend خودتان را جایگزین کنید:

"host_permissions": [
  "https://api.example.com/*"
]

مرحله دوم: طراحی Popup افزونه

فایل extension/popup.html:

<!doctype html>
<html lang="fa" dir="rtl">
<head>
    <meta charset="UTF-8">

    <meta
        name="viewport"
        content="width=device-width, initial-scale=1"
    >

    <title>دستیار هوش مصنوعی</title>

    <link
        rel="stylesheet"
        href="popup.css"
    >
</head>

<body>
    <main class="app">
        <header class="header">
            <div>
                <h1>دستیار هوش مصنوعی</h1>
                <p>تحلیل متن صفحه با API درواره</p>
            </div>

            <span class="status-dot"></span>
        </header>

        <section class="content-info">
            <strong id="page-title">
                صفحه فعلی
            </strong>

            <span id="selection-status">
                در حال بررسی متن صفحه...
            </span>
        </section>

        <label for="action">
            چه کاری انجام شود؟
        </label>

        <select id="action">
            <option value="summarize">
                خلاصه‌سازی متن
            </option>

            <option value="explain">
                توضیح به زبان ساده
            </option>

            <option value="translate">
                ترجمه روان به فارسی
            </option>

            <option value="rewrite">
                بازنویسی حرفه‌ای
            </option>

            <option value="question">
                پاسخ به سؤال
            </option>
        </select>

        <label for="instruction">
            دستور تکمیلی
        </label>

        <textarea
            id="instruction"
            placeholder="برای مثال: نکات مهم را به‌صورت فهرست بنویس..."
        ></textarea>

        <button id="run-button">
            اجرا با هوش مصنوعی
        </button>

        <p
            id="message"
            aria-live="polite"
        ></p>

        <section
            id="result-section"
            class="result-section"
            hidden
        >
            <div class="result-header">
                <strong>نتیجه</strong>

                <button
                    id="copy-button"
                    class="copy-button"
                    type="button"
                >
                    کپی
                </button>
            </div>

            <div id="result"></div>
        </section>
    </main>

    <script src="popup.js"></script>
</body>
</html>

در Manifest V3 بهتر است JavaScript داخل فایل جداگانه قرار بگیرد. به همین دلیل کد اجرایی را مستقیماً داخل HTML نمی‌نویسیم.

مرحله سوم: طراحی رابط فارسی

فایل extension/popup.css:

:root {
    color-scheme: light;
    font-family:
        Vazirmatn,
        Tahoma,
        Arial,
        sans-serif;
}

* {
    box-sizing: border-box;
}

body {
    width: 390px;
    min-height: 480px;
    margin: 0;
    background: #f5f6fb;
    color: #182033;
}

.app {
    padding: 18px;
}

.header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 18px;
}

.header h1 {
    margin: 0;
    font-size: 18px;
}

.header p {
    margin: 5px 0 0;
    color: #697188;
    font-size: 12px;
}

.status-dot {
    width: 10px;
    height: 10px;
    margin-top: 5px;
    border-radius: 50%;
    background: #6c4cff;
    box-shadow: 0 0 0 5px rgba(108, 76, 255, 0.12);
}

.content-info {
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-bottom: 16px;
    padding: 12px;
    border: 1px solid #e4e7f0;
    border-radius: 12px;
    background: #ffffff;
}

.content-info strong {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.content-info span {
    color: #72798c;
    font-size: 12px;
}

label {
    display: block;
    margin: 13px 0 6px;
    font-size: 13px;
    font-weight: 700;
}

select,
textarea,
button {
    width: 100%;
    font: inherit;
}

select,
textarea {
    border: 1px solid #d9ddea;
    border-radius: 10px;
    outline: none;
    background: #ffffff;
}

select {
    height: 42px;
    padding: 0 10px;
}

textarea {
    min-height: 82px;
    padding: 10px;
    resize: vertical;
    line-height: 1.7;
}

select:focus,
textarea:focus {
    border-color: #6c4cff;
    box-shadow: 0 0 0 3px rgba(108, 76, 255, 0.1);
}

#run-button {
    margin-top: 14px;
    padding: 12px;
    border: 0;
    border-radius: 10px;
    background: #6c4cff;
    color: #ffffff;
    cursor: pointer;
    font-weight: 700;
}

#run-button:hover {
    background: #5739e8;
}

#run-button:disabled {
    cursor: wait;
    opacity: 0.65;
}

#message {
    min-height: 20px;
    margin: 10px 0 0;
    color: #697188;
    font-size: 12px;
}

.result-section {
    margin-top: 16px;
    padding: 14px;
    border: 1px solid #e1e4ee;
    border-radius: 12px;
    background: #ffffff;
}

.result-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.copy-button {
    width: auto;
    padding: 4px 10px;
    border: 1px solid #d9ddea;
    border-radius: 7px;
    background: #ffffff;
    color: #4d556b;
    cursor: pointer;
    font-size: 12px;
}

#result {
    max-height: 280px;
    overflow-y: auto;
    line-height: 1.9;
    white-space: pre-wrap;
}

مرحله چهارم: استخراج متن صفحه

فایل extension/popup.js:

const BACKEND_URL = "http://localhost:8000";

const pageTitleElement =
    document.getElementById("page-title");

const selectionStatusElement =
    document.getElementById("selection-status");

const actionElement =
    document.getElementById("action");

const instructionElement =
    document.getElementById("instruction");

const runButton =
    document.getElementById("run-button");

const messageElement =
    document.getElementById("message");

const resultSection =
    document.getElementById("result-section");

const resultElement =
    document.getElementById("result");

const copyButton =
    document.getElementById("copy-button");

let currentPageData = null;


/**
 * این تابع داخل صفحه فعال اجرا می‌شود.
 */
function collectPageContent() {
    const selectedText =
        window.getSelection()?.toString().trim() || "";

    const mainElement = document.querySelector(
        "article, main, [role='main']"
    );

    const pageText = (
        selectedText ||
        mainElement?.innerText ||
        document.body?.innerText ||
        ""
    )
        .replace(/\s+/g, " ")
        .trim()
        .slice(0, 15000);

    return {
        title: document.title,
        url: window.location.href,
        text: pageText,
        source:
            selectedText.length > 0
                ? "selection"
                : "page",
    };
}


/**
 * دریافت تب فعال مرورگر
 */
async function getActiveTab() {
    const tabs = await chrome.tabs.query({
        active: true,
        currentWindow: true,
    });

    return tabs[0];
}


/**
 * اجرای تابع جمع‌آوری محتوا در صفحه
 */
async function loadPageContent() {
    try {
        const activeTab = await getActiveTab();

        if (!activeTab?.id) {
            throw new Error("تب فعالی پیدا نشد.");
        }

        const results =
            await chrome.scripting.executeScript({
                target: {
                    tabId: activeTab.id,
                },
                func: collectPageContent,
            });

        currentPageData = results[0]?.result;

        if (!currentPageData?.text) {
            throw new Error(
                "متن قابل استفاده‌ای در صفحه پیدا نشد."
            );
        }

        pageTitleElement.textContent =
            currentPageData.title || "صفحه بدون عنوان";

        if (currentPageData.source === "selection") {
            selectionStatusElement.textContent =
                `${currentPageData.text.length.toLocaleString("fa-IR")} ` +
                "نویسه از متن انتخاب‌شده";
        } else {
            selectionStatusElement.textContent =
                `${currentPageData.text.length.toLocaleString("fa-IR")} ` +
                "نویسه از محتوای صفحه";
        }
    } catch (error) {
        selectionStatusElement.textContent =
            error.message;

        runButton.disabled = true;
    }
}


/**
 * ارسال متن به Backend
 */
async function runAiAction() {
    if (!currentPageData?.text) {
        messageElement.textContent =
            "متنی برای پردازش وجود ندارد.";

        return;
    }

    const action = actionElement.value;
    const instruction = instructionElement.value.trim();

    if (action === "question" && !instruction) {
        messageElement.textContent =
            "سؤال خود را در بخش دستور تکمیلی بنویسید.";

        return;
    }

    runButton.disabled = true;
    resultSection.hidden = true;
    messageElement.textContent =
        "در حال پردازش با هوش مصنوعی...";

    try {
        const response = await fetch(
            `${BACKEND_URL}/api/process`,
            {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    action: action,
                    text: currentPageData.text,
                    instruction: instruction,
                    page_title: currentPageData.title,
                    page_url: currentPageData.url,
                }),
            }
        );

        const data = await response.json();

        if (!response.ok) {
            throw new Error(
                data.detail || "پردازش متن ناموفق بود."
            );
        }

        resultElement.textContent = data.result;
        resultSection.hidden = false;
        messageElement.textContent =
            `پردازش با مدل ${data.model} انجام شد.`;
    } catch (error) {
        messageElement.textContent = error.message;
    } finally {
        runButton.disabled = false;
    }
}


/**
 * کپی‌کردن نتیجه
 */
async function copyResult() {
    const result = resultElement.textContent.trim();

    if (!result) {
        return;
    }

    await navigator.clipboard.writeText(result);

    const previousText = copyButton.textContent;
    copyButton.textContent = "کپی شد";

    setTimeout(() => {
        copyButton.textContent = previousText;
    }, 1500);
}


runButton.addEventListener(
    "click",
    runAiAction
);

copyButton.addEventListener(
    "click",
    copyResult
);

loadPageContent();

تابع chrome.scripting.executeScript() کد استخراج متن را در تب فعال اجرا می‌کند و نتیجه را به Popup برمی‌گرداند.

در این نمونه، اگر کاربر متنی را انتخاب کرده باشد فقط همان متن پردازش می‌شود. در غیر این صورت، برنامه ابتدا دنبال article یا main می‌گردد و در نهایت از متن بدنه صفحه استفاده می‌کند.

چرا طول متن محدود شده است؟

در کد از این محدودیت استفاده کردیم:

.slice(0, 15000)

ارسال تمام محتوای یک صفحه بسیار طولانی می‌تواند هزینه و زمان پاسخ را افزایش دهد. همچنین منوها، فوتر، تبلیغات و قسمت‌های نامرتبط ممکن است وارد درخواست شوند.

برای نسخه حرفه‌ای بهتر است:

  • محتوای اصلی صفحه دقیق‌تر تشخیص داده شود.
  • اسکریپت‌ها و منوها حذف شوند.
  • متن طولانی به چند بخش تقسیم شود.
  • ابتدا بخش‌های مرتبط با سؤال پیدا شوند.
  • تعداد توکن‌ها پیش از ارسال محاسبه شود.

کتابخانه Mozilla Readability نیز برای استخراج محتوای اصلی مقاله قابل بررسی است.

مرحله پنجم: ساخت Backend متصل به درواره

وارد پوشه Backend شوید:

cd backend

فایل requirements.txt:

fastapi
uvicorn[standard]
openai
python-dotenv

نصب کتابخانه‌ها:

python -m venv .venv

در Linux یا macOS:

source .venv/bin/activate

در Windows:

.venv\Scripts\activate

سپس:

pip install -r requirements.txt

تنظیم کلید و مدل درواره

فایل backend/.env:

DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_MODEL=YOUR_MODEL_ID
DARVAREH_BASE_URL=https://api.darvareh.ir/v1
ALLOWED_ORIGINS=*

برای محیط عملی بهتر است ALLOWED_ORIGINS را به Origin افزونه منتشرشده محدود کنید.

شناسه مدل را از فهرست فعلی مدل‌های درواره بردارید.

کد کامل FastAPI

فایل backend/app.py:

import os
from typing import Literal

from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from openai import OpenAI
from pydantic import BaseModel, Field


load_dotenv()

DARVAREH_API_KEY = os.environ["DARVAREH_API_KEY"]
DARVAREH_MODEL = os.environ["DARVAREH_MODEL"]
DARVAREH_BASE_URL = os.getenv(
    "DARVAREH_BASE_URL",
    "https://api.darvareh.ir/v1",
)

origins_value = os.getenv(
    "ALLOWED_ORIGINS",
    "*",
)

allowed_origins = [
    origin.strip()
    for origin in origins_value.split(",")
    if origin.strip()
]

app = FastAPI(
    title="Darvareh AI Chrome Extension API",
    version="1.0.0",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=allowed_origins,
    allow_credentials=False,
    allow_methods=["POST", "GET"],
    allow_headers=["Content-Type"],
)

client = OpenAI(
    api_key=DARVAREH_API_KEY,
    base_url=DARVAREH_BASE_URL,
)


class ProcessRequest(BaseModel):
    action: Literal[
        "summarize",
        "explain",
        "translate",
        "rewrite",
        "question",
    ]

    text: str = Field(
        min_length=2,
        max_length=15000,
    )

    instruction: str = Field(
        default="",
        max_length=2000,
    )

    page_title: str = Field(
        default="",
        max_length=500,
    )

    page_url: str = Field(
        default="",
        max_length=2000,
    )


class ProcessResponse(BaseModel):
    result: str
    model: str


ACTION_PROMPTS = {
    "summarize": (
        "متن را به زبان فارسی خلاصه کن. "
        "ابتدا یک خلاصه کوتاه و سپس نکات اصلی را "
        "به‌صورت فهرست ارائه بده."
    ),
    "explain": (
        "مفهوم متن را به زبان فارسی ساده و دقیق توضیح بده. "
        "اصطلاحات تخصصی مهم را نیز تعریف کن."
    ),
    "translate": (
        "متن را به فارسی روان و حرفه‌ای ترجمه کن. "
        "نام محصولات، کدها و اصطلاحات تخصصی ضروری را "
        "با دقت حفظ کن."
    ),
    "rewrite": (
        "متن را به فارسی روان، حرفه‌ای و خوانا بازنویسی کن. "
        "معنای اصلی، نام‌ها و اعداد را تغییر نده."
    ),
    "question": (
        "فقط با استفاده از متن صفحه به سؤال کاربر پاسخ بده. "
        "اگر پاسخ در متن وجود ندارد، شفاف اعلام کن."
    ),
}


@app.get("/health")
def health_check():
    return {
        "status": "ok",
        "model": DARVAREH_MODEL,
    }


@app.post(
    "/api/process",
    response_model=ProcessResponse,
)
def process_page(request: ProcessRequest):
    base_instruction = ACTION_PROMPTS[request.action]

    additional_instruction = (
        request.instruction.strip()
        if request.instruction.strip()
        else "دستور تکمیلی وجود ندارد."
    )

    user_prompt = f"""
عنوان صفحه:
{request.page_title}

دستور اصلی:
{base_instruction}

دستور تکمیلی کاربر:
{additional_instruction}

متن صفحه:
{request.text}

قواعد:
- نتیجه را مستقیم و بدون مقدمه غیرضروری بنویس.
- اطلاعاتی را که در متن وجود ندارد اختراع نکن.
- نام‌ها، اعداد، تاریخ‌ها و کدها را دقیق حفظ کن.
- اگر متن برای اجرای درخواست کافی نیست، اعلام کن.
"""

    try:
        completion = client.chat.completions.create(
            model=DARVAREH_MODEL,
            messages=[
                {
                    "role": "system",
                    "content": (
                        "شما دستیار فارسی تحلیل و بازنویسی "
                        "محتوای صفحات وب هستید."
                    ),
                },
                {
                    "role": "user",
                    "content": user_prompt,
                },
            ],
        )
    except Exception as error:
        raise HTTPException(
            status_code=502,
            detail=f"خطا در ارتباط با مدل: {error}",
        ) from error

    content = completion.choices[0].message.content

    if not content:
        raise HTTPException(
            status_code=502,
            detail="مدل پاسخ متنی معتبری برنگرداند.",
        )

    return ProcessResponse(
        result=content.strip(),
        model=DARVAREH_MODEL,
    )

اجرای Backend

در پوشه backend اجرا کنید:

uvicorn app:app --reload

آدرس Backend:

http://localhost:8000

بررسی وضعیت:

http://localhost:8000/health

مستندات خودکار:

http://localhost:8000/docs

نصب افزونه در Chrome

برای آزمایش محلی:

  1. آدرس chrome://extensions را باز کنید.
  2. گزینه Developer mode را فعال کنید.
  3. روی Load unpacked کلیک کنید.
  4. پوشه extension را انتخاب کنید.
  5. افزونه را در نوار ابزار Pin کنید.
  6. یک صفحه وب معمولی باز کنید.
  7. روی آیکون افزونه بزنید.
  8. عملیات موردنظر را انتخاب و اجرا کنید.

طبق راهنمای رسمی Chrome، پس از تغییر manifest.json باید افزونه Reload شود. تغییر بعضی فایل‌های دیگر نیز ممکن است به بستن و بازکردن Popup یا بارگذاری مجدد صفحه نیاز داشته باشد. راهنمای Load unpacked

آزمایش افزونه با مثال واقعی

یک مقاله انگلیسی را باز و پاراگرافی از آن را انتخاب کنید.

در افزونه:

عملیات: ترجمه روان به فارسی
دستور تکمیلی: اصطلاحات تخصصی هوش مصنوعی را دقیق حفظ کن.

خروجی باید فقط بر اساس متن انتخاب‌شده تولید شود.

برای خلاصه‌سازی صفحه:

عملیات: خلاصه‌سازی متن
دستور تکمیلی: خلاصه را در پنج نکته کوتاه ارائه بده.

برای پرسش از صفحه:

عملیات: پاسخ به سؤال
دستور تکمیلی: مهم‌ترین نتیجه‌ای که نویسنده مطرح کرده چیست؟

افزودن منوی کلیک راست

می‌توان تجربه کاربری را ساده‌تر کرد تا کاربر پس از انتخاب متن، با کلیک راست عملیات هوش مصنوعی را اجرا کند.

ابتدا مجوز زیر را به manifest.json اضافه کنید:

"permissions": [
  "activeTab",
  "scripting",
  "contextMenus"
]

یک Service Worker تعریف کنید:

"background": {
  "service_worker": "service-worker.js"
}

فایل service-worker.js:

chrome.runtime.onInstalled.addListener(() => {
    chrome.contextMenus.create({
        id: "darvareh-explain",
        title: "توضیح متن با هوش مصنوعی",
        contexts: ["selection"],
    });
});

chrome.contextMenus.onClicked.addListener(
    async (info, tab) => {
        if (
            info.menuItemId !== "darvareh-explain" ||
            !info.selectionText ||
            !tab?.id
        ) {
            return;
        }

        const response = await fetch(
            "http://localhost:8000/api/process",
            {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    action: "explain",
                    text: info.selectionText,
                    instruction: "",
                    page_title: tab.title || "",
                    page_url: tab.url || "",
                }),
            }
        );

        const data = await response.json();

        await chrome.storage.local.set({
            lastAiResult:
                data.result || data.detail || "خطای نامشخص",
        });

        await chrome.action.openPopup();
    }
);

برای استفاده از chrome.storage.local نیز باید مجوز storage اضافه شود:

"permissions": [
  "activeTab",
  "scripting",
  "contextMenus",
  "storage"
]

Popup می‌تواند هنگام بازشدن، مقدار lastAiResult را خوانده و نمایش دهد.

ساخت Side Panel برای گفت‌وگوی طولانی

Popup پس از کلیک بیرون از آن بسته می‌شود. برای گفت‌وگوی چندمرحله‌ای یا نمایش پاسخ طولانی، Side Panel انتخاب مناسب‌تری است.

نمونه تنظیم Manifest:

"permissions": [
  "activeTab",
  "scripting",
  "sidePanel"
],
"side_panel": {
  "default_path": "sidepanel.html"
}

در Side Panel می‌توان امکانات زیر را افزود:

  • تاریخچه پرسش‌ها
  • انتخاب مدل
  • انتخاب لحن
  • ذخیره Prompt
  • نمایش متن منبع
  • گفت‌وگوی چندمرحله‌ای
  • خروجی Markdown
  • دانلود نتیجه
  • مقایسه دو پاسخ

ساخت افزونه با React یا TypeScript

برای افزونه کوچک، JavaScript ساده کافی است. در پروژه بزرگ‌تر می‌توان از ابزارهای زیر استفاده کرد:

ابزارکاربرد
TypeScriptکنترل نوع و کاهش خطا
Reactساخت رابط تعاملی
ViteBuild سریع پروژه
CRXJSاتصال Vite به Chrome Extension
WXTچارچوب توسعه افزونه مرورگر
Plasmoساخت Extension با React و TypeScript
Tailwind CSSطراحی رابط
Zodاعتبارسنجی پاسخ Backend
Vitestتست توابع JavaScript
Playwrightتست End-to-End

ساختار پیشنهادی TypeScript:

src/
├── popup/
│   ├── App.tsx
│   └── main.tsx
├── background/
│   └── index.ts
├── content/
│   └── index.ts
├── shared/
│   ├── api.ts
│   ├── schemas.ts
│   └── types.ts
└── manifest.json

اتصال افزونه به حساب کاربران

اگر افزونه قرار است به مشتریان ارائه شود، استفاده از یک کلید مشترک Backend کافی نیست.

ساختار بهتر:

  1. کاربر در وب‌سایت شما حساب ایجاد می‌کند.
  2. افزونه کاربر را به صفحه ورود هدایت می‌کند.
  3. Backend یک Session یا توکن مخصوص کاربر صادر می‌کند.
  4. افزونه درخواست را با همان شناسه ارسال می‌کند.
  5. Backend سقف مصرف کاربر را بررسی می‌کند.
  6. درخواست به درواره ارسال می‌شود.
  7. مصرف در حساب همان کاربر ثبت می‌شود.

در این معماری نباید کلید اصلی درواره در اختیار افزونه یا کاربر نهایی قرار بگیرد.

تعریف چند مدل برای چند کاربرد

ممکن است یک مدل سریع برای ترجمه و یک مدل قوی‌تر برای تحلیل صفحات مناسب باشد.

نمونه تنظیم Backend:

MODEL_ROUTES = {
    "summarize": os.environ["FAST_MODEL"],
    "translate": os.environ["FAST_MODEL"],
    "rewrite": os.environ["WRITING_MODEL"],
    "explain": os.environ["REASONING_MODEL"],
    "question": os.environ["REASONING_MODEL"],
}

سپس:

selected_model = MODEL_ROUTES[request.action]

و در درخواست:

completion = client.chat.completions.create(
    model=selected_model,
    messages=messages,
)

این ساختار امکان بهینه‌سازی هزینه، سرعت و کیفیت را فراهم می‌کند.

مدیریت صفحات بسیار طولانی

ارسال فقط ۱۵ هزار نویسه برای نمونه اولیه قابل قبول است، اما ممکن است پاسخ سؤال در انتهای صفحه قرار داشته باشد.

برای صفحات طولانی می‌توان از معماری RAG استفاده کرد:

استخراج متن صفحه
    ↓
تقسیم به Chunk
    ↓
ساخت Embedding
    ↓
دریافت سؤال
    ↓
بازیابی Chunkهای مرتبط
    ↓
ارسال متن مرتبط به مدل

اگر هدف فقط خلاصه‌سازی باشد، می‌توان هر بخش را جداگانه خلاصه و سپس خلاصه‌های میانی را ترکیب کرد.

برای یادگیری معماری بازیابی، مقاله RAG چیست و چگونه کار می‌کند؟ را مطالعه کنید.

خروجی JSON برای استخراج اطلاعات

اگر افزونه قرار است اطلاعات صفحه محصول یا آگهی را استخراج کند، خروجی ساختاریافته مناسب‌تر است.

نمونه:

{
  "title": "نام محصول",
  "brand": "برند",
  "price": 2500000,
  "currency": "IRR",
  "features": [
    "ویژگی اول",
    "ویژگی دوم"
  ]
}

در Backend باید پاسخ را با Pydantic اعتبارسنجی کرد و در صورت ناقص‌بودن نتیجه، عملیات مشخصی انجام داد.

اضافه‌کردن Streaming

برای پاسخ‌های طولانی، نمایش تدریجی متن تجربه بهتری ایجاد می‌کند.

دو روش رایج:

  • Server-Sent Events یا SSE
  • WebSocket

در این حالت Backend پاسخ Stream مدل را دریافت کرده و قسمت‌های جدید را به افزونه می‌فرستد.

برای Popupهای کوتاه، پاسخ معمولی ساده‌تر است. Side Panel برای Streaming و گفتگو مناسب‌تر خواهد بود.

مدیریت هزینه افزونه هوش مصنوعی

هزینه به تعداد کاربران، اندازه صفحات، طول پاسخ و مدل انتخاب‌شده بستگی دارد.

برای مدیریت مصرف:

  • طول متن صفحه را محدود کنید.
  • فقط متن انتخاب‌شده را در اولویت قرار دهید.
  • محتوای تکراری را دوباره پردازش نکنید.
  • نتیجه‌های پرتکرار را Cache کنید.
  • برای هر کاربر سقف مصرف تعیین کنید.
  • خروجی را متناسب با کاربرد محدود کنید.
  • مدل مناسب هر وظیفه را انتخاب کنید.
  • درخواست‌ها را بر اساس قابلیت ثبت کنید.
  • متن منو، فوتر و تبلیغات را حذف کنید.

برای محاسبه دقیق‌تر می‌توانید مقاله هزینه API هوش مصنوعی چگونه محاسبه می‌شود؟ را مطالعه کنید.

خطاهای رایج در ساخت افزونه هوش مصنوعی Chrome

قرار دادن API Key در popup.js

فایل‌های افزونه روی دستگاه کاربر قرار می‌گیرند. اتصال به درواره باید از Backend انجام شود.

درخواست مجوزهای بیش از نیاز

اگر پردازش فقط پس از کلیک کاربر انجام می‌شود، activeTab معمولاً از درخواست دسترسی دائمی به همه سایت‌ها مناسب‌تر است.

استخراج تمام متن document.body

این روش می‌تواند منوها، فوتر و بخش‌های نامرتبط را نیز دریافت کند. ابتدا article و main را بررسی کنید.

نادیده‌گرفتن محدودیت صفحات مرورگر

اجرای اسکریپت روی بعضی صفحات داخلی مانند chrome://، صفحه تنظیمات مرورگر یا صفحات خاص Chrome Web Store ممکن نیست.

وابستگی به یک مدل ثابت

شناسه مدل را از متغیر محیطی یا تنظیمات Backend بخوانید.

نمایش خطای فنی به کاربر

خطاهای Backend باید به پیام‌های کوتاه و قابل‌فهم تبدیل شوند.

نبود حالت Loading

پاسخ مدل ممکن است چند ثانیه طول بکشد. دکمه را موقتاً غیرفعال و وضعیت پردازش را نمایش دهید.

تولید HTML و قراردادن مستقیم در صفحه

اگر فقط متن نیاز دارید، نتیجه را با textContent نمایش دهید. برای HTML باید خروجی قبل از نمایش پالایش شود.

ارسال دوباره صفحه بدون تغییر

می‌توان از ترکیب URL، متن و نوع عملیات یک شناسه ساخت و نتیجه را در Cache نگه داشت.

آزمایش افزونه

تست رابط کاربری

موارد زیر را بررسی کنید:

  • نمایش درست فارسی و RTL
  • متن‌های کوتاه و طولانی
  • حالت Loading
  • خطای Backend
  • کپی نتیجه
  • بسته‌شدن و بازشدن Popup
  • صفحات بدون article
  • متن انتخاب‌شده
  • صفحات غیرقابل دسترسی

تست Backend

برای آزمایش مسیر پردازش:

curl -X POST http://localhost:8000/api/process \
  -H "Content-Type: application/json" \
  -d '{
    "action": "summarize",
    "text": "این یک متن آزمایشی برای خلاصه‌سازی است.",
    "instruction": "خلاصه را در یک جمله بنویس.",
    "page_title": "صفحه آزمایشی",
    "page_url": "https://example.com"
  }'

نمونه خروجی:

{
  "result": "این متن نمونه‌ای برای آزمایش قابلیت خلاصه‌سازی است.",
  "model": "YOUR_MODEL_ID"
}

تست پرامپت

مجموعه‌ای ثابت از صفحات و متن‌ها آماده کنید و کیفیت چند مدل را روی آن‌ها بسنجید.

معیارها:

  • کیفیت فارسی
  • حفظ معنای متن
  • دقت ترجمه
  • پیروی از دستور
  • سرعت پاسخ
  • هزینه هر درخواست
  • ثبات خروجی

انتشار در Chrome Web Store

برای انتشار عمومی:

  1. نام و توضیحات افزونه را نهایی کنید.
  2. آیکون‌های استاندارد آماده کنید.
  3. آدرس localhost را با Backend واقعی جایگزین کنید.
  4. نسخه Production افزونه را آزمایش کنید.
  5. مجوزهای Manifest را بازبینی کنید.
  6. فایل‌های غیرضروری را حذف کنید.
  7. پوشه افزونه را ZIP کنید.
  8. حساب توسعه‌دهنده Chrome Web Store ایجاد کنید.
  9. فایل ZIP، تصاویر و توضیحات را بارگذاری کنید.
  10. افزونه را برای بررسی ارسال کنید.

در توضیحات فروشگاه باید کاربرد هر مجوز به‌صورت واضح بیان شود.

ایده‌های محصول مبتنی بر افزونه هوش مصنوعی

افزونه خلاصه‌سازی فارسی

صفحات طولانی را به خلاصه فارسی و نکات کلیدی تبدیل می‌کند.

افزونه دستیار فروش

اطلاعات محصولات سایت‌های مختلف را استخراج و به قالب فروشگاه تبدیل می‌کند.

افزونه تولید پاسخ پشتیبانی

متن پیام مشتری را انتخاب و پاسخ پیشنهادی آماده می‌کند.

افزونه تحلیل آگهی

مشخصات آگهی را استخراج و با معیارهای کاربر مقایسه می‌کند.

افزونه دستیار برنامه‌نویسی

خطا، کد یا مستندات انتخاب‌شده را توضیح می‌دهد.

افزونه مطالعه مقاله

خلاصه، سؤال، فلش‌کارت و واژه‌های مهم را از مقاله تولید می‌کند.

افزونه تولید محتوای شبکه اجتماعی

بخشی از یک صفحه را به پست لینکدین، کپشن یا متن کوتاه تبدیل می‌کند.

افزونه ترجمه تخصصی

ترجمه را بر اساس واژه‌نامه اختصاصی سازمان انجام می‌دهد.

افزونه دستیار CRM

اطلاعات صفحه شرکت یا مشتری را خلاصه و به سامانه CRM منتقل می‌کند.

چک‌لیست نسخه عملی

  • از Manifest V3 استفاده شده است.
  • کلید API فقط در Backend قرار دارد.
  • شناسه مدل از تنظیمات خوانده می‌شود.
  • مجوزهای افزونه محدود و مشخص‌اند.
  • متن صفحه پیش از ارسال پاک‌سازی می‌شود.
  • طول ورودی محدود شده است.
  • درخواست‌های نامعتبر رد می‌شوند.
  • خطاها در رابط کاربری نمایش داده می‌شوند.
  • حالت Loading وجود دارد.
  • مصرف هر کاربر قابل ثبت است.
  • امکان تغییر مدل وجود دارد.
  • پاسخ‌های تکراری قابل Cache هستند.
  • افزونه روی صفحات مختلف آزمایش شده است.
  • رابط فارسی به‌صورت RTL نمایش داده می‌شود.
  • Backend با آدرس HTTPS عملیاتی اجرا می‌شود.

پرسش‌های متداول

افزونه هوش مصنوعی کروم چیست؟

برنامه‌ای است که قابلیت‌هایی مانند خلاصه‌سازی، ترجمه، بازنویسی و پرسش از محتوای صفحات وب را به مرورگر Chrome اضافه می‌کند.

آیا می‌توان بدون برنامه‌نویسی افزونه Chrome ساخت؟

ابزارهایی برای ساخت اولیه افزونه وجود دارند، اما اتصال حرفه‌ای به API، مدیریت کاربران و پردازش صفحات معمولاً به JavaScript و Backend نیاز دارد.

آیا می‌توان افزونه را به API درواره متصل کرد؟

بله. بهتر است افزونه به Backend برنامه شما وصل شود و Backend درخواست را با آدرس پایه https://api.darvareh.ir/v1 به درواره ارسال کند.

چرا نباید کلید API را داخل افزونه قرار داد؟

زیرا فایل‌های افزونه روی دستگاه کاربران قرار دارند و کلید تعبیه‌شده در آن‌ها قابل استخراج است.

Manifest V3 چیست؟

نسخه فعلی معماری افزونه‌های Chrome است که ساختار Manifest، Service Worker، مجوزها و نحوه اجرای افزونه را مشخص می‌کند.

activeTab چه کاربردی دارد؟

این مجوز پس از اقدام مستقیم کاربر، دسترسی موقت به تب فعلی می‌دهد. در پروژه این مقاله برای استخراج متن صفحه از آن استفاده کردیم.

آیا افزونه روی همه سایت‌ها کار می‌کند؟

روی بیشتر صفحات عادی وب قابل استفاده است، اما دسترسی به صفحات داخلی مرورگر و بعضی صفحات محافظت‌شده محدود است.

آیا افزونه ساخته‌شده روی Edge هم اجرا می‌شود؟

مرورگرهای مبتنی بر Chromium معمولاً از بخش بزرگی از استاندارد Chrome Extensions پشتیبانی می‌کنند؛ بااین‌حال باید افزونه روی هر مرورگر جداگانه آزمایش شود.

آیا می‌توان نتیجه را داخل صفحه نمایش داد؟

بله. Content Script می‌تواند یک پنجره شناور یا پنل داخل صفحه ایجاد کند. در این حالت باید استایل‌ها و تداخل با سایت میزبان مدیریت شوند.

چگونه مدل مناسب را انتخاب کنیم؟

چند مدل را با مجموعه ثابتی از صفحات فارسی و انگلیسی مقایسه و کیفیت، سرعت، هزینه و پیروی از دستور را ارزیابی کنید.

آیا می‌توان از مدل متفاوت برای هر قابلیت استفاده کرد؟

بله. Backend می‌تواند بر اساس نوع عملیات، مدل مناسب ترجمه، خلاصه‌سازی یا تحلیل را انتخاب کند.

جمع‌بندی

برای ساخت افزونه هوش مصنوعی کروم باید سه بخش اصلی ایجاد شود:

  1. رابط افزونه با HTML و CSS
  2. استخراج متن صفحه با Chrome Scripting API
  3. Backend متصل به مدل هوش مصنوعی

در پروژه این مقاله، افزونه متن انتخاب‌شده یا محتوای اصلی صفحه را دریافت می‌کند و برای خلاصه‌سازی، ترجمه، توضیح، بازنویسی یا پاسخ‌گویی به Backend می‌فرستد. Backend نیز از API سازگار با OpenAI درواره برای دسترسی به مدل انتخاب‌شده استفاده می‌کند.

این معماری امکان ساخت افزونه‌های فارسی برای مطالعه، تولید محتوا، برنامه‌نویسی، فروش، پشتیبانی و تحلیل صفحات وب را فراهم می‌کند.

برای دریافت کلید API، انتخاب مدل و مشاهده جزئیات اتصال، به مستندات API درواره مراجعه کنید.

مقالات مرتبط

منابع

این مقاله صرفاً با هدف آموزش و اطلاع‌رسانی تهیه شده است. پیش از استفاده عملی، مستندات رسمی سرویس‌ها و صفحه سلب مسئولیت را مطالعه کنید.

Read more