الگوهای طراحی عاملی با LLMها + پیاده سازی (پایتون)

در دنیای هوش مصنوعی، مدل های زبانی بزرگ (LLM) مانند GPT-3.5 و GPT-4 توانسته اند تحولی شگرف در پردازش زبان طبیعی ایجاد کنند. این مدل ها با دریافت ورودی متنی، خروجی هایی تولید می کنند که در بسیاری از موارد با پاسخ های انسانی برابری می کند. اما آیا می توان عملکرد این مدل ها را بهبود بخشید؟ پاسخ مثبت است؛ استفاده از رویکردهای عامل محور (Agentic) میتواند به طور قابل توجهی کارایی این مدل ها را افزایش دهد.

در روش های سنتی، مدل های زبانی به صورت «zero-shot» عمل می کنند. این روش شبیه به این است که از کسی بخواهیم بدون بازبینی و اصلاح، یک مقاله را از ابتدا تا انتها بنویسد. با اینکه مدل ها در این حالت عملکرد خوبی دارند، اما محدودیت هایی نیز وجود دارد. در مقابل، رویکردهای عامل محور به مدل اجازه می دهند تا به صورت تکراری و مرحله به مرحله به تولید و بهبود خروجی بپردازد.

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

تحقیقات نشان داده است که استفاده از این رویکردها می تواند بهبود چشمگیری در عملکرد مدل ها ایجاد کند. برای مثال، در آزمون HumanEval که برای ارزیابی توانایی مدل ها در تولید کد استفاده می شود، مدل GPT-3.5 در حالت صفر شات دقت ۴۸.۱٪ داشت. این دقت در مدل GPT-4 به ۶۷.۰٪ افزایش یافت. اما با به کارگیری رویکردهای عامل محور، دقت مدل GPT-3.5 به ۹۵.۱٪ رسید.

چهار الگوی اصلی در طراحی عامل محور وجود دارد که می تواند به بهبود عملکرد مدل های زبانی کمک کند:

بازتاب (Reflection): در این روش، مدل خروجی خود را بررسی کرده و به دنبال راه هایی برای بهبود آن می گردد. این فرایند شبیه به بازبینی و اصلاح متون توسط نویسندگان است.

استفاده از ابزارها (Tool Use): مدل به ابزارهایی مانند جستجوی وب، اجرای کد یا هر عملکرد دیگری دسترسی دارد که می تواند به جمع آوری اطلاعات، انجام اقدامات یا پردازش داده ها کمک کند. این قابلیت به مدل امکان می دهد تا با دسترسی به منابع خارجی، پاسخ های دقیق تری ارائه دهد.

برنامه ریزی (Planning): مدل می تواند یک برنامه چندمرحله ای برای دستیابی به هدف تعیین کند و آن را اجرا کند. برای مثال، در نوشتن یک مقاله، مدل می تواند ابتدا طرح کلی را ایجاد کرده، سپس تحقیق کند، پیشنویس بنویسد و در نهایت آن را بازبینی کند.

همکاری چندعاملی (Multi-agent Collaboration): در این رویکرد، چندین عامل هوش مصنوعی با هم همکاری میکنند، وظایف را تقسیم کرده و با بحث و تبادل نظر، به راه حل های بهتری نسبت به یک عامل واحد دست مییابند. این همکاری می تواند به تولید خروجی های با کیفیت تر منجر شود.

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

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



پیاده سازی الگوهای طراحی در تحلیل سهام

در این بخش، نحوه پیاده سازی الگوهای طراحی در یک گردش کار مرتبط با تحلیل سهام مورد بررسی قرار می گیرد. با بهره گیری از کتابخانه های Streamlit و Langchain، برنامه ای کاربردی توسعه داده می شود که قابلیت دریافت داده های زمانی سهام و نمایش آن ها به صورت نمودار خطی را داراست. تمامی کدهای مرتبط با این پروژه در (گیتهاب) در دسترس است و نسخه نهایی برنامه نیز از [اینجا] قابل مشاهده و آزمایش است.

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

  • historicalprice-tool: ابزاری برای دریافت قیمت های تاریخی یک یا چند سهام.
  • line-chart-tool: ابزاری برای نمایش داده های تاریخی به صورت نمودار خطی.

