ساخت یک سیستم RAG واقعی با LangChain، LlamaIndex و API درواره؛ آموزش گام‌به‌گام از صفر تا استقرار

آموزش گام‌به‌گام ساخت یک سیستم RAG واقعی با LangChain، LlamaIndex و API درواره؛ از اتصال داده‌ها و ساخت Vector Index تا ایجاد Agent، API و استقرار با Docker.

Share
ساخت یک سیستم RAG واقعی با LangChain، LlamaIndex و API درواره؛ آموزش گام‌به‌گام از صفر تا استقرار
Darvareh API + LlamaIndex LangChain

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

برای مثال، اگر از یک مدل هوش مصنوعی بپرسید:

«شرایط قرارداد خدمات شرکت ما چیست؟»

یا:

«در مستندات داخلی ما، فرایند دریافت API Key چگونه توضیح داده شده است؟»

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

اینجاست که RAG وارد می‌شود.

RAG مخفف Retrieval-Augmented Generation است؛ یعنی قبل از اینکه مدل پاسخ تولید کند، ابتدا اطلاعات مرتبط از داده‌های شما بازیابی می‌شود و سپس مدل بر اساس همان اطلاعات پاسخ می‌دهد.

در این مقاله، یک سیستم RAG واقعی می‌سازیم که بتواند:

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

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

  • LlamaIndex برای مدیریت اسناد، ایندکس، بازیابی اطلاعات و Query Engine
  • LangChain برای ساخت لایه Agent، Prompt و توسعه‌پذیری بیشتر

همچنین برای اتصال به مدل‌های هوش مصنوعی از API درواره استفاده می‌کنیم؛ چون API درواره با استاندارد OpenAI-Compatible سازگار است و می‌تواند در ابزارهایی که از Base URL، API Key و مدل‌های OpenAI-compatible پشتیبانی می‌کنند استفاده شود. LangChain در مستندات رسمی خود برای ChatOpenAI از پکیج langchain-openai استفاده می‌کند و قابلیت کار با APIهای سازگار با OpenAI را نیز پشتیبانی می‌کند. LlamaIndex نیز برای OpenAI و OpenAI-like Providerها امکان تنظیم api_key و api_base را ارائه می‌دهد.

معماری نهایی سیستم RAG

قبل از شروع کدنویسی، بهتر است معماری پروژه را ببینیم.

در این پروژه، جریان کلی سیستم به این شکل خواهد بود:

کاربر
  ↓
سؤال
  ↓
Backend
  ↓
LlamaIndex Query Engine
  ↓
Retriever
  ↓
Vector Index
  ↓
اسناد مرتبط
  ↓
مدل هوش مصنوعی از طریق API درواره
  ↓
پاسخ نهایی

در مرحله بعد، اگر بخواهیم سیستم را هوشمندتر کنیم، LangChain را به معماری اضافه می‌کنیم:

کاربر
  ↓
LangChain Agent
  ↓
تصمیم‌گیری
  ↓
LlamaIndex Retriever
  ↓
اسناد مرتبط
  ↓
API درواره
  ↓
مدل هوش مصنوعی
  ↓
پاسخ نهایی

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

پیش‌نیازهای پروژه

برای اجرای این پروژه به موارد زیر نیاز دارید:

  • Python نسخه 3.10 یا بالاتر
  • یک پوشه شامل فایل‌های متنی یا Markdown
  • یک API Key از درواره
  • آدرس Base URL درواره
  • کتابخانه‌های LlamaIndex و LangChain

اطلاعات اتصال به API درواره:

Base URL:
https://api.darvareh.ir/v1

Authorization:
Bearer YOUR_DARVAREH_API_KEY

نمونه مدل:

openai/gpt-5.5

نکته مهم: نام دقیق مدل‌ها را بهتر است از پنل یا مستندات مدل‌های درواره بررسی کنید و همان مقدار را در کد قرار دهید.

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

ابتدا یک پوشه برای پروژه ایجاد کنید:

mkdir darvareh-rag-demo
cd darvareh-rag-demo

سپس یک محیط مجازی بسازید:

python -m venv .venv

فعال‌سازی در macOS و Linux:

source .venv/bin/activate

فعال‌سازی در Windows:

.venv\Scripts\activate

حالا کتابخانه‌های مورد نیاز را نصب کنید:

pip install llama-index llama-index-llms-openai-like langchain langchain-openai python-dotenv

در این پروژه از llama-index-llms-openai-like استفاده می‌کنیم تا بتوانیم به یک API سازگار با OpenAI متصل شویم. در مستندات LlamaIndex، OpenAILike برای Providerهایی معرفی شده که API سازگار با OpenAI ارائه می‌کنند و امکان تنظیم api_base دارند.

ساخت فایل env

برای اینکه API Key را مستقیم داخل کد ننویسیم، یک فایل .env ایجاد می‌کنیم:

touch .env

داخل فایل .env مقدارهای زیر را قرار دهید:

DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_BASE_URL=https://api.darvareh.ir/v1
DARVAREH_MODEL=openai/gpt-5.5

نکته امنیتی: فایل .env را در GitHub یا مخزن عمومی قرار ندهید.

آماده‌سازی داده‌ها

حالا یک پوشه برای اسناد ایجاد می‌کنیم:

