در حالی که اکثر مردم بر روی تولید افزوده بازیابی (RAG) بر روی متن بدون ساختار، مانند اسناد یا اسناد شرکت تمرکز میکنند، من نسبت به سیستمهای بازیابی بر روی اطلاعات ساختاریافته، به ویژه نمودارهای دانش ، بسیار خوشبین هستم. در مورد GraphRAG، به ویژه پیاده سازی مایکروسافت، هیجان زیادی وجود داشته است. با این حال، در اجرای آنها، داده های ورودی متنی بدون ساختار در قالب اسناد است که با استفاده از یک مدل زبان بزرگ (LLM) به یک نمودار دانش تبدیل می شود.
در این پست وبلاگ، نحوه پیادهسازی یک رتریور را بر روی یک نمودار دانش حاوی اطلاعات ساختاریافته از سیستم گزارشدهی رویدادهای نامطلوب FDA (FAERS) نشان خواهیم داد که اطلاعاتی در مورد رویدادهای نامطلوب دارو ارائه میدهد. اگر تا به حال با نمودارهای دانش و بازیابی سرهم بندی کرده اید، اولین فکر شما ممکن است استفاده از یک LLM برای ایجاد پرس و جوهای پایگاه داده برای بازیابی اطلاعات مربوطه از یک نمودار دانش برای پاسخ به یک سوال معین باشد. با این حال، تولید پرس و جو پایگاه داده با استفاده از LLM ها هنوز در حال تکامل است و ممکن است هنوز سازگارترین یا قوی ترین راه حل را ارائه نکند. بنابراین، در حال حاضر جایگزین های قابل دوام چیست؟
به نظر من، بهترین راه حل فعلی، تولید پرس و جو پویا است. این روش به جای تکیه کامل بر یک LLM برای تولید پرس و جو کامل، از یک لایه منطقی استفاده می کند که به طور قطعی یک پرس و جو پایگاه داده را از پارامترهای ورودی از پیش تعریف شده ایجاد می کند. این راه حل را می توان با استفاده از یک LLM با پشتیبانی از فراخوانی تابع پیاده سازی کرد. مزیت استفاده از ویژگی فراخوانی تابع در توانایی تعریف به یک LLM نهفته است که چگونه باید یک ورودی ساختاریافته را برای یک تابع آماده کند. این رویکرد تضمین می کند که فرآیند تولید پرس و جو کنترل شده و سازگار است در حالی که امکان انعطاف پذیری ورودی کاربر را فراهم می کند.
این تصویر فرآیند درک سوال کاربر برای بازیابی اطلاعات خاص را نشان می دهد. جریان شامل سه مرحله اصلی است:
یک کاربر در مورد عوارض جانبی رایج داروی لیریکا برای افراد زیر 35 سال سوال می پرسد.
LLM تصمیم می گیرد که کدام تابع و پارامترهای مورد نیاز را فراخوانی کند. در این مثال، تابعی به نام side_effects با پارامترهایی از جمله داروی Lyrica و حداکثر سن 35 سال را انتخاب کرد.
تابع و پارامترهای شناسایی شده برای تولید قطعی و پویا یک عبارت کوئری پایگاه داده (Cypher) برای بازیابی اطلاعات مرتبط استفاده می شود.
پشتیبانی از فراخوانی تابع برای موارد استفاده پیشرفته LLM حیاتی است، مانند اجازه دادن به LLMها برای استفاده از چندین رتریور بر اساس قصد کاربر یا ایجاد جریانهای چند عاملی. من چند مقاله با استفاده از LLM های تجاری با پشتیبانی از فراخوانی تابع بومی نوشته ام. با این حال، ما از Llama-3.1 که اخیراً منتشر شده است، یک LLM منبع باز برتر با پشتیبانی از فراخوانی تابع بومی استفاده خواهیم کرد.
کد در GitHub موجود است.
ما از Neo4j، که یک پایگاه داده گراف بومی است، برای ذخیره اطلاعات رویدادهای نامطلوب استفاده خواهیم کرد. میتوانید با دنبال کردن این پیوند، یک پروژه Sandbox ابری رایگان راهاندازی کنید که با FAERS از پیش پرجمعیتشده همراه است.
نمونه پایگاه داده نمونه دارای یک نمودار با طرح زیر است.
این طرح بر روی گره Case متمرکز است، که جنبه های مختلف گزارش ایمنی دارو، از جمله داروهای درگیر، واکنش های تجربه شده، نتایج و درمان های تجویز شده را به هم مرتبط می کند. مشخصه هر داروی اولیه، ثانویه، همزمان یا تداخلی است. موارد همچنین با اطلاعات مربوط به سازنده، گروه سنی بیمار و منبع گزارش همراه است. این طرح امکان ردیابی و تجزیه و تحلیل روابط بین داروها، واکنش های آنها و نتایج را به شیوه ای ساختاریافته فراهم می کند.
ما با ایجاد یک اتصال به پایگاه داده با نمونه سازی یک شی Neo4jGraph شروع می کنیم:
os.environ["NEO4J_URI"] = "bolt://18.206.157.187:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "elevation-reservist-thousands" graph = Neo4jGraph(refresh_schema=False)
گزینه های زیادی برای میزبانی LLM های منبع باز مانند Llama-3.1 وجود دارد. ما از کاتالوگ NVIDIA API استفاده خواهیم کرد که ریزسرویس های استنتاج NVIDIA NIM را ارائه می دهد و از عملکرد فراخوانی مدل های Llama 3.1 پشتیبانی می کند. هنگامی که یک حساب کاربری ایجاد می کنید، 1000 توکن دریافت می کنید که برای دنبال کردن آن کافی است. شما باید یک کلید API ایجاد کنید و آن را در نوت بوک کپی کنید:
os.environ["NVIDIA_API_KEY"] = "nvapi-" llm = ChatNVIDIA(model="meta/llama-3.1-70b-instruct")
ما از lama-3.1–70b استفاده خواهیم کرد زیرا نسخه 8b دارای برخی اشکالات با پارامترهای اختیاری در تعاریف تابع است.
نکته خوب در مورد میکروسرویس های NVIDIA NIM این است که اگر نگرانی های امنیتی یا نگرانی های دیگری دارید می توانید به راحتی آنها را به صورت محلی میزبانی کنید ، بنابراین به راحتی قابل تعویض است و فقط باید یک پارامتر URL را به پیکربندی LLM اضافه کنید:
# connect to an local NIM running at localhost:8000, # specifying a specific model llm = ChatNVIDIA( base_url="http://localhost:8000/v1", model="meta/llama-3.1-70b-instruct" )
ما یک ابزار واحد با چهار پارامتر اختیاری را پیکربندی می کنیم. ما یک عبارت Cypher متناظر را بر اساس این پارامترها می سازیم تا اطلاعات مربوطه را از نمودار دانش بازیابی کنیم. ابزار ما قادر خواهد بود بیشترین عوارض جانبی را بر اساس داروی ورودی، سن و سازنده دارو شناسایی کند.
@tool def get_side_effects( drug: Optional[str] = Field( description="disease mentioned in the question. Return None if no mentioned." ), min_age: Optional[int] = Field( description="Minimum age of the patient. Return None if no mentioned." ), max_age: Optional[int] = Field( description="Maximum age of the patient. Return None if no mentioned." ), manufacturer: Optional[str] = Field( description="manufacturer of the drug. Return None if no mentioned." ), ): """Useful for when you need to find common side effects.""" params = {} filters = [] side_effects_base_query = """ MATCH (c:Case)-[:HAS_REACTION]->(r:Reaction), (c)-[:IS_PRIMARY_SUSPECT]->(d:Drug) """ if drug and isinstance(drug, str): candidate_drugs = [el["candidate"] for el in get_candidates(drug, "drug")] if not candidate_drugs: return "The mentioned drug was not found" filters.append("d.name IN $drugs") params["drugs"] = candidate_drugs if min_age and isinstance(min_age, int): filters.append("c.age > $min_age ") params["min_age"] = min_age if max_age and isinstance(max_age, int): filters.append("c.age < $max_age ") params["max_age"] = max_age if manufacturer and isinstance(manufacturer, str): candidate_manufacturers = [ el["candidate"] for el in get_candidates(manufacturer, "manufacturer") ] if not candidate_manufacturers: return "The mentioned manufacturer was not found" filters.append( "EXISTS {(c)<-[:REGISTERED]-(:Manufacturer {manufacturerName: $manufacturer})}" ) params["manufacturer"] = candidate_manufacturers[0] if filters: side_effects_base_query += " WHERE " side_effects_base_query += " AND ".join(filters) side_effects_base_query += """ RETURN d.name AS drug, r.description AS side_effect, count(*) AS count ORDER BY count DESC LIMIT 10 """ print(f"Using parameters: {params}") data = graph.query(side_effects_base_query, params=params) return data
تابع get_side_effects برای بازیابی عوارض جانبی رایج داروها از نمودار دانش با استفاده از معیارهای جستجوی مشخص طراحی شده است. برای سفارشی کردن جستجو، پارامترهای اختیاری را برای نام دارو، محدوده سنی بیمار و سازنده دارو می پذیرد. هر پارامتر دارای توضیحاتی است که به همراه توضیحات تابع به یک LLM ارسال میشود و LLM را قادر میسازد تا نحوه استفاده از آنها را درک کند. سپس تابع یک پرسوجو Cypher پویا را بر اساس ورودیهای ارائهشده میسازد، این پرسوجو را در برابر نمودار دانش اجرا میکند و دادههای عوارض جانبی حاصل را برمیگرداند.
بیایید تابع را آزمایش کنیم:
get_side_effects("lyrica") # Using parameters: {'drugs': ['LYRICA', 'LYRICA CR']} # [{'drug': 'LYRICA', 'side_effect': 'Pain', 'count': 32}, # {'drug': 'LYRICA', 'side_effect': 'Fall', 'count': 21}, # {'drug': 'LYRICA', 'side_effect': 'Intentional product use issue', 'count': 20}, # {'drug': 'LYRICA', 'side_effect': 'Insomnia', 'count': 19}, # ...
ابزار ما ابتدا داروی Lyrica ذکر شده در سوال را با مقادیر «['LYRICA', 'LYRICA CR']» در نمودار دانش ترسیم کرد، سپس یک عبارت Cypher مربوطه را برای یافتن بیشترین عوارض جانبی اجرا کرد.
تنها کاری که باید انجام دهید پیکربندی یک عامل LLM است که می تواند از ابزار تعریف شده برای پاسخ به سؤالات مربوط به عوارض جانبی دارو استفاده کند.
تصویر یک کاربر را در حال تعامل با یک عامل Llama 3.1 برای پرس و جو در مورد عوارض جانبی دارو نشان می دهد. عامل به یک ابزار اثرات جانبی دسترسی پیدا می کند که اطلاعات را از یک نمودار دانش بازیابی می کند تا داده های مربوطه را در اختیار کاربر قرار دهد.
ما با تعریف الگوی سریع شروع می کنیم:
prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are a helpful assistant that finds information about common side effects. " "If tools require follow up questions, " "make sure to ask the user for clarification. Make sure to include any " "available options that need to be clarified in the follow up questions " "Do only the things the user specifically requested. ", ), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ] )
الگوی درخواست شامل پیام سیستم، تاریخچه گپ اختیاری و ورودی کاربر است. agent_scratchpad برای LLM محفوظ است، زیرا گاهی اوقات برای پاسخ به سؤال به مراحل متعددی مانند اجرا و بازیابی اطلاعات از ابزارها نیاز دارد.
کتابخانه LangChain اضافه کردن ابزارها به LLM را با استفاده از روش bind_tools آسان می کند:
tools = [get_side_effects] llm_with_tools = llm.bind_tools(tools=tools) agent = ( { "input": lambda x: x["input"], "chat_history": lambda x: _format_chat_history(x["chat_history"]) if x.get("chat_history") else [], "agent_scratchpad": lambda x: format_to_openai_function_messages( x["intermediate_steps"] ), } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser() ) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True).with_types( input_type=AgentInput, output_type=Output )
عامل ورودی را از طریق تبدیلها و کنترلکنندههایی پردازش میکند که تاریخچه چت را قالببندی میکنند، LLM را با ابزارهای محدود اعمال میکنند و خروجی را تجزیه میکنند. در نهایت، عامل با یک مجری تنظیم می شود که جریان اجرا را مدیریت می کند، انواع ورودی و خروجی را مشخص می کند و شامل تنظیمات پرحرفی برای ثبت جزئیات در طول اجرا می شود.
بیایید نماینده را آزمایش کنیم:
agent_executor.invoke( { "input": "What are the most common side effects when using lyrica for people below 35 years old?" } )
نتایج:
LLM تشخیص داد که باید از تابع get_side_effects با آرگومان های مناسب استفاده کند. سپس تابع به صورت پویا یک عبارت Cypher را تولید می کند، اطلاعات مربوطه را واکشی می کند و آن را به LLM برمی گرداند تا پاسخ نهایی را ایجاد کند.
قابلیتهای فراخوانی توابع افزودنی قدرتمندی برای مدلهای منبع باز مانند Llama 3.1 است که تعاملات ساختاریافتهتر و کنترلشدهتری را با منابع و ابزارهای داده خارجی ممکن میسازد. عامل های مبتنی بر گراف، فراتر از پرس و جو از اسناد بدون ساختار، امکانات هیجان انگیزی را برای تعامل با نمودارهای دانش و داده های ساخت یافته ارائه می دهند. سهولت میزبانی این مدل ها با استفاده از پلتفرم هایی مانند میکروسرویس های NVIDIA NIM باعث می شود به طور فزاینده ای در دسترس باشند.
مثل همیشه، کد در GitHub در دسترس است.
برای کسب اطلاعات بیشتر در مورد این موضوع، در NODES 2024 در 7 نوامبر، کنفرانس توسعه دهندگان مجازی رایگان ما در مورد برنامه های هوشمند، نمودارهای دانش و هوش مصنوعی به ما بپیوندید. اکنون ثبت نام کنید