3.1 🧠 Память в LangChain

💾 Добавим памяти LLM'ке! 🦙

Вы получаете таблицу с вопросами, ответить на некоторые из них возможно только если знаешь предыдущие. Вам нужно получить ответы на все вопросы с помощью LLM, добавив память для хранения контекста. Но не забудьте очищать память после завершения каждой серии вопросов, чтобы не запутать модель ещё больше  контекстом не по теме.

Что делать? Ответьте на вопросы из датафрейма

Что на входе?  Датасет с вопросами в столбце question и номером диалога в столбце dialogue_id.

Загрузка файлов с помощью кода
import pandas as pd
df = pd.read_csv("https://stepik.org/media/attachments/lesson/1084404/dial_df.csv")

Что на выходе? csv файл, содержащий три столбца - dialogue_id, question, answer.

В качестве ответа на каждый из вопросов нужно дать одно число.

Замечание: Ваше решение будет зачтено, если в нём будет минимум 15 правильных ответов.

Начать решать можно в этом ноутбуке!


Вдруг кто-то хочет opensource: QuantFactory/Meta-Llama-3-8B-Instruct-GGUF/Meta-Llama-3-8B-Instruct.Q6_K.gguf  6.6 GB
даёт 17 из 18. На вопросе про восьмитысячники глючит, пишет что это понятие из математики. вопрос лучше переформулировать чтобы было понятно, что речь идет про горы.

@Igor_Khmelkov, А как ты запустил? локально скачал? есть ноутбук решения?

@Дмитрий_Блинов, запускал локально через чат в LM Studio 0.2.23. И к стати можно память не очищать, всё работает. Можно попробовать с кодом, там есть встроенный локальный сервер, имитирующий API OpenAI. Пример кода оттуда: 

# Chat with an intelligent assistant in your terminal
from openai import OpenAI
# Point to the local server
client = OpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
history = [
    {"role": "system", "content": "You are an intelligent assistant. You always provide well-reasoned answers that are both correct and helpful."},
    {"role": "user", "content": "Hello, introduce yourself to someone opening this program for the first time. Be concise."},
]
while True:
    completion = client.chat.completions.create(
        model="model-identifier",
        messages=history,
        temperature=0.7,
        stream=True,
    )
    new_message = {"role": "assistant", "content": ""}
    for chunk in completion:
        if chunk.choices[0].delta.content:
            print(chunk.choices[0].delta.content, end="", flush=True)
            new_message["content"] += chunk.choices[0].delta.content
    history.append(new_message)
    print()
    history.append({"role": "user", "content": input("> ")})

@Igor_Khmelkov, Спасибо большое, очень важная информация для меня

Комментарий закреплён

Вы побили порог в 15: ваша точность 18 из 18.

Привет, наверно немного оверкил)

template = """Ответь на вопрос, в ответе используй только число без текста.
                Question: {q}
                Answer: В ответе только одно число, например, 4"""
                ans = []
                for id, df_ in df.groupby('dialogue_id'):
                    memory = ConversationBufferMemory()
                    conversation_buf_mem = ConversationChain(
                        llm=llm,
                        memory = memory,
                        verbose=True
                    )
                    for q in df_['question']:
                        response = conversation_buf_mem.invoke(template.format(q=q))
                        ans.append(response['response'])
                df['answer'] = ans

Вы побили порог в 15: ваша точность 18 из 18.