mkdir data

داخل پوشه data چند فایل متنی یا Markdown قرار دهید.

مثلاً فایل زیر را ایجاد کنید:

touch data/api-guide.md

نمونه محتوای فایل:

# راهنمای API درواره

درواره یک API سازگار با OpenAI ارائه می‌دهد.

Base URL:
https://api.darvareh.ir/v1

برای ارسال درخواست باید از Header زیر استفاده کنید:

Authorization: Bearer YOUR_API_KEY

مسیر Chat Completions:

POST /chat/completions

نمونه کاربرد:
اتصال ابزارهایی مانند Open WebUI، LangChain، LlamaIndex، Cline، Roo Code و OpenCode به مدل‌های هوش مصنوعی.

در پروژه واقعی، این پوشه می‌تواند شامل موارد زیر باشد:

  • مستندات محصول
  • FAQ
  • فایل‌های Markdown
  • متن قراردادها
  • مستندات API
  • راهنمای داخلی سازمان
  • خروجی تبدیل‌شده از PDFها

ساخت اولین RAG با LlamaIndex و API درواره

حالا فایل اصلی پروژه را ایجاد می‌کنیم:

touch app.py

کد زیر را داخل app.py قرار دهید:

import os
from dotenv import load_dotenv

from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai_like import OpenAILike

load_dotenv()

DARVAREH_API_KEY = os.getenv("DARVAREH_API_KEY")
DARVAREH_BASE_URL = os.getenv("DARVAREH_BASE_URL")
DARVAREH_MODEL = os.getenv("DARVAREH_MODEL")

if not DARVAREH_API_KEY:
    raise ValueError("DARVAREH_API_KEY is missing in .env file")

llm = OpenAILike(
    model=DARVAREH_MODEL,
    api_key=DARVAREH_API_KEY,
    api_base=DARVAREH_BASE_URL,
    is_chat_model=True,
)

Settings.llm = llm

documents = SimpleDirectoryReader("data").load_data()

index = VectorStoreIndex.from_documents(documents)

query_engine = index.as_query_engine()

response = query_engine.query(
    "Base URL درواره چیست و چگونه باید از API Key استفاده کنم؟"
)

print(response)

حالا پروژه را اجرا کنید:

python app.py

اگر همه چیز درست تنظیم شده باشد، سیستم باید بر اساس فایل‌های داخل پوشه data پاسخ دهد.

در این کد چه اتفاقی افتاد؟

ابتدا اطلاعات اتصال به درواره را از فایل .env خواندیم.

سپس یک مدل OpenAI-Compatible تعریف کردیم:

llm = OpenAILike(
    model=DARVAREH_MODEL,
    api_key=DARVAREH_API_KEY,
    api_base=DARVAREH_BASE_URL,
    is_chat_model=True,
)

بعد این مدل را به‌عنوان LLM پیش‌فرض LlamaIndex تنظیم کردیم:

Settings.llm = llm

سپس اسناد را از پوشه data خواندیم:

documents = SimpleDirectoryReader("data").load_data()

بعد از آن یک VectorStoreIndex ساختیم:

index = VectorStoreIndex.from_documents(documents)

در LlamaIndex، VectorStoreIndex یکی از رایج‌ترین Indexها برای برنامه‌های RAG است. طبق مستندات رسمی LlamaIndex، این Index اسناد را به Node تبدیل می‌کند، برای متن هر Node embedding می‌سازد و آن‌ها را برای Query آماده می‌کند.

در نهایت Query Engine را ساختیم:

query_engine = index.as_query_engine()

Query Engine در LlamaIndex رابطی است که سؤال طبیعی کاربر را دریافت می‌کند، اسناد مرتبط را از Index بازیابی می‌کند و پاسخ نهایی را تولید می‌کند.

مشکل مهم: Embedding را هم باید دقیق تنظیم کنیم

تا اینجا LLM را از طریق API درواره تنظیم کردیم. اما در سیستم RAG، فقط مدل پاسخ‌گو مهم نیست؛ مدل Embedding هم بسیار مهم است.

Embedding وظیفه دارد متن‌ها را به بردارهای عددی تبدیل کند تا بتوانیم شباهت معنایی را پیدا کنیم.

اگر Embedding درست تنظیم نشود، Retriever ممکن است اسناد نامرتبط را پیدا کند و پاسخ نهایی ضعیف شود.

در نسخه Production، باید برای Embedding هم یک مدل مشخص انتخاب کنید.

اگر API درواره در مستندات شما endpoint و مدل Embedding سازگار با OpenAI ارائه می‌دهد، می‌توان آن را نیز مشابه LLM تنظیم کرد. در غیر این صورت، می‌توانید برای شروع از Embedding محلی یا یکی از Embedding Providerهای پشتیبانی‌شده توسط LlamaIndex استفاده کنید.

در نسخه ساده آموزشی، LlamaIndex ممکن است از تنظیمات پیش‌فرض استفاده کند؛ اما برای محصول واقعی، پیشنهاد می‌شود Embedding را به‌صورت explicit تنظیم کنید.

نسخه بهتر با تنظیم جداگانه Embedding

اگر مدل Embedding در API Guide درواره فعال باشد، ساختار کلی چنین خواهد بود:

