آموزش هوش مصنوعی

آموزش LangGraph؛ ساخت ایجنت تحلیل سند با گراف وضعیت

پیشرفته مطالعه در ۱۱ دقیقه
mahdyar

آموزش LangGraph را با یک پروژه‌ی واقعی شروع می‌کنیم؛ ایجنتی که یادداشت‌های دست‌نویس یا اسناد تصویری را می‌خواند و درباره‌شان به سؤال‌ها پاسخ می‌دهد. تا اینجا با smolagents کار کردیم که بیشتر جزئیات اجرای ایجنت را پنهان می‌کند. LangGraph رویکرد متفاوتی دارد: مسیر اجرا را خودتان به شکل گراف تعریف می‌کنید.

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

LangGraph چیست؟

LangGraph کتابخانه‌ای از خانواده‌ی LangChain برای ساخت ایجنت و گردش کار است. ایده‌ی اصلی‌اش ساده است: کار را به شکل یک گراف تعریف می‌کنید. هر گره یک مرحله از کار است و یال‌ها مشخص می‌کنند بعد از هر مرحله کجا برویم.

سه مفهوم کلیدی: وضعیت، گره و یال

وضعیت (State) ساختار داده‌ای است که میان همه‌ی گره‌ها جابه‌جا می‌شود. هر گره وضعیت را می‌خواند، کارش را انجام می‌دهد و بخشی از وضعیت را به‌روز می‌کند. مثلاً فهرست پیام‌ها و مسیر فایل ورودی در وضعیت نگه داشته می‌شوند.

گره (Node) یک تابع پایتون است که یک مرحله از کار را انجام می‌دهد؛ مثلاً فراخوانی مدل یا اجرای ابزار. یال (Edge) مسیر حرکت را تعیین می‌کند. یال‌ها می‌توانند ثابت باشند یا شرطی؛ یعنی بر اساس وضعیت فعلی تصمیم بگیرند به کدام گره بروند.

چرا گراف؟

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

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

پروژه؛ ایجنت تحلیل سند آلفرد

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

  1. پردازش سند تصویری.
  2. استخراج متن با مدل بینایی-زبانی.
  3. انجام محاسبه در صورت نیاز.
  4. تحلیل محتوا و ارائه‌ی خلاصه.
  5. اجرای دستورهای مشخص درباره‌ی سند.

نصب و آماده‌سازی

سه بسته لازم داریم: langgraph برای گراف، langchain_openai برای اتصال به مدل و langchain_core برای کلاس‌های پایه‌ی پیام:

bash
pip install langgraph langchain_openai langchain_core

مثال درسنامه از مدل GPT-4o استفاده می‌کند که هم متن و هم تصویر را می‌فهمد. کلید API را در متغیر محیطی OPENAI_API_KEY قرار دهید. هر مدل بینایی دیگری که LangChain پشتیبانی کند هم کار می‌کند.

تعریف وضعیت ایجنت

وضعیت این ایجنت دو فیلد دارد. input_file مسیر سند ورودی را نگه می‌دارد و messages فهرست پیام‌های گفت‌وگوست:

python
from typing import Annotated, Optional, TypedDict
from langchain_core.messages import AnyMessage
from langgraph.graph.message import add_messages

class AgentState(TypedDict):
    input_file: Optional[str]  # PDF or PNG file path
    messages: Annotated[list[AnyMessage], add_messages]

نکته‌ی جدید، عملگر add_messages است. بدون آن، هر گره‌ای که پیام برگرداند، کل فهرست را جایگزین می‌کرد. با این عملگر پیام‌های جدید به فهرست اضافه می‌شوند و تاریخچه‌ی گفت‌وگو حفظ می‌شود.

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

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

ابزار استخراج متن از تصویر

این ابزار تصویر را می‌خواند، به base64 تبدیل می‌کند و همراه یک دستور ساده به مدل بینایی می‌فرستد:

python
import base64
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI

vision_llm = ChatOpenAI(model="gpt-4o")

def extract_text(img_path: str) -> str:
    """Extract text from an image file using a multimodal model."""
    try:
        with open(img_path, "rb") as f:
            image_base64 = base64.b64encode(f.read()).decode("utf-8")
        message = HumanMessage(content=[
            {"type": "text", "text": "Extract all the text from this image. Return only the extracted text, no explanations."},
            {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}},
        ])
        return vision_llm.invoke([message]).content.strip()
    except Exception as e:
        return f"Error extracting text: {e}"

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

اتصال ابزارها به مدل

حالا مدل اصلی را می‌سازیم و ابزارها را با bind_tools به آن وصل می‌کنیم:

python
def divide(a: int, b: int) -> float:
    """Divide a and b."""
    return a / b

tools = [divide, extract_text]
llm_with_tools = ChatOpenAI(model="gpt-4o").bind_tools(tools, parallel_tool_calls=False)

پارامتر parallel_tool_calls=False باعث می‌شود مدل در هر قدم فقط یک ابزار را صدا بزند. این کار دنبال‌کردن مسیر اجرا را ساده‌تر می‌کند و برای آموزش مناسب‌تر است.