Не факт, что код является лаконичным и оптимизированным, но задачу им выполнил :)

 

    prompt_template = """Тебе будет задан вопрос, ответь на него.
                Текст: {text_input}
                Ответ: Твой ответ должен содержать только число, никаких больше слов и знаков быть не должно. 
                """
                    path = 'путь к файлу'
                    df = pd.read_csv(path)
                    answer_list = []  # Список, где будем хранить ответы модели
                    number_context = 1
                    for index, row in tqdm(df.iterrows(), total=df.shape[0]):
                        dialogue_id = row['dialogue_id']  # Получаем значение столбца dialogue_id
                        question = row['question']  # Получаем значение столбца question
                        if dialogue_id == number_context:
                            prompt = prompt_template.format(text_input=question)  # Формируем промпт, подставляя текст вопроса
                            response = conversation.invoke(prompt)  # Отправляем сформированный промпт в модель
                            answer_list.append(response["response"])
                        else:
                            conversation.memory.clear()
                            prompt = prompt_template.format(text_input=question)  # Формируем промпт, подставляя текст вопроса
                            response = conversation.invoke(prompt)  # Отправляем сформированный промпт в модель
                            answer_list.append(response["response"])
                            number_context += 1
                    df['answer'] = answer_list  # запишем предсказания в датафрейм
                    df.to_csv('solution.csv', index=False)  # сохраним решение в csv

Вы побили порог в 15: ваша точность 18 из 18.

# создайте цепочку диалога, в параметры передайте языковую модель и память

memory = ConversationBufferWindowMemory(k=3)

conversation_bufwin_mem = ConversationChain(

llm= llm,

memory = memory, # модуль памяти

verbose=True # отображать логи в консоли или нет

)

 

# ответьте на вопросы с помощью ЛЛМки

ans = []

for id, q in zip(df['dialogue_id'], df['question']):

    ans.append(conversation_bufwin_mem.invoke(input=q+' Ответь одним числом. Только числом.')['response'])

df['answer'] = ans # запишем предсказания в датафрейм

df['answer'] = df['answer'].astype(int)

df.to_csv('solution.csv', index=False)

Вы побили порог в 15: ваша точность 18 из 18.

ans = []

prev_id = df['dialogue_id'][0]

for id, q in zip(df['dialogue_id'], df['question']):

  if id != prev_id:

    conversation_buf_mem.memory.clear()

    response = conversation_buf_mem.invoke(f'Ответь на вопрос числом. Не пиши пояснения, пиши только четкое число цифрой, не пиши число словами, не ставь знаков припинания: {q}')['response']

    ans.append(response)

    prev_id = id

Вы побили порог в 15: ваша точность 17 из 18.

Здесь как раз можно использовать ConversationEntityMemory и игнорировать, что в dataframe есть dialogue_id

Вы побили порог в 15: ваша точность 16 из 18.

# Формируем prompt
from langchain import PromptTemplate

template = """The following is a friendly conversation between a human and an AI. 
The AI answer only in int format.
Current conversation:\n{history}\nHuman: {input}\nAI:"""

prompt_template = PromptTemplate(
    input_variables=['history', 'input'], 
    template=template
)


from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

# создаём цепочку диалога, в параметры передаём языковую модель и память
memory = ConversationBufferMemory()
conversation = ConversationChain(
    prompt=prompt_template,
    llm=llm,  # языковая модель
    memory = memory, # модуль памяти, теперь мы запоминаем
    # verbose=True # отображать логи в консоли или нет
)


from tqdm import tqdm
tqdm.pandas()

class DialogueProcessor:
    def __init__(self):
        self.current_dialogue_id = None
        self.model_answers = []  # Store model answers

    def create_answer(self, row):
        # Check if the dialogue_id has changed and reset the memory if needed
        if row['dialogue_id'] != self.current_dialogue_id:
            memory.clear()  # Clear memory if dialogue_id changes
            self.current_dialogue_id = row['dialogue_id']
        
        # Generate model answer based on the question
        model_answer = conversation.invoke(row['question'])['response']

        try:
            self.model_answers.append(int(model_answer))  # Store the answer
        except ValueError:
            self.model_answers.append(None)

    def process_dialogues(self, df):
        # Apply the function to each row with tqdm progress bar
        df.progress_apply(self.create_answer, axis=1)

# Example usage:
dialogue_processor = DialogueProcessor()

# Limit test sample
# dialogue_processor.process_dialogues(df.head(5))
dialogue_processor.process_dialogues(df)

 

Вы побили порог в 15: ваша точность 18 из 18.