تعریف عامل برنامه ریز (Planner Agent)

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

تعریف سیستم پرامپت برای عامل برنامه ریز:

system = f&quot&quot&quotYou are a stock analyst assistant that gets the required parameters from the user and after generates a step by step plan in JSON format outlining the steps required to fulfill the user's request.

Tools:

{
    &quotname&quot: &quothistoricalprice-tool&quot,
    &quotdescription&quot: &quothistoricalprice-tool(symbol: str, days: str) -> str - Returns the price information of stock/stocks and corresponding timestamps for the last n days.&quot,
    &quotparameters&quot: {
        &quottype&quot: &quotobject&quot,
        &quotproperties&quot: {
            &quotsymbol&quot: {
                &quotdescription&quot: &quotThe stock symbol/symbols to get the historical price for. Can be a single stock symbol or multiple stock symbols separated by a comma. Example: AAPL,MSFT&quot,
                &quottype&quot: &quotstring&quot,
            },
            &quotdays&quot: {
                &quotdescription&quot: &quotThe number of days to get the historical price for.&quot,
                &quottype&quot: &quotstring&quot,
            },
        },
        &quotrequired&quot: [&quotsymbol&quot, &quotdays&quot],
    },
},
{
    &quotname&quot: &quotline-chart-tool&quot,
    &quotdescription&quot: &quotline-chart-tool(x_values: str, y_values: str, symbol: str) -> str - Plots a line chart to screen and also saves it under images\\ folder using the x and y values provided.\nSupports only one stock symbol at a time.&quot,
    &quotparameters&quot: {
        &quottype&quot: &quotobject&quot,
        &quotproperties&quot: {
            &quotx_values&quot: {
                &quotdescription&quot: &quotThe x values represented as a string. Example format: ['2024-04-02', '2024-04-03', '2024-04-04', '2024-04-05']&quot,
                &quottype&quot: &quotstring&quot,
            },
            &quoty_values&quot: {
                &quotdescription&quot: &quotThe y values represented as a string. Example format: [166, 168, 171, 164]&quot,
                &quottype&quot: &quotstring&quot,
            },
            &quotsymbol&quot: {
                &quotdescription&quot: &quotThe stock symbol to plot the line chart for.&quot,
                &quottype&quot: &quotstring&quot,
            },
        },
        &quotrequired&quot: [&quotx_values&quot, &quoty_values&quot, &quotsymbol&quot],
    },
}

## Instructions
- Keep asking questions to user until you get all the necessary information to generate your JSON plan.
- ALWAYS ask the user if they need at least one visual chart. If they explicitly state that they need visual chart then you don't need to ask. If the user doesn't want a visual chart NEVER include it in your JSON plan's steps.
- NEVER mention the tool names on your questions.
- If the user don't mention about the number of days, NEVER assume the number of days. Ask the user for the number of days.
- NEVER include any JSON object in your response before getting the necessary arguments and clarifications from the user.
- NEVER assume the user's answers or requests.
 Your JSON plan should have a single &quotuser_request_summary&quot key and &quotsteps&quot key containing a list of steps with ONLY &quotaction&quot and &quottool&quot keys for each step. &quotaction&quot key should contain the action to be taken and &quottool&quot key should only contain the tool name to be used for that action.
- You can mention the tool arguments in the &quotaction&quot key.
- Your JSON plan should contain as few steps as possible. You can merge the steps if possible.
- Always separate the steps if there are multiple stock symbols for line-chart-tool.
- NEVER use a tool name that is not provided. You can use &quotNone&quot for the tool name when you don't require a tool to complete that step. For example: If the user wants a markdown report, you can use &quotNone&quot for the tool name.

Hint: line-chart-tool can be called with historicalprice-tool's output. You don't need to ask line-chart-tool's inputs to user.

Begin! 

&quot&quot&quot

نکات کلیدی در تعریف عامل برنامه ریز:

  • باید از کاربر بپرسد که آیا نیاز به نمودار دارد یا خیر.
  • هرگز نباید نام ابزارها را در سوالات به کاربر بگوید.
  • نباید فرضی درباره تعداد روزها یا سایر ورودی ها داشته باشد.
  • ساختار JSON باید شامل کلیدهای "user_request_summary" و "steps" باشد که در هر گام، کلیدهای "action" و "tool" وجود دارد.