ساخت گراف با الگوی ReAct

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

گره دستیار

گره دستیار پیام سیستمی را می‌سازد، توضیح ابزارها و نام فایل فعلی را در آن می‌گذارد و مدل را صدا می‌زند:

python
from langchain_core.messages import SystemMessage

def assistant(state: AgentState):
    sys_msg = SystemMessage(content=(
        "You are a helpful butler named Alfred. You can analyse documents and run computations with the provided tools. "
        f"Currently the loaded image is: {state['input_file']}"
    ))
    return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])], "input_file": state["input_file"]}

خروجی گره یک پیام جدید است که به لطف add_messages به تاریخچه اضافه می‌شود. این پیام یا پاسخ نهایی است یا درخواست فراخوانی یک ابزار.

گره ابزارها و یال شرطی

حالا گراف را می‌سازیم. دو گره داریم: assistant و tools. گره ابزارها با کلاس آماده‌ی ToolNode ساخته می‌شود که ابزار خواسته‌شده را اجرا می‌کند:

python
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition

builder = StateGraph(AgentState)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()

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

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

گراف ReAct در LangGraph با گره دستیار، گره ابزارها و یال شرطی

ایجنت در عمل

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

مثال اول؛ محاسبه‌ی ساده

از ایجنت می‌خواهیم ۶۷۹۰ را بر ۵ تقسیم کند. مسیر اجرا کاملاً شفاف است. دستیار ابزار divide را با ورودی‌های ۶۷۹۰ و ۵ صدا می‌زند. گره ابزارها عدد ۱۳۵۸ را برمی‌گرداند. دستیار دوباره اجرا می‌شود و پاسخ نهایی را می‌نویسد.

این مثال ساده نشان می‌دهد هر پیام، از درخواست کاربر تا فراخوانی ابزار و پاسخ آن، در وضعیت ثبت می‌شود. با چاپ messages کل مسیر را قدم‌به‌قدم می‌بینید.

مثال دوم؛ تحلیل یادداشت آقای وین

حالا سؤال واقعی: «بر اساس یادداشت آقای وین، برای منوی شام چه چیزهایی باید بخرم؟» همراه این سؤال، مسیر تصویر یادداشت در input_file قرار می‌گیرد.

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

جدول زیر تفاوت دو مثال را در مسیر گراف خلاصه می‌کند.

مثالابزار فراخوانی‌شدهتعداد دور حلقهخروجی
تقسیم عددdivideیک دورعدد ۱۳۵۸
تحلیل یادداشتextract_textیک دورفهرست خرید منوی شام
پرسش عمومیهیچبدون دورپاسخ مستقیم مدل

درس‌های کلیدی ساخت ایجنت با LangGraph

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

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

تمرین عملی؛ خواندن برگه‌ی ثبت‌نام دوره

ایجنت LangGraph را برای دستیار پروژه‌ی مشترک به کار بگیرید:

  1. از یک برگه‌ی ثبت‌نام دوره، حتی دست‌نویس، عکس بگیرید.
  2. ابزاری به نام find_course اضافه کنید که با نام دوره، مشخصاتش را برگرداند.
  3. بپرسید «این دانشجو در کدام دوره ثبت‌نام کرده و کلاس از چه تاریخی شروع می‌شود؟».

با چاپ پیام‌ها بررسی کنید ایجنت اول extract_text و بعد find_course را صدا زد یا نه. این یعنی دو دور حلقه در گراف؛ همان رفتاری که با ترکیب ابزارها انتظار داریم.

سخن پایانی

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

در قسمت نوزدهم یاد می‌گیریم کیفیت، زمان و هزینه‌ی ایجنت‌ها را چطور اندازه بگیریم.

این آموزش بر پایه‌ی بخش Document Analysis Graph از دوره‌ی آزاد Agents Course در Hugging Face (مجوز Apache-2.0) به فارسی بازنویسی و تکمیل شده است.

پرسش‌های پرتکرار

LangGraph چیست و چه فرقی با smolagents دارد؟

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

State در LangGraph چیست؟

ساختار داده‌ای که میان همه‌ی گره‌ها جابه‌جا می‌شود و وضعیت فعلی کار را نگه می‌دارد؛ مثلاً فهرست پیام‌ها و مسیر فایل ورودی. هر گره State را می‌خواند و بخشی از آن را به‌روز می‌کند.

add_messages در LangGraph چه کاری انجام می‌دهد؟

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

tools_condition چطور مسیر گراف را تعیین می‌کند؟

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

دیدگاه‌ها

هنوز دیدگاهی ثبت نشده است. شروع‌کننده باشید — پرسش و اصلاح را با آغوش باز می‌پذیریم.

دیدگاه بگذارید

برای **پررنگ**، *مورب*، `کد` و لینک از Markdown استفاده کنید. لینک‌ها nofollow هستند.

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

تصویر کد امنیتی پنج‌رقمی

در حال دریافت کد امنیتی…

مهلت کد تمام شد. روی «کد جدید» بزنید.