import os
from dotenv import load_dotenv

from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.openai import OpenAIEmbedding

load_dotenv()

DARVAREH_API_KEY = os.getenv("DARVAREH_API_KEY")
DARVAREH_BASE_URL = os.getenv("DARVAREH_BASE_URL")
DARVAREH_MODEL = os.getenv("DARVAREH_MODEL")
DARVAREH_EMBEDDING_MODEL = os.getenv("DARVAREH_EMBEDDING_MODEL", "text-embedding-3-small")

llm = OpenAILike(
    model=DARVAREH_MODEL,
    api_key=DARVAREH_API_KEY,
    api_base=DARVAREH_BASE_URL,
    is_chat_model=True,
)

embed_model = OpenAIEmbedding(
    model=DARVAREH_EMBEDDING_MODEL,
    api_key=DARVAREH_API_KEY,
    api_base=DARVAREH_BASE_URL,
)

Settings.llm = llm
Settings.embed_model = embed_model

documents = SimpleDirectoryReader("data").load_data()

index = VectorStoreIndex.from_documents(documents)

query_engine = index.as_query_engine(
    similarity_top_k=3
)

response = query_engine.query(
    "API درواره با چه Base URL استفاده می‌شود؟"
)

print(response)

نکته مهم برای انتشار در وبلاگ درواره: اگر هنوز در API Guide درواره بخش Embeddings عمومی نشده یا مدل Embedding مشخصی معرفی نشده، این بخش را با عبارت «در صورت فعال بودن مدل‌های Embedding در حساب شما» منتشر کنید تا مقاله دقیق و منطبق با وضعیت واقعی API بماند.

استفاده از LangChain در کنار LlamaIndex

تا اینجا سیستم RAG را با LlamaIndex ساختیم. اما اگر بخواهیم سیستم ما فقط سؤال و جواب ساده نباشد و بتواند تصمیم‌گیری کند، می‌توانیم LangChain را به معماری اضافه کنیم.

LangChain برای ساخت Agent، Prompt Template، Tool و Workflow مناسب است.

در این معماری:

LlamaIndex = مدیریت داده، Index و Retrieval

LangChain = منطق Agent، Prompt و ابزارها

API درواره = اتصال به مدل‌های هوش مصنوعی

به زبان ساده:

  • LlamaIndex می‌داند کدام بخش از اسناد مرتبط است.
  • LangChain می‌تواند تصمیم بگیرد چه زمانی از LlamaIndex استفاده کند.
  • درواره مدل هوش مصنوعی مورد نیاز را در اختیار سیستم قرار می‌دهد.

ساخت Retriever Tool با LlamaIndex برای استفاده در LangChain

در بخش قبل، یک سیستم RAG ساده با LlamaIndex ساختیم. حالا می‌خواهیم آن را به یک Tool تبدیل کنیم تا بتوانیم از آن داخل LangChain استفاده کنیم.

ایده این است:

کاربر سؤال می‌پرسد

↓

LangChain تصمیم می‌گیرد آیا نیاز به جستجو در اسناد دارد یا نه

↓

اگر نیاز بود، Tool مربوط به LlamaIndex اجرا می‌شود

↓

اسناد مرتبط پیدا می‌شوند

↓

مدل از طریق API درواره پاسخ نهایی را تولید می‌کند

برای شروع، یک فایل جدید ایجاد می‌کنیم:

touch rag_tool.py

داخل این فایل کد زیر را قرار دهید:

import os
from dotenv import load_dotenv

from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings
from llama_index.llms.openai_like import OpenAILike

load_dotenv()

DARVAREH_API_KEY = os.getenv("DARVAREH_API_KEY")
DARVAREH_BASE_URL = os.getenv("DARVAREH_BASE_URL")
DARVAREH_MODEL = os.getenv("DARVAREH_MODEL")

llm = OpenAILike(
    model=DARVAREH_MODEL,
    api_key=DARVAREH_API_KEY,
    api_base=DARVAREH_BASE_URL,
    is_chat_model=True,
)

Settings.llm = llm

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)

query_engine = index.as_query_engine(
    similarity_top_k=3
)


def search_documents(question: str) -> str:
    """
    جستجو در اسناد داخلی و تولید پاسخ بر اساس داده‌های موجود.
    """
    response = query_engine.query(question)
    return str(response)

این فایل یک تابع به نام search_documents دارد که سؤال را دریافت می‌کند و از LlamaIndex برای پیدا کردن پاسخ استفاده می‌کند.

تبدیل Retriever به Tool در LangChain

حالا یک فایل جدید برای Agent ایجاد می‌کنیم:

touch agent.py

ابتدا کتابخانه‌های مورد نیاز را وارد می‌کنیم:

import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate

from rag_tool import search_documents

load_dotenv()

سپس مدل را با API درواره تنظیم می‌کنیم:

llm = ChatOpenAI(
    model=os.getenv("DARVAREH_MODEL"),
    api_key=os.getenv("DARVAREH_API_KEY"),
    base_url=os.getenv("DARVAREH_BASE_URL"),
)

در نسخه‌های جدید langchain-openai، پارامتر base_url برای تنظیم API Base استفاده می‌شود. اگر در نسخۀ نصب‌شده شما خطا داد، می‌توانید از openai_api_base استفاده کنید:

