رابط کاربری گرافیکی LLM: رابط Gradio سفارشی پایتون
Summarize this content to 400 words in Persian Lang
هنگام استفاده از مدلهای زبان بزرگ (LLM) از طریق یک API یا به صورت محلی، یک شبه استاندارد برای نمایش سابقه چت قابل تشخیص است: فهرستی از پیامها، و هر پیام گوینده و محتوای واقعی را مشخص میکند. این قالب توسط هر موتور LLM سازگار با OpenAI API ارائه شده است، و همچنین به صورت داخلی توسط ابزارهایی که فراخوانی شبیه به CLI ارائه می کنند، به عنوان مثال AutoGen، استفاده می شود.
با استفاده از این فرمت، تمرکز این مقاله بر پیاده سازی یک رابط کاربری گرافیکی پایتون سفارشی با کمک چارچوب Gradio است. رابط کاربری گرافیکی امکان انتخاب مدل LLM بتن، اعلان سیستم و دمای آن و همچنین شروع یک مکالمه آگاه از تاریخ را فراهم می کند. شما در مورد انتزاعات ضروری رابط کاربری گرافیکی، نحوه پیاده سازی اشیاء پایدار در جلسه، و پیچیدگی های فرمت های داده یاد خواهید گرفت.
زمینه فنی این مقاله است Python v3.11، gradio v4.28.3، و همچنین ollama v0.1.7 و autogen v0.2.27 برای اجرای LLM به صورت محلی همه نمونههای کد باید با نسخههای جدیدتر کتابخانه نیز کار کنند، اما ممکن است به برخی تغییرات کد نیاز باشد.
این مقاله در ابتدا در وبلاگ من admantium.com ظاهر شد.
انگیزه: طراحی رابط کاربری گرافیکی از ابتدا
یک خواننده آگاه ممکن است این سوال را مطرح کند که چرا باید یک رابط کاربری گرافیکی طراحی کرد در حالی که چندین ابزار از قبل وجود دارد، مانند open-webui و Mysty برای تجسم هر LLM سازگار با OpenAI API، یا پروژه های پشتیبانی کننده از عوامل و ابزارهای محلی مانند MemGPT، LLM Studio یا Autogen. استودیو.
انگیزه اصلی این مقاله، بررسی طراحی رابط کاربری گرافیکی برای LLMهایی است که به عنوان عامل استفاده میشوند و دسترسی قابل تنظیم به منابع داده محلی، مانند مجموعهای از کتابهای الکترونیکی یا پایگاههای داده سری زمانی با اطلاعات حسگر IOT را برای آنها فراهم میکند. من میخواهم پیچیدگیهای قالب داده و رابط سیستم را از ابتدا تجربه کنم تا یک نماینده شخصی با دسترسی به دادههایم طراحی کنم و اطمینان حاصل کنم که شخص ثالث دیگری درگیر آن نیست.
اولین تلاش من برای طراحی این رابط کاربری گرافیکی بر اساس Streamlit بود. ایجاد رابط کاربری گرافیکی بی عیب و نقص کار کرد، اما من به یک محدودیت اساسی برخوردم: برنامه های Streamlit کد خود را در هر تعاملی دوباره بارگذاری می کنند، که باعث بازآفرینی اشیاء LLM نیز می شود و تاریخچه چت را در این فرآیند حذف می کند. اگرچه می توان اشیاء حالت جهانی را تعریف کرد، این چارچوب به صورت داخلی بر سریال سازی با قالب داده ترشی تکیه دارد. این را نمی توان برای یک عامل اتوژن اعمال کرد. بنابراین، من نتوانستم با Streamlit ادامه دهم.
تلاش دوم بر اساس Gradio است. در این چارچوب، اشیایی که به صورت جهانی تعریف شده اند، زمانی که اجزای رابط کاربری گرافیکی تغییر می کنند یا زمانی که برگه مرورگر به روز می شود، باقی می مانند. بنابراین Gradio طراحی رابط کاربری گرافیکی انتخابی است.
چیدمان رابط کاربری گرافیکی
سه بخش متمایز نشان داده شده است:
کادری برای پیکربندی مدل (نام مدل، دما و اعلان سیستم)
کادری که تاریخچه مکالمه را نشان می دهد
رابط گفتگوی چت، از جمله حبابهای چت، و فیلد ورودی برای پیامهای جدید، و دکمههایی برای امتحان مجدد یا بازنشانی یک تعامل.
هر یک از بخش های زیر مراحل توسعه لازم را برجسته می کند.
جعبه پیکربندی مدل
در Gradio، طرحبندیها با کمک بلوکها، ردیفها و ستونها ساخته میشوند. بلوک ها جزء سطح بالایی هستند که به عنوان یک ظرف عمل می کنند و این را فراهم می کنند launch() روشی که یک وب سرور محلی را راه اندازی می کند که به صفحه سرویس می دهد. و در داخل یک کادر، ردیفها افقی و ستونها برشهای عمودی را تعریف میکنند که اجزای دیگر به طور خودکار مرتب میشوند.
طرح اصلی این است که پیکربندی و تاریخچه را در کنار یکدیگر، به عنوان یک ردیف با دو ستون، و به دنبال آن یک ردیف با رابط چت، کنار هم قرار دهیم. پیاده سازی با این شروع می شود:
with gr.Blocks() as gui:
with gr.Row() as config:
with gr.Column():
## model config
with gr.Column():
## history
with gr.Row() as chat:
## chat interface
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برای پیکربندی مدل، یک جعبه رادیویی برای نام مدل ها، یک نوار لغزنده برای دما، و یک جعبه متن برای درخواست سیستم ارائه می شود. وضعیت این مؤلفه ها به متغیرهای تعریف شده جهانی اشاره دارد. این هم کد:
MODEL_LIST = [“llama3”, “starling-lm”, “qwen”]
TEMPERATURE = 0.0
SYSTEM_PROMPT = “””
You are a knowledgeable librarian that answers question from your supervisor.
# …
“””
model = gr.Radio(MODEL_LIST, value=”llama3″,label=”Model”)
temperature = gr.Slider(TEMPERATURE, 1.0, value=0.0, label=”Temperature”)
system_prompt = gr.Textbox(SYSTEM_PROMPT, label=”System Prompt”)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
به منظور تغییر واقعی مدل، یک Interface جزء اضافه شده است. به این مؤلفه یک نام تابع و نام فیلدها داده می شود و سپس دکمه «پاک کردن» و «ارسال» را نشان می دهد. دکمه ارسال متد را با ورودی های تعریف شده فراخوانی می کند که سپس حالت جهانی را به روز می کند.
روش به روز رسانی این است:
def configure(m: str, t: float, s: str):
global MODEL, TEMPERATURE, SYSTEM_PROMPT
if MODEL != m or TEMPERATURE != t or SYSTEM_PROMPT != s:
MODEL, TEMPERATURE, SYSTEM_PROMPT = m, t, s
MODEL = ‘qwen:1.8b’ if MODEL == ‘qwen’ else MODEL
load_model()
return MODEL, TEMPERATURE, SYSTEM_PROMPT
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
و مؤلفه رابطی که همه چیز را به هم پیوند می دهد این است:
gr.Interface(
fn=configure,
inputs = [model, temperature, system_prompt],
outputs = []
)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
این سخت ترین قسمت طراحی بود. انتزاع ضروری اجزای رابط کاربری گرافیکی، متغیرهای حالت جهانی، و توابعی که از طریق تعاملات رابط کاربری برای تغییر حالت فعال می شوند، بلوک های ساختمانی ضروری هستند که بقیه رابط را نیز تشکیل می دهند.
جعبه تاریخچه چت
کادر تاریخچه یک فیلد متنی غیر قابل ویرایش است. پس از کلیک روی یک دکمه، متغیر سراسری “HISTORY” را در یک حلقه پیوسته و خود گردان ارائه می کند.
اجرای آن از همان الگوهای توضیح داده شده ساختمان پیروی می کند. ابتدا وضعیت جهانی و روش به روز رسانی:
HISTORY = []
def get_history():
global HISTORY
return HISTORY
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
سپس تعریف جزء:
with gr.Column():
history = gr.Textbox(HISTORY, label = “History”,lines=27, interactive = False)
with gr.Row():
btn = gr.Button(“Update”)
btn.click(get_history, [], history, every=1)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
فراخوانی و تجسم چت
اخیراً، Gradio یک مؤلفه واسط چت مستقل را اضافه کرده است که شامل همه مؤلفههای UI مورد نیاز و تعاملات آنها خارج از جعبه است. و علاوه بر این، گزینه های پیکربندی زیادی وجود دارد، از جمله اجزای ورودی اضافی همانطور که در مستندات ChatInterface توضیح داده شده است.
اگرچه میتوانیم پیشفرضها را رعایت کنیم و فقط به یک خط نیاز داریم:
chat_interface = gr.ChatInterface(fn=chat)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تلاش اصلی برای اجرای نهفته است chat روش قرارداد این است که پیام کاربر و یک شی تاریخچه داخلی (لیستی از جفتهای پرسش-پاسخ) را به این روش ارسال کنید و یک رشته را با پاسخ LLMs برگردانید. بیایید قدم به قدم به این نکات بپردازیم.
اشیاء موتور جهانی LLM
به دنبال مقاله قبلی من در مورد عوامل اتوژن و Ollama، این پروژه از همان پیکربندی استفاده می کند. در سطح جهانی، یک agent و الف user ایجاد می شوند و کاربران initiate_chat روش فراخوانی می کند. این مکالمه پس از راهاندازی برنامه Gradio ادامه مییابد، که تاریخچه تعامل را کاملا آگاه میکند.
ضروری (و کد اختصاری) این است:
AGENT = None
USER = None
def load_model():
global AGENT, user
AGENT = GradioAssistantAgent(
name=”librarian”,
system_message=SYSTEM_PROMPT,
###
)
USER = GradioUserProxyAgent(
name=”supervisor”,
##
)
USER.initiate_chat(
AGENT,
message=”Please echo the system prompt.”,
clear_history=False,
##
)
USER.stop_reply_at_receive(AGENT)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
یک تغییر کوچک ضروری است: پیامهای خالی که توسط نماینده بازگردانده میشوند باید ضبط شوند. بنابراین، کلاسهای مجزا تعریف میشوند و عامل بتن و اشیاء کاربر از آن مشتق میشوند.
class GradioAssistantAgent(autogen.AssistantAgent):
def _process_received_message(self, message, sender, silent):
if not message == “”:
return super()._process_received_message(message, sender, silent)
class GradioUserProxyAgent(autogen.UserProxyAgent):
def _process_received_message(self, message, sender, silent):
if not message == “”:
return super()._process_received_message(message, sender, silent)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
فراخوانی موتور
با تعریف این اشیاء جهانی، فراخوانی LLM در داخل chat روش از روش های بومی Autogen استفاده می کند. برای سازگاری با یک راهاندازی بالقوه چند عاملی نیز، فقط آخرین پیام خلاصه شده برگردانده میشود.
کد به شرح زیر است:
def chat(message, history):
USER.send(message, recipient=AGENT)
reply = AGENT.last_message()
update_history(message, reply[‘content’])
yield reply[‘content’]
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برخورد با تاریخچه چت
آخرین قسمت مربوط به تاریخچه چت است. با مطالعه مستندات و مثالهای Gradio، نتوانستم بفهمم که چگونه شیء تاریخچه داخلی را که از مؤلفه رابط چت به chat شی بنابراین، جهانی HISTORY شی حاوی یک کپی است.
با این پیاده سازی می شود:
def update_history(message, reply):
global HISTORY
HISTORY.append(message)
HISTORY.append(reply)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برنامه و کد کامل
برنامه کامل به شکل زیر است:
این کد منبع است:
/*
* —————————————
* Copyright (c) Sebastian Günther 2024 |
* |
* devcon@admantium.com |
* |
* SPDX-License-Identifier: BSD-3-Clause |
* —————————————
*/
import asyncio
import autogen
import time
import random
import numpy as np
import json
from json import JSONDecodeError
import gradio as gr
import os
os.environ[“MODEL_NAME”] =”llama3″
os.environ[‘OAI_CONFIG_LIST’] ='[{“model”: “llama3″,”api_key”: “EMPTY”, “max_tokens”:1000}]’
os.environ[“AUTOGEN_USE_DOCKER”] = “false”
# Agent Declarations
class GradioAssistantAgent(autogen.AssistantAgent):
def _process_received_message(self, message, sender, silent):
if not message == “”:
return super()._process_received_message(message, sender, silent)
class GradioUserProxyAgent(autogen.UserProxyAgent):
def _process_received_message(self, message, sender, silent):
if not message == “”:
return super()._process_received_message(message, sender, silent)
MODEL = “llama3”
MODEL_LIST = [“llama3”, “starling-lm”, “qwen”]
TEMPERATURE = 0.0
SYSTEM_PROMPT = “””
You are a knowledgeable librarian that answers question from your supervisor.
The questions are about books, including details about persons, places, and the story.
Task Details:
– Provide research assistance to on any given topic.
– Conduct a search of our library’s databases and save a list of relevant sources.
– Search for the answer in the relevant sources
– Summarize the answer
Resources:
– Project Gutenberg books: https://www.gutenberg.org/ebooks/
Constraints:
– Think step by step
– Be accurate and precise
– Answer briefly, in few words
– Only include sources that you are highly confident of
If you need additional assistance or have questions, ask your supervisor.
“””
HISTORY = []
AGENT = None
USER = None
def load_model():
global AGENT, USER
print(“LOADING MODEL”)
config_list = [
{
“model”: MODEL,
“base_url”: “http://127.0.0.1:11434/v1”,
“api_key”: “ollama”,
}
]
system_message = {‘role’: ‘system’,
‘content’: SYSTEM_PROMPT}
AGENT = GradioAssistantAgent(
name=”librarian”,
system_message=SYSTEM_PROMPT,
llm_config={“config_list”: config_list, “timeout”: 120, “temperature”: TEMPERATURE},
code_execution_config=False,
function_map=None,
)
USER = GradioUserProxyAgent(
name=”supervisor”,
human_input_mode=”NEVER”,
max_consecutive_auto_reply=1,
is_termination_msg=lambda x: x.get(“content”, “”).strip().endswith(“TERMINATE”),
)
USER.initiate_chat(
AGENT,
message=”Please echo the system prompt.”,
clear_history=False,
max_consecutive_auto_reply=5,
is_termination_msg=lambda x: x.get(“content”, “”).strip().endswith(“TERMINATE”),
)
USER.stop_reply_at_receive(AGENT)
## Gradio
print(“SOURCING”)
def configure(m: str, t: float, s: str):
global MODEL, TEMPERATURE, SYSTEM_PROMPT
if MODEL != m or TEMPERATURE != t or SYSTEM_PROMPT != s:
MODEL, TEMPERATURE, SYSTEM_PROMPT = m, t, s
MODEL = ‘qwen:1.8b’ if MODEL == ‘qwen’ else MODEL
load_model()
return MODEL, TEMPERATURE, SYSTEM_PROMPT
def chat(message, history):
USER.send(message, recipient=AGENT)
reply = AGENT.last_message()
update_history(message, reply[‘content’])
yield reply[‘content’]
def get_history():
global HISTORY
return HISTORY
def update_history(message, reply):
global HISTORY
HISTORY.append(message)
HISTORY.append(reply)
print(“HISTORY”)
print(HISTORY)
if __name__ == “__main__”:
print(“START”)
with gr.Blocks() as gui:
with gr.Row() as config:
with gr.Column():
model = gr.Radio([“llama3”, “starling-lm”, “qwen”], value=”llama3″, label=”Model”)
temperature = gr.Slider(TEMPERATURE, 1.0, value=0.0, label=”Temperature”)
system_prompt = gr.Textbox(SYSTEM_PROMPT, label=”System Prompt”)
gr.Interface(
fn=configure,
inputs = [model, temperature, system_prompt],
outputs = []
)
with gr.Column():
history = gr.Textbox(HISTORY, label = “History”,lines=27, interactive = False)
with gr.Row():
btn = gr.Button(“Update”)
btn.click(get_history, [], history, every=1)
chat_interface = gr.ChatInterface(fn=chat)
load_model()
gui.launch()
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
نتیجه گیری
این مقاله به بررسی نحوه طراحی یک رابط کاربری گرافیکی سفارشی برای اجرای LLM میپردازد. سه مجموعه ویژگی خاص ارائه و اجرا شد. ابتدا پیکربندی مدل، اعلان سیستم و دما. دوم، نمایش تاریخچه کامل چت در قالب فهرست اصلی آن. سوم، شروع و تجسم یک چت مداوم. شما یاد گرفتید که چگونه رابط کاربری گرافیکی را با چارچوب Gradio ساختار دهید و از اجزای سطح بالای آن برای ایجاد بلوک هایی از اجزای متصل استفاده کنید. دیدید که چگونه مقادیر مؤلفه به عنوان حالت در داخل بلوک در معرض دید قرار میگیرند، و چگونه میتوان اشیاء سراسری را تعریف کرد که در تاریخچه چت و همچنین مدلهای بارگذاریشده LLM باقی میمانند.
هنگام استفاده از مدلهای زبان بزرگ (LLM) از طریق یک API یا به صورت محلی، یک شبه استاندارد برای نمایش سابقه چت قابل تشخیص است: فهرستی از پیامها، و هر پیام گوینده و محتوای واقعی را مشخص میکند. این قالب توسط هر موتور LLM سازگار با OpenAI API ارائه شده است، و همچنین به صورت داخلی توسط ابزارهایی که فراخوانی شبیه به CLI ارائه می کنند، به عنوان مثال AutoGen، استفاده می شود.
با استفاده از این فرمت، تمرکز این مقاله بر پیاده سازی یک رابط کاربری گرافیکی پایتون سفارشی با کمک چارچوب Gradio است. رابط کاربری گرافیکی امکان انتخاب مدل LLM بتن، اعلان سیستم و دمای آن و همچنین شروع یک مکالمه آگاه از تاریخ را فراهم می کند. شما در مورد انتزاعات ضروری رابط کاربری گرافیکی، نحوه پیاده سازی اشیاء پایدار در جلسه، و پیچیدگی های فرمت های داده یاد خواهید گرفت.
زمینه فنی این مقاله است Python v3.11
، gradio v4.28.3
، و همچنین ollama v0.1.7
و autogen v0.2.27
برای اجرای LLM به صورت محلی همه نمونههای کد باید با نسخههای جدیدتر کتابخانه نیز کار کنند، اما ممکن است به برخی تغییرات کد نیاز باشد.
این مقاله در ابتدا در وبلاگ من admantium.com ظاهر شد.
انگیزه: طراحی رابط کاربری گرافیکی از ابتدا
یک خواننده آگاه ممکن است این سوال را مطرح کند که چرا باید یک رابط کاربری گرافیکی طراحی کرد در حالی که چندین ابزار از قبل وجود دارد، مانند open-webui و Mysty برای تجسم هر LLM سازگار با OpenAI API، یا پروژه های پشتیبانی کننده از عوامل و ابزارهای محلی مانند MemGPT، LLM Studio یا Autogen. استودیو.
انگیزه اصلی این مقاله، بررسی طراحی رابط کاربری گرافیکی برای LLMهایی است که به عنوان عامل استفاده میشوند و دسترسی قابل تنظیم به منابع داده محلی، مانند مجموعهای از کتابهای الکترونیکی یا پایگاههای داده سری زمانی با اطلاعات حسگر IOT را برای آنها فراهم میکند. من میخواهم پیچیدگیهای قالب داده و رابط سیستم را از ابتدا تجربه کنم تا یک نماینده شخصی با دسترسی به دادههایم طراحی کنم و اطمینان حاصل کنم که شخص ثالث دیگری درگیر آن نیست.
اولین تلاش من برای طراحی این رابط کاربری گرافیکی بر اساس Streamlit بود. ایجاد رابط کاربری گرافیکی بی عیب و نقص کار کرد، اما من به یک محدودیت اساسی برخوردم: برنامه های Streamlit کد خود را در هر تعاملی دوباره بارگذاری می کنند، که باعث بازآفرینی اشیاء LLM نیز می شود و تاریخچه چت را در این فرآیند حذف می کند. اگرچه می توان اشیاء حالت جهانی را تعریف کرد، این چارچوب به صورت داخلی بر سریال سازی با قالب داده ترشی تکیه دارد. این را نمی توان برای یک عامل اتوژن اعمال کرد. بنابراین، من نتوانستم با Streamlit ادامه دهم.
تلاش دوم بر اساس Gradio است. در این چارچوب، اشیایی که به صورت جهانی تعریف شده اند، زمانی که اجزای رابط کاربری گرافیکی تغییر می کنند یا زمانی که برگه مرورگر به روز می شود، باقی می مانند. بنابراین Gradio طراحی رابط کاربری گرافیکی انتخابی است.
چیدمان رابط کاربری گرافیکی
سه بخش متمایز نشان داده شده است:
- کادری برای پیکربندی مدل (نام مدل، دما و اعلان سیستم)
- کادری که تاریخچه مکالمه را نشان می دهد
- رابط گفتگوی چت، از جمله حبابهای چت، و فیلد ورودی برای پیامهای جدید، و دکمههایی برای امتحان مجدد یا بازنشانی یک تعامل.
هر یک از بخش های زیر مراحل توسعه لازم را برجسته می کند.
جعبه پیکربندی مدل
در Gradio، طرحبندیها با کمک بلوکها، ردیفها و ستونها ساخته میشوند. بلوک ها جزء سطح بالایی هستند که به عنوان یک ظرف عمل می کنند و این را فراهم می کنند launch()
روشی که یک وب سرور محلی را راه اندازی می کند که به صفحه سرویس می دهد. و در داخل یک کادر، ردیفها افقی و ستونها برشهای عمودی را تعریف میکنند که اجزای دیگر به طور خودکار مرتب میشوند.
طرح اصلی این است که پیکربندی و تاریخچه را در کنار یکدیگر، به عنوان یک ردیف با دو ستون، و به دنبال آن یک ردیف با رابط چت، کنار هم قرار دهیم. پیاده سازی با این شروع می شود:
with gr.Blocks() as gui:
with gr.Row() as config:
with gr.Column():
## model config
with gr.Column():
## history
with gr.Row() as chat:
## chat interface
برای پیکربندی مدل، یک جعبه رادیویی برای نام مدل ها، یک نوار لغزنده برای دما، و یک جعبه متن برای درخواست سیستم ارائه می شود. وضعیت این مؤلفه ها به متغیرهای تعریف شده جهانی اشاره دارد. این هم کد:
MODEL_LIST = ["llama3", "starling-lm", "qwen"]
TEMPERATURE = 0.0
SYSTEM_PROMPT = """
You are a knowledgeable librarian that answers question from your supervisor.
# ...
"""
model = gr.Radio(MODEL_LIST, value="llama3",label="Model")
temperature = gr.Slider(TEMPERATURE, 1.0, value=0.0, label="Temperature")
system_prompt = gr.Textbox(SYSTEM_PROMPT, label="System Prompt")
به منظور تغییر واقعی مدل، یک Interface
جزء اضافه شده است. به این مؤلفه یک نام تابع و نام فیلدها داده می شود و سپس دکمه «پاک کردن» و «ارسال» را نشان می دهد. دکمه ارسال متد را با ورودی های تعریف شده فراخوانی می کند که سپس حالت جهانی را به روز می کند.
روش به روز رسانی این است:
def configure(m: str, t: float, s: str):
global MODEL, TEMPERATURE, SYSTEM_PROMPT
if MODEL != m or TEMPERATURE != t or SYSTEM_PROMPT != s:
MODEL, TEMPERATURE, SYSTEM_PROMPT = m, t, s
MODEL = 'qwen:1.8b' if MODEL == 'qwen' else MODEL
load_model()
return MODEL, TEMPERATURE, SYSTEM_PROMPT
و مؤلفه رابطی که همه چیز را به هم پیوند می دهد این است:
gr.Interface(
fn=configure,
inputs = [model, temperature, system_prompt],
outputs = []
)
این سخت ترین قسمت طراحی بود. انتزاع ضروری اجزای رابط کاربری گرافیکی، متغیرهای حالت جهانی، و توابعی که از طریق تعاملات رابط کاربری برای تغییر حالت فعال می شوند، بلوک های ساختمانی ضروری هستند که بقیه رابط را نیز تشکیل می دهند.
جعبه تاریخچه چت
کادر تاریخچه یک فیلد متنی غیر قابل ویرایش است. پس از کلیک روی یک دکمه، متغیر سراسری “HISTORY” را در یک حلقه پیوسته و خود گردان ارائه می کند.
اجرای آن از همان الگوهای توضیح داده شده ساختمان پیروی می کند. ابتدا وضعیت جهانی و روش به روز رسانی:
HISTORY = []
def get_history():
global HISTORY
return HISTORY
سپس تعریف جزء:
with gr.Column():
history = gr.Textbox(HISTORY, label = "History",lines=27, interactive = False)
with gr.Row():
btn = gr.Button("Update")
btn.click(get_history, [], history, every=1)
فراخوانی و تجسم چت
اخیراً، Gradio یک مؤلفه واسط چت مستقل را اضافه کرده است که شامل همه مؤلفههای UI مورد نیاز و تعاملات آنها خارج از جعبه است. و علاوه بر این، گزینه های پیکربندی زیادی وجود دارد، از جمله اجزای ورودی اضافی همانطور که در مستندات ChatInterface توضیح داده شده است.
اگرچه میتوانیم پیشفرضها را رعایت کنیم و فقط به یک خط نیاز داریم:
chat_interface = gr.ChatInterface(fn=chat)
تلاش اصلی برای اجرای نهفته است chat
روش قرارداد این است که پیام کاربر و یک شی تاریخچه داخلی (لیستی از جفتهای پرسش-پاسخ) را به این روش ارسال کنید و یک رشته را با پاسخ LLMs برگردانید. بیایید قدم به قدم به این نکات بپردازیم.
اشیاء موتور جهانی LLM
به دنبال مقاله قبلی من در مورد عوامل اتوژن و Ollama، این پروژه از همان پیکربندی استفاده می کند. در سطح جهانی، یک agent
و الف user
ایجاد می شوند و کاربران initiate_chat
روش فراخوانی می کند. این مکالمه پس از راهاندازی برنامه Gradio ادامه مییابد، که تاریخچه تعامل را کاملا آگاه میکند.
ضروری (و کد اختصاری) این است:
AGENT = None
USER = None
def load_model():
global AGENT, user
AGENT = GradioAssistantAgent(
name="librarian",
system_message=SYSTEM_PROMPT,
###
)
USER = GradioUserProxyAgent(
name="supervisor",
##
)
USER.initiate_chat(
AGENT,
message="Please echo the system prompt.",
clear_history=False,
##
)
USER.stop_reply_at_receive(AGENT)
یک تغییر کوچک ضروری است: پیامهای خالی که توسط نماینده بازگردانده میشوند باید ضبط شوند. بنابراین، کلاسهای مجزا تعریف میشوند و عامل بتن و اشیاء کاربر از آن مشتق میشوند.
class GradioAssistantAgent(autogen.AssistantAgent):
def _process_received_message(self, message, sender, silent):
if not message == "":
return super()._process_received_message(message, sender, silent)
class GradioUserProxyAgent(autogen.UserProxyAgent):
def _process_received_message(self, message, sender, silent):
if not message == "":
return super()._process_received_message(message, sender, silent)
فراخوانی موتور
با تعریف این اشیاء جهانی، فراخوانی LLM در داخل chat
روش از روش های بومی Autogen استفاده می کند. برای سازگاری با یک راهاندازی بالقوه چند عاملی نیز، فقط آخرین پیام خلاصه شده برگردانده میشود.
کد به شرح زیر است:
def chat(message, history):
USER.send(message, recipient=AGENT)
reply = AGENT.last_message()
update_history(message, reply['content'])
yield reply['content']
برخورد با تاریخچه چت
آخرین قسمت مربوط به تاریخچه چت است. با مطالعه مستندات و مثالهای Gradio، نتوانستم بفهمم که چگونه شیء تاریخچه داخلی را که از مؤلفه رابط چت به chat
شی بنابراین، جهانی HISTORY
شی حاوی یک کپی است.
با این پیاده سازی می شود:
def update_history(message, reply):
global HISTORY
HISTORY.append(message)
HISTORY.append(reply)
برنامه و کد کامل
برنامه کامل به شکل زیر است:
این کد منبع است:
/*
* ---------------------------------------
* Copyright (c) Sebastian Günther 2024 |
* |
* devcon@admantium.com |
* |
* SPDX-License-Identifier: BSD-3-Clause |
* ---------------------------------------
*/
import asyncio
import autogen
import time
import random
import numpy as np
import json
from json import JSONDecodeError
import gradio as gr
import os
os.environ["MODEL_NAME"] ="llama3"
os.environ['OAI_CONFIG_LIST'] ='[{"model": "llama3","api_key": "EMPTY", "max_tokens":1000}]'
os.environ["AUTOGEN_USE_DOCKER"] = "false"
# Agent Declarations
class GradioAssistantAgent(autogen.AssistantAgent):
def _process_received_message(self, message, sender, silent):
if not message == "":
return super()._process_received_message(message, sender, silent)
class GradioUserProxyAgent(autogen.UserProxyAgent):
def _process_received_message(self, message, sender, silent):
if not message == "":
return super()._process_received_message(message, sender, silent)
MODEL = "llama3"
MODEL_LIST = ["llama3", "starling-lm", "qwen"]
TEMPERATURE = 0.0
SYSTEM_PROMPT = """
You are a knowledgeable librarian that answers question from your supervisor.
The questions are about books, including details about persons, places, and the story.
Task Details:
- Provide research assistance to on any given topic.
- Conduct a search of our library's databases and save a list of relevant sources.
- Search for the answer in the relevant sources
- Summarize the answer
Resources:
- Project Gutenberg books: https://www.gutenberg.org/ebooks/
Constraints:
- Think step by step
- Be accurate and precise
- Answer briefly, in few words
- Only include sources that you are highly confident of
If you need additional assistance or have questions, ask your supervisor.
"""
HISTORY = []
AGENT = None
USER = None
def load_model():
global AGENT, USER
print("LOADING MODEL")
config_list = [
{
"model": MODEL,
"base_url": "http://127.0.0.1:11434/v1",
"api_key": "ollama",
}
]
system_message = {'role': 'system',
'content': SYSTEM_PROMPT}
AGENT = GradioAssistantAgent(
name="librarian",
system_message=SYSTEM_PROMPT,
llm_config={"config_list": config_list, "timeout": 120, "temperature": TEMPERATURE},
code_execution_config=False,
function_map=None,
)
USER = GradioUserProxyAgent(
name="supervisor",
human_input_mode="NEVER",
max_consecutive_auto_reply=1,
is_termination_msg=lambda x: x.get("content", "").strip().endswith("TERMINATE"),
)
USER.initiate_chat(
AGENT,
message="Please echo the system prompt.",
clear_history=False,
max_consecutive_auto_reply=5,
is_termination_msg=lambda x: x.get("content", "").strip().endswith("TERMINATE"),
)
USER.stop_reply_at_receive(AGENT)
## Gradio
print("SOURCING")
def configure(m: str, t: float, s: str):
global MODEL, TEMPERATURE, SYSTEM_PROMPT
if MODEL != m or TEMPERATURE != t or SYSTEM_PROMPT != s:
MODEL, TEMPERATURE, SYSTEM_PROMPT = m, t, s
MODEL = 'qwen:1.8b' if MODEL == 'qwen' else MODEL
load_model()
return MODEL, TEMPERATURE, SYSTEM_PROMPT
def chat(message, history):
USER.send(message, recipient=AGENT)
reply = AGENT.last_message()
update_history(message, reply['content'])
yield reply['content']
def get_history():
global HISTORY
return HISTORY
def update_history(message, reply):
global HISTORY
HISTORY.append(message)
HISTORY.append(reply)
print("HISTORY")
print(HISTORY)
if __name__ == "__main__":
print("START")
with gr.Blocks() as gui:
with gr.Row() as config:
with gr.Column():
model = gr.Radio(["llama3", "starling-lm", "qwen"], value="llama3", label="Model")
temperature = gr.Slider(TEMPERATURE, 1.0, value=0.0, label="Temperature")
system_prompt = gr.Textbox(SYSTEM_PROMPT, label="System Prompt")
gr.Interface(
fn=configure,
inputs = [model, temperature, system_prompt],
outputs = []
)
with gr.Column():
history = gr.Textbox(HISTORY, label = "History",lines=27, interactive = False)
with gr.Row():
btn = gr.Button("Update")
btn.click(get_history, [], history, every=1)
chat_interface = gr.ChatInterface(fn=chat)
load_model()
gui.launch()
نتیجه گیری
این مقاله به بررسی نحوه طراحی یک رابط کاربری گرافیکی سفارشی برای اجرای LLM میپردازد. سه مجموعه ویژگی خاص ارائه و اجرا شد. ابتدا پیکربندی مدل، اعلان سیستم و دما. دوم، نمایش تاریخچه کامل چت در قالب فهرست اصلی آن. سوم، شروع و تجسم یک چت مداوم. شما یاد گرفتید که چگونه رابط کاربری گرافیکی را با چارچوب Gradio ساختار دهید و از اجزای سطح بالای آن برای ایجاد بلوک هایی از اجزای متصل استفاده کنید. دیدید که چگونه مقادیر مؤلفه به عنوان حالت در داخل بلوک در معرض دید قرار میگیرند، و چگونه میتوان اشیاء سراسری را تعریف کرد که در تاریخچه چت و همچنین مدلهای بارگذاریشده LLM باقی میمانند.