نمونه خروجی عامل برنامه ریز

برای درخواست کاربر:

What is TSLA stock price for the last 10 days? Please also plot a line chart for it.

عامل برنامه ریز خروجی زیر را تولید می کند:

{
    &quotuser_request_summary&quot: &quotدریافت اطلاعات قیمت تاریخی سهام TSLA برای 10 روز گذشته و نمایش آن به صورت نمودار خطی.&quot,
    &quotsteps&quot: [
        {
            &quotaction&quot: &quotدریافت اطلاعات قیمت تاریخی سهام TSLA برای 10 روز گذشته&quot,
            &quottool&quot: &quothistoricalprice-tool&quot
        },
        {
            &quotaction&quot: &quotنمایش نمودار خطی با استفاده از اطلاعات قیمت تاریخی&quot,
            &quottool&quot: &quotline-chart-tool&quot
        }
    ]
}

تعریف عامل اجرایی وظایف (Task Executor Agent)

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

تعریف سیستم پرامپت برای عامل اجرایی:

system_prompt = f&quot&quot&quotYou are a helpful assistant.
You can use the following actions:

{
    &quotname&quot: &quothistoricalprice-tool&quot,
    &quotdescription&quot: &quothistoricalprice-tool(symbol: str, days: str) -> str - Returns the price information of stock/stocks and corresponding timestamps for the last n days.&quot,
    ...
},
{
    &quotname&quot: &quotline-chart-tool&quot,
    &quotdescription&quot: &quotline-chart-tool(x_values: str, y_values: str, symbol: str) -> str - Plots a line chart to screen and also saves it under images\\ folder using the x and y values provided.\nSupports only one stock symbol at a time.&quot,
    ...
}

Accomplish the task in steps. If you get error in previous step fix it in the current step. Use the following format:

Step 1: The action in the JSON plan
Plan: Your plan for this step and the next step
Action: the action to take, should be one of ['historicalprice-tool', 'line-chart-tool', 'None'].
Input: the input to the action, should be a valid JSON object
Observation: the result of the action

...

Once you have completed all the steps, your final answer should be in the format:
Final Answer: the final answer to the user_request_summary

Begin! Reminder to use tools if necessary. When you want to respond directly, you can choose 'None' as the action. NEVER assume the user's input. 

&quot&quot&quot

نکات کلیدی در تعریف عامل اجرایی:

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

تابع اصلی اجرا

تابع run مسئول اجرای مراحل برنامه است. این تابع از گزارش JSON تولید شده توسط عامل برنامه ریز استفاده می کند و مراحل را یکی یکی اجرا می کند. همچنین دارای یک حلقه بازخورد برای مدیریت خطاها است.

def run(content):
   tools = [&quothistoricalprice-tool&quot, &quotline-chart-tool&quot, &quotnone&quot]
    count = 0
    previous_step_number = 0
    expander = None
    isfinalanswer = False
    steps_size = get_steps_size(content)
    while(True):
        print(&quotPrevious step number: &quot, previous_step_number)
        count += 1
        if count > 9:
            raise ValueError(&quotToo many steps&quot)
        output = generate_response_executer(system_prompt, content)
        ...

ویژگی های تابع run:

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

تعریف توابع ابزارها

دو تابع اصلی به عنوان ابزارها تعریف شده اند که توسط عامل اجرایی استفاده می شوند:

  1. get_historical_price: این تابع با استفاده از کتابخانه yfinance، قیمت های تاریخی سهام را دریافت می کند.
import yfinance as yf

class gethistoricalprice(BaseModel):
    symbol: str = Field(description=&quotThe stock symbol/symbols to get the historical price for...&quot)
    days: str = Field(description=&quotThe number of days to get the historical price for.&quot)

@tool(&quothistoricalprice-tool&quot, args_schema=gethistoricalprice)
def get_historical_price(symbol: str, days: str) -> str:
    &quot&quot&quot
    دریافت اطلاعات قیمت تاریخی سهام برای تعداد روزهای مشخص.
    &quot&quot&quot
    end_date = datetime.now()
    start_date = end_date - relativedelta(days=int(days))
    data = yf.download(symbol, start=start_date.strftime('%Y-%m-%d'), end=end_date.strftime('%Y-%m-%d'))
    data = data.dropna()
    close_prices = data['Close'].astype(int).to_dict()
    return str(close_prices)