llm = ChatOpenAI(
    model=os.getenv("DARVAREH_MODEL"),
    openai_api_key=os.getenv("DARVAREH_API_KEY"),
    openai_api_base=os.getenv("DARVAREH_BASE_URL"),
)

حالا Tool را تعریف می‌کنیم:

@tool
def company_docs_search(question: str) -> str:
    """
    برای پاسخ‌گویی به سؤال‌هایی که مربوط به اسناد داخلی، راهنمای API،
    مستندات محصول یا اطلاعات شرکت هستند، از این ابزار استفاده کن.
    """
    return search_documents(question)

اکنون Prompt مربوط به Agent را می‌سازیم:

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """
            شما یک دستیار هوش مصنوعی دقیق هستید.
            اگر سؤال کاربر مربوط به اسناد داخلی، مستندات API یا اطلاعات موجود در فایل‌ها بود،
            حتماً از ابزار company_docs_search استفاده کن.
            اگر پاسخ در اسناد موجود نبود، صادقانه بگو که اطلاعات کافی در اسناد پیدا نشد.
            """
        ),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ]
)

حالا Agent را می‌سازیم:

tools = [company_docs_search]

agent = create_tool_calling_agent(
    llm=llm,
    tools=tools,
    prompt=prompt,
)

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
)

و در نهایت سؤال را اجرا می‌کنیم:

response = agent_executor.invoke(
    {
        "input": "Base URL API درواره چیست و چطور باید API Key را ارسال کنم؟"
    }
)

print(response["output"])

نسخه کامل فایل agent.py:

import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate

from rag_tool import search_documents

load_dotenv()

llm = ChatOpenAI(
    model=os.getenv("DARVAREH_MODEL"),
    api_key=os.getenv("DARVAREH_API_KEY"),
    base_url=os.getenv("DARVAREH_BASE_URL"),
)

@tool
def company_docs_search(question: str) -> str:
    """
    برای پاسخ‌گویی به سؤال‌هایی که مربوط به اسناد داخلی، راهنمای API،
    مستندات محصول یا اطلاعات شرکت هستند، از این ابزار استفاده کن.
    """
    return search_documents(question)

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """
            شما یک دستیار هوش مصنوعی دقیق هستید.
            اگر سؤال کاربر مربوط به اسناد داخلی، مستندات API یا اطلاعات موجود در فایل‌ها بود،
            حتماً از ابزار company_docs_search استفاده کن.
            اگر پاسخ در اسناد موجود نبود، صادقانه بگو که اطلاعات کافی در اسناد پیدا نشد.
            """
        ),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ]
)

tools = [company_docs_search]

agent = create_tool_calling_agent(
    llm=llm,
    tools=tools,
    prompt=prompt,
)

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
)

response = agent_executor.invoke(
    {
        "input": "Base URL API درواره چیست و چطور باید API Key را ارسال کنم؟"
    }
)

print(response["output"])

برای اجرا:

python agent.py

چرا LangChain را به LlamaIndex اضافه کردیم؟

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

اما وقتی می‌خواهید سیستم شما هوشمندتر شود، LangChain مفید می‌شود.

برای مثال، یک Agent می‌تواند تصمیم بگیرد:

  • آیا سؤال نیاز به جستجو در اسناد دارد؟
  • آیا باید از ابزار دیگری استفاده کند؟
  • آیا پاسخ باید از API داخلی شرکت گرفته شود؟
  • آیا اطلاعات کافی در اسناد وجود دارد یا نه؟

به همین دلیل، ترکیب LangChain و LlamaIndex برای پروژه‌های واقعی بسیار کاربردی است.

ساخت API برای سیستم RAG با FastAPI

تا اینجا سیستم را از طریق فایل Python اجرا کردیم. اما برای استفاده واقعی، بهتر است آن را به یک API تبدیل کنیم تا بتوانیم از Frontend، وب‌سایت، پنل سازمانی یا چت‌بات به آن درخواست بفرستیم.

ابتدا FastAPI و Uvicorn را نصب کنید:

pip install fastapi uvicorn

یک فایل جدید بسازید:

touch server.py

کد زیر را داخل server.py قرار دهید:

from fastapi import FastAPI
from pydantic import BaseModel

from rag_tool import search_documents

app = FastAPI(
    title="Darvareh RAG Demo",
    version="1.0.0"
)

class QueryRequest(BaseModel):
    question: str

class QueryResponse(BaseModel):
    answer: str

@app.get("/")
def health_check():
    return {
        "status": "ok",
        "message": "Darvareh RAG API is running"
    }

@app.post("/query", response_model=QueryResponse)
def query_docs(request: QueryRequest):
    answer = search_documents(request.question)
    return QueryResponse(answer=answer)

حالا سرور را اجرا کنید:

uvicorn server:app --reload --host 0.0.0.0 --port 8000

اکنون API شما روی آدرس زیر فعال است:

http://localhost:8000

برای تست:

curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Base URL درواره چیست؟"
  }'

نمونه خروجی:

{
  "answer": "Base URL درواره https://api.darvareh.ir/v1 است و برای احراز هویت باید API Key را در Header Authorization به‌صورت Bearer ارسال کنید."
}

افزودن Endpoint مبتنی بر Agent

