ایده اصلی عامل LangChain این است که از LLM به عنوان یک مغز برای انجام فکر خودکار، تصمیمگیری و اجرای اقدامات مختلف برای در نهایت انجام وظایف مورد هدف استفاده کنیم.
نکته: از منظر توسعه، این به معنای این است که ما ابتدا اقدامات مختلف را ایجاد میکنیم و سپس وظیفهای را به عامل میدهیم، اجازه میدهیم به LLM تحلیل کند که کدام API را برای انجام وظیفه فراخوانی کند.
برای درک بهتر از مشکلی که عامل LangChain برنامه ریزی برای حل آن دارد، بیایید یک مثال را مورد بررسی قرار دهیم.
برای مثال:
اگر بخواهیم بررسی کنیم که "Docker آیا میتواند به عنوان یک راهکار مستقری تولید استفاده شود"، ابتدا "مقدمه Docker" را در Baidu جستجو میکنیم، نتایج جستجو را مشاهده میکنیم، سپس بیشتر جستجو کرده و "مزایا و معایب مستقری Docker" و دیگر نتایج را مشاهده میکنیم و به همین ترتیب تا رسیدن به نتیجه.
هدف عامل LangChain این است که این فرایند را شبیهسازی کند. من میتوانم یک مجموعه ابزار (مانند جستجو در Baidu، ابزار استخراج محتوای URL) را پیشبستهبندی کنم، سپس وظیفه هدفی به عامل بدهم "آیا Docker میتواند به عنوان یک راهکار مستقری تولید استفاده شود؟". عامل سپس پیامهایی را برای فراخوانی بر روی LLM ایجاد خواهد کرد. برای دستیابی به این وظیفه، مرحله بعد این است که اقدامی را انجام دهیم (به عبارت دیگر، چه ابزاری را فراخوانی کنیم). هوش مصنوعی به اندازه کافی پیشرفت داشته و در حال حاضر این ویژگی هنوز در مرحله آزمایشی است و بارها بر روی LLM فراخوانی میکند، بنابراین از تعداد قابل توجهی توکن استفاده میشود. انجام یک وظیفه میتواند دهها هزار توکن را در عرض چند دقیقه هزینه کند. اگر میخواهید پول صرفهجویی کنید، توصیه میشود ابتدا به LLM اجازه دهید وظایف منطقی ساده را انجام دهد.
مفاهیم اصلی
حالا بیایید مفاهیم مرتبط و اجزا را معرفی کنیم.
عامل
یک عامل میتواند به عنوان دستیار ما، به نمایش درآید و به نیابت از ما تصمیمات بگیرد. در اجرای زیرین عامل LangChain، از طریق LLM که عمل بعدی (یا فراخوانی API) تعیین میشود، استفاده میشود. حالت ReAct فرآیند تصمیمگیری هوش مصنوعی را توصیف میکند. افراد علاقمند میتوانند این موضوع را بیشتر بررسی کنند.
LangChain چند نوع عامل برای سناریوهای مختلف ارائه میدهد.
ابزارها
من فکر میکنم بهتر است ابزارها را به عنوان APIها درک کنیم، که قبلاً با انواع مختلف از APIهای کاربردی طراحی شدهاند تا تواناییهای LLM را گسترش دهند. LLM تعیین میکند کدام API خاص را برای انجام یک وظیفه فراخوانی کند.
مجموعه ابزار
معمولاً مجموعه ابزار به LLM بیش از یک یا دو ابزار ارائه میدهد تا LLM وقتی وظایف را انجام میدهد گزینههای بیشتری داشته باشد.
اجراکننده عامل
اجرا کننده نماینده مسئول اجرای ابزار (API) انتخاب شده توسط LLM است. کد نمایی برای این زمان اجرا به صورت زیر است:
next_action = agent.get_action(...)
while next_action != AgentFinish:
observation = run(next_action)
next_action = agent.get_action(..., next_action, observation)
return next_action
اگرچه فرایند اجرا پیچیده نیست، اما اجراکننده مسئول حل مسائل جزئیات زیادی است، اصلیترین آنها عبارتند از:
- رسیدگی به شرایطی که عامل ابزاری انتخاب میکند که وجود ندارد
- رسیدگی به موارد خطا ابزار
- برخورد با شرایطی که عامل خروجی تولید میکند که به عنوان فراخوانی ابزار حل نشود
- مشکلات اشکالزدایی.
شروع سریع
این بخش معرفیای ساده از استفاده اولیه عامل LangChain را ارائه میدهد.
1. بارگذاری LLM
ابتدا بیایید مدل زبان (LLM) را که برای کنترل عامل استفاده خواهیم کرد، بارگیری کنیم.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
2. تعریف ابزارها
سپس، برخی ابزارها را برای فراخوانی عامل تعریف میکنیم. یک تابع پایتون بسیار ساده برای محاسبه طول کلمه ورودی خواهیم نوشت.
from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
"""Returns the length of the word."""
return len(word)
get_word_length.invoke("abc")
توجه: نظرات تابع بسیار مهم هستند. آنها به LLM میگویند که مشکلی را با فراخوانی آنها میتواند حل کند. تابع
get_word_length
به LLM میگوید که میتواند طول یک کلمه را محاسبه کند.
3
تعریف مجموعهای از ابزارها
tools = [get_word_length]
3. ایجاد یک دستورالعمل
حالا بیایید یک دستورالعمل ایجاد کنیم. زیرا فراخوانی تابع OpenAI بهینهسازی شده است، تقریبا نیازی به هیچ دستورالعملی در مورد استدلال یا فرمت خروجی نداریم. تنها دو متغیر ورودی داریم: input
و agent_scratchpad
. input
نشاندهنده سوال ورودی کاربر است و agent_scratchpad
یک قالب دارایی برای دستورات فراخوانی واسط است که هنگام اجرای دستور فراخوانی ابزار در قالب دستور مدل وارد میشود.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"شما یک دستیار بسیار قدرتمند هستید، اما وضعیت کنونی را نمیفهمید.",
),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
4. متصل کردن ابزارها به LLM
چطور از این متوجه میشود که کدام ابزارها را میتواند استفاده کند؟
این وابسته به ویژگی فراخوانی ابزار OpenAI است (بسیاری از مدلها از ویژگیهای مشابه پشتیبانی میکنند). ما فقط باید به مدل فرمت فراخوانی ابزارهای تعریف شده را بگوییم.
llm_with_tools = llm.bind_tools(tools)
5. ایجاد یک واسط
حال که محتوای قبلی را یکپارچه کردهایم، میتوانیم به ایجاد برنامه پروکسی بپردازیم. ما دو تابع کاربردی آخر را وارد میکنیم: یکی برای فرمت دهی به مراحل میانی (اقدامات پروکسی، خروجیهای ابزار) برای تبدیل آنها به پیامهای ورودی که میتوانند به مدل ارسال شوند، و دیگری برای تبدیل پیامهای خروجی به اقدامات پروکسی/پایان پروکسی.
from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
تعریف دستورالعمل واسط
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
بیایید از طریق یک مثال عملکرد برنامه پروکسی را نشان دهیم:
list(agent_executor.stream({"input": "تعداد حروف کلمه 'آموزش' چقدر است؟"}))
مثالی از گزارش خروجی واسط
> ورود به زنجیره اجرایی جدید واسط...
فراخوانی: اجرای `get_word_length` با پارامتر `{'word': 'آموزش'}`
تعداد 4 حرف در کلمه "آموزش" وجود دارد.
> زنجیره اجرایی تکمیل شد.
توسط این مثال، فرایند کامل برنامه پروکسی را نشان دادهایم.
اضافه کردن قابلیت حافظه به واسط
اگر بخواهیم واسط قادر به یادآوری گفتگوهای قبلی باشد، در واقع بسیار ساده است: تنها کافی است که محتوای بازگشتی توسط هوش مصنوعی را در دستورالعمل قرار دهیم و همراه آن به هوش مصنوعی ارسال کنیم.
اصلاح قالب دستورالعمل
در زیر قالب دستورالعمل را برای شامل یک متغیر الگوی تاریخچه گفتگو اصلاح میکنیم.
from langchain.prompts import MessagesPlaceholder
MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"شما یک دستیار عالی هستید، اما در محاسبه طول کلمات ماهر نیستید.",
),
MessagesPlaceholder(variable_name=MEMORY_KEY),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
اصلاح تعریف فرآیند واسط
تعریف فرآیند واسط را به گونهای اصلاح میکنیم که دادههای تاریخچه گفتگو را برای قالب دستورالعمل فراهم کند، همانطور که در کد زیر نشان داده شده است.
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(
x["intermediate_steps"]
),
"chat_history": lambda x: x["chat_history"],
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
ارائه دادههای تاریخچه گفتگو هنگام فراخوانی عامل
chat_history = []
input1 = "تعداد حروف کلمه 'educa' چقدر است؟"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend(
[
HumanMessage(content=input1),
AIMessage(content=result["output"]),
]
)
agent_executor.invoke({"input": "آیا این کلمه واقعا وجود دارد؟", "chat_history": chat_history})
در سناریوهای واقعی تجاری، میتوانید تاریخچه گفتگو را در یک پایگاه داده ذخیره کرده و بر اساس نیازهای تجاری، اطلاعات را به الگوی ورودی درج کنید.
نکته: در حال حاضر، عملکرد حافظه مدلهای بزرگ (LLM) اغلب با درج محتوای گفتگوی تاریخی در الگوی ورودی و ارسال آن به LLM بهصورت اصلی انجام میشود. LangChain به سادگی برخی پوششها را فراهم میکند. شما میتوانید انتخاب کنید که از آن استفاده نکنید و بهصورت دستی محتوای گفتگو را به الگوی ورودی اضافه کرده و آن را ارسال کنید.