plot_line_chart: این تابع با استفاده از کتابخانه های plotly و streamlit، نمودار خطی قیمت های سهام را رسم می کند.

import plotly.graph_objects as go
import ast

class plotlinechart(BaseModel):
    x_values: str = Field(description=&quotThe x values represented as a string...&quot)
    y_values: str = Field(description=&quotThe y values represented as a string...&quot)
    symbol: str = Field(description=&quotThe stock symbol to plot the line chart for.&quot)

@tool(&quotline-chart-tool&quot, args_schema=plotlinechart)
def plot_line_chart(x_values: str, y_values: str, symbol: str) -> str:
    &quot&quot&quot
    رسم نمودار خطی و ذخیره آن در پوشه images.
    &quot&quot&quot
    x_values = ast.literal_eval(x_values)
    y_values = ast.literal_eval(y_values)
    fig = go.Figure(data=go.Scatter(x=x_values, y=y_values, mode='lines'))
    fig.update_layout(xaxis_title=&quotDate&quot, yaxis_title=f&quotPrice of {symbol}&quot)
    st.plotly_chart(fig)

    if not os.path.exists('images'):
        os.makedirs('images')

    filename = f&quotimages/fig_{uuid.uuid4().hex}.png&quot
    fig.write_image(filename)

   return f&quotLine chart has been plotted to screen and saved successfully to {filename}&quot

تولید پاسخ با استفاده از مدل زبان بزرگ (LLM)

برای تولید پاسخ ها و اجرای دستورات، از یک مدل زبان بزرگ استفاده شده است. در این مثال، از مدل llama3-70b استفاده شده است، اما می توان از هر مدل دیگری نیز بهره برد.

def createclient():
    client = Groq()
    return {&quotclient&quot: client, &quotmodel&quot: &quotllama3-70b-8192&quot}

def generate_response_planner(system_message, appended_messages, stop=None):
    messages = [
        {&quotrole&quot: &quotsystem&quot, &quotcontent&quot: system_message},
        *appended_messages
    ]
    clientdict = createclient()
    client = clientdict[&quotclient&quot]
    model = clientdict[&quotmodel&quot]

    resp = client.chat.completions.create(
        model=model,
        messages=messages,
        stop=stop,
        temperature=0.2,
        max_tokens=1000
    )
    return resp

def generate_response_executer(system_message, user_input):
    messages = [
        {&quotrole&quot: &quotsystem&quot, &quotcontent&quot: system_message},
        {&quotrole&quot: &quotuser&quot, &quotcontent&quot: user_input}
    ]
    clientdict = createclient()
    client = clientdict[&quotclient&quot]
    model = clientdict[&quotmodel&quot]

    completion = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
        stop=[&quotObservation:&quot]
    )
    return completion.choices[0].message.content

نمونه خروجی:

همانطور که ملاحظه می کنید در خروجی کار که با استفاده از: "streamlit run main.py" می توانیم آن را ببینیم، یک صفحه کاربری وب را داریم که توسط streamlit نوشته شده است که شامل دو قسمت است، یک قسمت که selectbox هست و می توان سوالات آماده را انتخاب کرد، در قسمت پایین هم یک user input داریم که کاربر بتواند سوال خود را بپرسد.

در اینجا ما یک سوال آماده را انتخاب کردیم و تا سوالمان را جواب دهد؛ اما همانطور که می بینید در ادامه سوالات با جزییات بیشتری می پرسد تا بتواند پاسخ دقیق تری را ارائه دهد.

در سمت چپ قسمت message history را داریم که اگر کاربر clear message history را بزند لیست پیام ها و سوال هایی که قبلا پرسیده است خالی می شود.

در ادامه نکته ای در رابطه با نحوه استفاده از reflection در خروجی کار هست. همانطور که میدانید بازخورد خودکار باعث می شود تا اگر خروجی نیاز به اصلاح داشت آنرا بطور خودکار تغییر می دهد و اصلاح می کند و این نتیجه کاملا قابل مشاهده است.

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


منابع medium.com | github.com | deeplearning.ai