اگر بخواهید به‌جای Query ساده، از Agent استفاده کنید، می‌توانید یک فایل جداگانه به نام agent_runtime.py بسازید.

touch agent_runtime.py

کد:

import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate

from rag_tool import search_documents

load_dotenv()

llm = ChatOpenAI(
    model=os.getenv("DARVAREH_MODEL"),
    api_key=os.getenv("DARVAREH_API_KEY"),
    base_url=os.getenv("DARVAREH_BASE_URL"),
)

@tool
def company_docs_search(question: str) -> str:
    """
    جستجو در اسناد داخلی، مستندات محصول و راهنمای API.
    """
    return search_documents(question)

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """
            شما یک دستیار دقیق برای پاسخ‌گویی به سؤال‌های کاربران هستید.
            اگر سؤال درباره اسناد داخلی یا API بود، از ابزار company_docs_search استفاده کن.
            اگر اطلاعات کافی در اسناد نبود، حدس نزن.
            """
        ),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ]
)

tools = [company_docs_search]

agent = create_tool_calling_agent(
    llm=llm,
    tools=tools,
    prompt=prompt,
)

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=False,
)

def ask_agent(question: str) -> str:
    response = agent_executor.invoke(
        {
            "input": question
        }
    )
    return response["output"]

حالا server.py را به این شکل تغییر دهید:

from fastapi import FastAPI
from pydantic import BaseModel

from rag_tool import search_documents
from agent_runtime import ask_agent

app = FastAPI(
    title="Darvareh RAG Demo",
    version="1.0.0"
)

class QueryRequest(BaseModel):
    question: str

class QueryResponse(BaseModel):
    answer: str

@app.get("/")
def health_check():
    return {
        "status": "ok",
        "message": "Darvareh RAG API is running"
    }

@app.post("/query", response_model=QueryResponse)
def query_docs(request: QueryRequest):
    answer = search_documents(request.question)
    return QueryResponse(answer=answer)

@app.post("/agent", response_model=QueryResponse)
def query_agent(request: QueryRequest):
    answer = ask_agent(request.question)
    return QueryResponse(answer=answer)

حالا دو مسیر دارید:

/query

برای پرسش‌وپاسخ مستقیم روی اسناد.

/agent

برای پاسخ‌گویی هوشمندتر با LangChain Agent.

استقرار ساده روی سرور

برای استقرار اولیه روی یک سرور لینوکسی، می‌توانید پروژه را با Uvicorn اجرا کنید.

اما برای محیط Production بهتر است از Gunicorn یا Docker استفاده کنید.

ابتدا فایل requirements.txt بسازید:

pip freeze > requirements.txt

نمونه محتوای تقریبی:

fastapi
uvicorn
python-dotenv
llama-index
llama-index-llms-openai-like
langchain
langchain-openai

ساخت Dockerfile

یک فایل به نام Dockerfile بسازید:

touch Dockerfile

محتوا:

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000"]

ساخت Image:

docker build -t darvareh-rag-demo .

اجرای Container:

docker run -d \
  --name darvareh-rag-demo \
  -p 8000:8000 \
  --env-file .env \
  darvareh-rag-demo

اکنون سرویس روی پورت 8000 اجرا می‌شود.

ساخت docker-compose.yml

برای مدیریت راحت‌تر، یک فایل docker-compose.yml بسازید:

services:
  rag-api:
    build: .
    container_name: darvareh-rag-demo
    restart: always
    ports:
      - "8000:8000"
    env_file:
      - .env
    volumes:
      - ./data:/app/data

اجرای سرویس:

docker compose up -d

نکات مهم برای نسخه Production

نسخه‌ای که تا اینجا ساختیم آموزشی است. برای Production باید چند نکته را رعایت کنید.

۱. ذخیره Index

در مثال ساده، هر بار که برنامه اجرا می‌شود، اسناد دوباره خوانده و Index ساخته می‌شود. این روش برای پروژه کوچک قابل قبول است، اما برای پروژه واقعی مناسب نیست.

در Production باید Index را ذخیره کنید و فقط هنگام تغییر اسناد آن را به‌روزرسانی کنید.

۲. استفاده از Vector Database واقعی

برای پروژه‌های کوچک، Index داخلی کافی است.

اما برای پروژه‌های بزرگ بهتر است از Vector Database استفاده کنید:

  • Qdrant
  • Chroma
  • Pinecone
  • Weaviate
  • pgvector

۳. کنترل دسترسی

اگر اسناد حساس دارید، هر کاربر نباید به همه اطلاعات دسترسی داشته باشد.

باید دسترسی بر اساس نقش کاربر، سازمان یا پروژه کنترل شود.

۴. Logging و Monitoring

در سیستم واقعی باید ثبت کنید:

  • چه سؤالی پرسیده شد؟
  • کدام اسناد بازیابی شدند؟
  • کدام مدل استفاده شد؟
  • هزینه تقریبی چقدر بود؟
  • پاسخ چقدر زمان برد؟

۵. مدیریت هزینه

RAG می‌تواند مصرف توکن بالایی داشته باشد.

برای کنترل هزینه:

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

۶. جلوگیری از پاسخ‌های ساختگی

در Prompt سیستم باید تأکید شود که اگر پاسخ در اسناد پیدا نشد، مدل نباید حدس بزند.

مثال:

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

چرا API درواره برای این معماری مناسب است؟

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

برای مثال:

  • تحلیل حقوقی اسناد → مدل قوی‌تر
  • پاسخ‌گویی عمومی → مدل سریع‌تر
  • استفاده پرتعداد کاربران → مدل اقتصادی‌تر
  • تحلیل فارسی → مدلی با عملکرد بهتر در فارسی

اگر برای هر مدل بخواهید Provider جداگانه مدیریت کنید، پروژه پیچیده می‌شود.

با API درواره، یک لایه واحد برای دسترسی به مدل‌های مختلف دارید:

Backend RAG
  ↓
API درواره
  ↓
مدل‌های مختلف هوش مصنوعی

مزایا:

  • یک API Key
  • یک Base URL
  • یک داشبورد مصرف
  • یک کیف پول ریالی
  • امکان تغییر مدل بدون تغییر معماری
  • سازگاری با ابزارهای OpenAI-Compatible

جمع‌بندی این بخش

در این بخش، سیستم RAG ساده را به یک معماری کاربردی‌تر تبدیل کردیم:

  • LlamaIndex را به‌عنوان موتور Retrieval استفاده کردیم.
  • Retriever را به Tool تبدیل کردیم.
  • Tool را وارد LangChain کردیم.
  • یک Agent ساختیم.
  • یک API با FastAPI ایجاد کردیم.
  • پروژه را برای استقرار با Docker آماده کردیم.

ساختار نهایی پروژه

تا اینجا یک سیستم RAG واقعی ساختیم که از LlamaIndex، LangChain، FastAPI و API درواره استفاده می‌کند.

ساختار نهایی پروژه به شکل زیر است:

darvareh-rag-demo/
│
├── data/
│   └── api-guide.md
│
├── .env
├── app.py
├── rag_tool.py
├── agent.py
├── agent_runtime.py
├── server.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml

توضیح فایل‌ها:

data/

محل نگهداری اسناد و فایل‌هایی است که سیستم RAG باید روی آن‌ها پاسخ دهد.

.env

محل نگهداری API Key، Base URL و نام مدل درواره است.

app.py

نسخه ساده سیستم RAG با LlamaIndex.

rag_tool.py

موتور اصلی جستجو در اسناد و پاسخ‌گویی مبتنی بر LlamaIndex.

agent.py

نمونه اجرای LangChain Agent در محیط خط فرمان.

agent_runtime.py

لایه اجرایی Agent برای استفاده در API.

server.py

سرور FastAPI برای ارائه Endpointهای /query و /agent.

Dockerfile

فایل ساخت Docker Image.

docker-compose.yml

فایل اجرای ساده سرویس با Docker Compose.

Best Practices برای ساخت سیستم RAG واقعی

ساخت یک نمونه ساده RAG آسان است، اما ساخت یک سیستم قابل اعتماد برای محصول واقعی نیاز به دقت بیشتری دارد.

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

۱. داده‌های ورودی را تمیز کنید

کیفیت پاسخ RAG به کیفیت داده وابسته است.

اگر اسناد شما:

  • تکراری باشند
  • قدیمی باشند
  • ساختار نامناسب داشته باشند
  • متن استخراج‌شده از PDF خراب باشد
  • تیترها و بخش‌بندی‌ها مشخص نباشند

خروجی سیستم نیز ضعیف خواهد شد.

قبل از Index کردن اسناد، داده‌ها را پاک‌سازی کنید.

مثلاً:

  • حذف صفحات خالی
  • حذف Header و Footer تکراری
  • حذف متن‌های نامرتبط
  • اصلاح Encoding فارسی
  • یکسان‌سازی عنوان‌ها

۲. Chunk Size را درست انتخاب کنید

Chunk Size یعنی هر بخش از متن چقدر بزرگ باشد.

اگر Chunkها خیلی بزرگ باشند، Retriever نمی‌تواند بخش دقیق را پیدا کند.

اگر خیلی کوچک باشند، Context کافی برای پاسخ وجود ندارد.

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

chunk_size: 800 تا 1200 کاراکتر
chunk_overlap: 100 تا 200 کاراکتر

در پروژه‌های فارسی، باید چند حالت مختلف را تست کنید، چون ساختار جمله‌ها و پاراگراف‌ها ممکن است با انگلیسی متفاوت باشد.

۳. Metadata اضافه کنید

Metadata به Retriever کمک می‌کند اسناد مرتبط‌تری پیدا کند.

برای هر سند می‌توانید این اطلاعات را ذخیره کنید:

  • عنوان سند
  • نوع سند
  • تاریخ انتشار
  • نسخه
  • دسته‌بندی
  • نویسنده
  • سطح دسترسی
  • نام سازمان یا پروژه

مثلاً:

{
  "title": "API Guide",
  "category": "developer-docs",
  "version": "1.0",
  "access_level": "public"
}

در پروژه‌های سازمانی، Metadata برای فیلتر کردن دسترسی بسیار مهم است.

۴. از Top K مناسب استفاده کنید

در Query Engine معمولاً مشخص می‌کنیم چند سند مرتبط بازیابی شود.

در مثال ما:

query_engine = index.as_query_engine(
    similarity_top_k=3
)

اگر top_k خیلی کم باشد، ممکن است اطلاعات کافی پیدا نشود.

اگر خیلی زیاد باشد، Context شلوغ می‌شود و هزینه توکن بالا می‌رود.

برای شروع:

similarity_top_k = 3 تا 5

انتخاب مناسبی است.

۵. پاسخ ندادن در نبود داده را جدی بگیرید

یکی از مشکلات رایج سیستم‌های RAG، پاسخ‌های ساختگی است.

مدل ممکن است حتی وقتی سند کافی ندارد، پاسخ تولید کند.

برای کاهش این مشکل، در Prompt سیستم بنویسید:

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

این کار احتمال Hallucination را کاهش می‌دهد.

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

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

مثلاً:

سؤال: Base URL درواره چیست؟
پاسخ مورد انتظار: https://api.darvareh.ir/v1

سؤال: API Key چگونه ارسال می‌شود؟
پاسخ مورد انتظار: در Header Authorization به‌صورت Bearer

با این روش می‌توانید تغییرات در Chunking، مدل، Embedding یا Retriever را ارزیابی کنید.

۷. مدل را بر اساس نوع سؤال انتخاب کنید

همیشه لازم نیست از قدرتمندترین مدل استفاده کنید.

در یک سیستم RAG حرفه‌ای می‌توان چند مدل داشت:

  • مدل سریع برای سؤال‌های ساده
  • مدل قدرتمند برای تحلیل‌های پیچیده
  • مدل اقتصادی برای حجم بالای درخواست
  • مدل مناسب فارسی برای پاسخ‌های فارسی

با API درواره می‌توانید از یک Base URL و یک API Key استفاده کنید و مدل‌ها را بر اساس نیاز تغییر دهید.

خطاهای رایج در ساخت RAG

خطای ۱: پاسخ نامرتبط

دلایل احتمالی:

  • Chunk بندی ضعیف
  • Embedding نامناسب
  • داده‌های نامرتبط
  • تعداد Top K کم یا زیاد
  • سؤال کاربر مبهم

راه‌حل:

  • Chunk Size را تغییر دهید.
  • Metadata اضافه کنید.
  • داده‌ها را پاک‌سازی کنید.
  • سؤال‌های تست بسازید.

خطای ۲: مدل اطلاعاتی خارج از اسناد می‌دهد

دلیل:

Prompt به‌اندازه کافی محدودکننده نیست.

راه‌حل:

در System Prompt تأکید کنید که مدل فقط بر اساس اسناد پاسخ دهد.

خطای ۳: هزینه زیاد API

دلایل:

  • Chunkهای بزرگ
  • Top K زیاد
  • استفاده دائمی از مدل گران
  • نبود Cache

راه‌حل:

  • Top K را کاهش دهید.
  • مدل اقتصادی‌تر انتخاب کنید.
  • پاسخ‌ها را Cache کنید.
  • Chunkها را بهینه کنید.

خطای ۴: کند بودن پاسخ

دلایل:

  • Index در هر اجرا از نو ساخته می‌شود.
  • Vector Database مناسب استفاده نشده.
  • اسناد زیاد و بدون بهینه‌سازی هستند.
  • مدل انتخاب‌شده کند است.

راه‌حل:

  • Index را Persist کنید.
  • از Vector Database واقعی استفاده کنید.
  • مدل سریع‌تر انتخاب کنید.
  • اسناد را دسته‌بندی کنید.

خطای ۵: مشکل اتصال به API

اگر سیستم به API درواره متصل نمی‌شود، این موارد را بررسی کنید:

Base URL:
https://api.darvareh.ir/v1
Authorization:
Bearer YOUR_DARVAREH_API_KEY

همچنین بررسی کنید:

  • API Key فعال باشد.
  • موجودی کیف پول کافی باشد.
  • نام مدل درست وارد شده باشد.
  • درخواست با فرمت OpenAI-Compatible ارسال شود.

نمونه تست مستقیم API درواره با curl

برای اطمینان از اینکه API Key و Base URL درست هستند، قبل از اجرای LangChain یا LlamaIndex می‌توانید یک تست مستقیم بگیرید:

curl https://api.darvareh.ir/v1/chat/completions \
  -H "Authorization: Bearer YOUR_DARVAREH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.5",
    "messages": [
      {
        "role": "user",
        "content": "سلام، خودت را معرفی کن."
      }
    ]
  }'

اگر این درخواست پاسخ داد، یعنی اتصال پایه درست است و مشکل احتمالی در تنظیمات کتابخانه یا کد پروژه است.

نسخه Production پیشنهادی

برای یک محصول واقعی، معماری پیشنهادی بهتر است چنین باشد:

Frontend
  ↓
Backend API
  ↓
Auth / User Access
  ↓
RAG Service
  ↓
Vector Database
  ↓
LlamaIndex Retriever
  ↓
LangChain Agent
  ↓
API درواره
  ↓
Model

اجزای پیشنهادی:

  • FastAPI برای Backend
  • PostgreSQL برای کاربران و لاگ‌ها
  • Qdrant یا pgvector برای Vector Database
  • LlamaIndex برای Retrieval
  • LangChain برای Agent و Toolها
  • API درواره برای اتصال به مدل‌ها
  • Docker برای استقرار
  • Nginx برای Reverse Proxy
  • HTTPS برای امنیت

سناریوهای واقعی استفاده از این سیستم

دستیار مستندات API

اگر محصول شما مستندات فنی دارد، می‌توانید فایل‌های Markdown یا HTML مستندات را وارد سیستم کنید.

کاربر می‌پرسد:

چطور API Key بسازم؟

سیستم:

  • بخش مربوط به API Key را پیدا می‌کند.
  • پاسخ دقیق می‌دهد.
  • در صورت نیاز مسیر مرتبط را معرفی می‌کند.

چت‌بات سازمانی

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

کارمند می‌پرسد:

فرایند درخواست مرخصی چیست؟

سیستم بر اساس اسناد داخلی پاسخ می‌دهد.

دستیار پشتیبانی مشتریان

برای شرکت‌های SaaS، می‌توان مستندات محصول و FAQ را وارد سیستم کرد.

مشتری می‌پرسد:

چطور حسابم را شارژ کنم؟

سیستم پاسخ را از مستندات پیدا می‌کند.

تحلیل قراردادها

برای تیم‌های حقوقی، می‌توان قراردادها را وارد سیستم کرد.

کاربر می‌پرسد:

بند مربوط به فسخ قرارداد را پیدا کن.

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

سوالات متداول

RAG چیست؟

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

LangChain در این پروژه چه نقشی دارد؟

LangChain برای ساخت Agent، تعریف Tool، مدیریت Prompt و ایجاد جریان‌های هوشمند استفاده می‌شود. در این مقاله از LangChain برای تبدیل Retriever به Tool و ساخت Agent استفاده کردیم.

LlamaIndex در این پروژه چه نقشی دارد؟

LlamaIndex مسئول بارگذاری اسناد، ساخت Index، بازیابی اطلاعات مرتبط و ایجاد Query Engine است.

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

بله. اگر هدف فقط پرسش‌وپاسخ روی اسناد باشد، LlamaIndex به‌تنهایی کافی است. اما برای ساخت Agentهای پیچیده‌تر، LangChain می‌تواند مفید باشد.

آیا API درواره با LangChain و LlamaIndex سازگار است؟

بله. از آنجا که API درواره با استاندارد OpenAI-Compatible کار می‌کند، می‌توان آن را در ابزارهایی که از Base URL و API Key سازگار با OpenAI پشتیبانی می‌کنند استفاده کرد.

Base URL درواره چیست؟

Base URL درواره به شکل زیر است:

https://api.darvareh.ir/v1

API Key درواره چگونه ارسال می‌شود؟

API Key باید در Header درخواست به‌صورت Bearer ارسال شود:

Authorization: Bearer YOUR_DARVAREH_API_KEY

آیا برای ساخت RAG حتماً Vector Database لازم است؟

برای نمونه‌های کوچک، می‌توان از Index داخلی استفاده کرد. اما برای پروژه‌های واقعی و داده‌های زیاد، استفاده از Vector Database مانند Qdrant، Chroma، Pinecone یا pgvector توصیه می‌شود.

آیا این سیستم برای زبان فارسی مناسب است؟

بله، اما کیفیت خروجی به چند عامل بستگی دارد:

  • کیفیت داده‌های فارسی
  • مدل انتخاب‌شده
  • مدل Embedding
  • نحوه Chunk بندی
  • Prompt مناسب فارسی

جمع‌بندی

در این مقاله یک سیستم RAG واقعی ساختیم که از LlamaIndex، LangChain و API درواره استفاده می‌کند.

در مسیر ساخت این پروژه یاد گرفتیم:

  • RAG چیست و چرا مهم است.
  • چگونه اسناد را با LlamaIndex بارگذاری کنیم.
  • چگونه Vector Index بسازیم.
  • چگونه Query Engine ایجاد کنیم.
  • چگونه API درواره را به‌عنوان مدل OpenAI-Compatible تنظیم کنیم.
  • چگونه Retriever را به Tool تبدیل کنیم.
  • چگونه LangChain Agent بسازیم.
  • چگونه با FastAPI یک API برای سیستم RAG ایجاد کنیم.
  • چگونه پروژه را با Docker آماده استقرار کنیم.

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

پروژه RAG خود را با API درواره بسازید

اگر قصد دارید یک سیستم RAG، چت‌بات سازمانی، دستیار اسناد یا Agent هوشمند بسازید، API درواره می‌تواند لایه اتصال شما به مدل‌های مختلف هوش مصنوعی باشد.

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

  • از طریق یک API به مدل‌های مختلف هوش مصنوعی دسترسی داشته باشید.
  • از Base URL سازگار با OpenAI استفاده کنید.
  • API Key خود را در ابزارهایی مانند LangChain، LlamaIndex، Open WebUI، Cline، Roo Code و OpenCode قرار دهید.
  • مصرف و هزینه را در یک داشبورد مدیریت کنید.
  • حساب خود را به‌صورت ریالی شارژ کنید.
Base URL:
https://api.darvareh.ir/v1

درواره؛ زیرساخت استفاده از هوش مصنوعی در ایران.

مقالات مرتبط

برای ادامه مسیر، این مقالات را نیز مطالعه کنید:

Read more