Прежде всего, что такое LangChain? Это превосходный фреймворк для разработки приложений на основе языковых моделей. Один из нескольких способов, которыми вы, возможно, захотите использовать это, — это создание чат-бота. Если вы когда-либо пробовали OpenAI API, вы, возможно, знаете, что при использовании конечной точки ChatCompletion вам потребуется передать всю историю чата в API. Оболочка LangChain OpenAI позаботится об этом за вас, используя идею memory . Кроме того, вы также можете сгенерировать summary и добавить его в свой вызов API OpenAI, если вы не хотите передавать долгую историю чата или хотите сохранить некоторые из своих токенов.
В библиотеке Python для LangChain есть прекрасный встроенный класс под названием ConversationSummaryBufferMemory, который в итоге объединяет идею memory и summary. Он хранит буфер недавних взаимодействий в памяти, но вместо того, чтобы просто полностью сбрасывать старые взаимодействия, он компилирует их в сводку и использует оба в вызове API. Узнать больше об этой идее можно здесь.
Однако в большинстве случаев вы можете предпочесть использовать библиотеку TS/JS вместо Python при создании веб-приложения чат-бота. К сожалению, класс ConversationSummaryBufferMemory НЕТ!!! существуют в библиотеке TS/JS. В этой статье я покажу вам, как вы можете самостоятельно реализовать эту идею сохранения недавних взаимодействий и обобщения более старых, используя BufferWindowMemory и ConversationSummaryMemory, которые являются двумя встроенными классами в библиотеке TS/JS langChain.
Начиная!
Настраивать
Прежде чем мы начнем, убедитесь, что вы установили библиотеку langChain с помощью npm install -S langchain и импортировали необходимые библиотеки.
import { OpenAI } from "langchain/llms/openai";
import { BaseMessage, HumanMessage, AIMessage, SystemMessage } from "langchain/schema"
import { BaseChatMemory, ChatMessageHistory, ConversationSummaryMemory } from "langchain/memory"
import { SUMMARY_PROMPT } from "langchain/dist/memory/prompt";
import { BufferMemory, BufferWindowMemory } from "langchain/memory";
import { ConversationChain, LLMChain } from "langchain/chains";
import { ChatMessagePromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate, PromptTemplate, SystemMessagePromptTemplate } from "langchain/prompts";
На самом деле мы не используем весь импорт, но я просто помещаю его туда, чтобы вы могли быстро просмотреть некоторые возможные варианты.
Прежде всего, чтобы мы могли обобщать старые сообщения и сохранять новые как есть, нам нужно хранить все сообщения где-то в нашем приложении. Давайте создадим простой массив типа Message, в котором хранятся все прошлые Message .
export type Message = {
role: string // human or AI
content: string
}
const messages = [] Array<Message>
Здесь мы будем инициализировать пустой массив, но если у вас есть прошлые сообщения, которые вы храните в базе данных, вы определенно можете инициализировать их этими сообщениями.
Вспомогательные функции
Давайте сначала создадим вспомогательную функцию, которая преобразует имеющиеся у нас сообщения в объект ChatMessageHistory, чтобы мы могли быстро вызывать эту функцию всякий раз, когда мы хотим создать цепочку с прошлой историей или сделать некоторое обобщение для заданного набора сообщений. Это будет выглядеть следующим образом.
export const createChatMessageHistory = (messages: Array<Message>) => {
const history = new ChatMessageHistory()
messages.forEach((message)=> {
switch (message.role) {
case "human": {
history.addUserMessage(message.content)
break
}
case "AI": {
history.addAIChatMessage(message.content)
break
}
default: {
history.addMessage(new SystemMessage(message.content))
break
}
}
})
return history
}
Эта функция принимает массив Message в качестве входных данных и возвращает объект ChatMessageHistory, содержащий заданные сообщения, в качестве выходных данных.
У нас также будет вспомогательная функция, которая суммирует заданный массив Message.
const model = new OpenAI({
modelName: 'gpt-3.5-turbo',
temperature: 0,
openAIApiKey: OPENAI_API_KEY, //Remember to replace OPENAI_API_KEY with your own ones
})
export const summarizeMessages = async (messages: Array<Message>) => {
const summary_memory = new ConversationSummaryMemory({
llm: model,
chatHistory: createChatMessageHistory(messages)
})
const summary = await summary_memory.predictNewSummary(await summary_memory.chatHistory.getMessages(), "")
return summary
}
Цепочка бесед
Чтобы у нас была и сводка, и память, нам нужно создать цепочку с несколькими входами, используя template, которая выглядит следующим образом.
const template = `
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
System: {chat_summary}
{recent_chat_history}
Human: {human_input}
AI:`
chat_summary будет местом, где мы передаем сводку старых сообщений, а human_input будет новым сообщением чата. recent_chat_history будет автоматически управляться объектом BufferWindowMemory.
Давайте создадим наш ConversationChain с подсказкой шаблона.
Сначала нам нужно преобразовать строку шаблона в объект PromptTemplate.
const prompt = new PromptTemplate({
inputVariables: ["chat_summary", "human_input", "recent_chat_history"],
template: template,
})
Давайте также создадим наш объект BufferWindowMemory, который поможет нам отслеживать последние разговоры.
k = 2
const history = createChatMessageHistory(messages)
const memory = new BufferWindowMemory({
chatHistory: history,
memoryKey: "recent_chat_history",
inputKey:"human_input",
k: k,
returnMessages: false,
})
k — это количество разговоров туда и обратно, которые можно использовать в качестве памяти. Давайте оставим его маленьким, чтобы мы могли быстро визуализировать результат. memoryKey — это то место, где BufferWindowMemory добавит/обновит историю, а inputKey укажет, какие входные данные являются фактическими сообщениями чата, введенными пользователем, которые должны быть добавлены в историю.
СУПЕР ВАЖНО!!! Поскольку у нас есть несколько входных данных, inputKey должен быть указан!!!
Затем можно создать ConversationChain следующим образом.
const chain = new ConversationChain({
memory: memory,
verbose: true, // Just to print everything out so that we can see what is actually happening
llm: model,
prompt: prompt,
})
Отправить сообщение и начать чат
Теперь у нас есть все, что нам нужно, и мы готовы отправить несколько сообщений в OPENAI.
Помнить? То, что мы хотим отправить, — это комбинированное обобщение старых сообщений и новых. Вот как мы это сделаем.
export const sendMessage = async (pastMessages: Array<Message>, newMessage: Message, chain: ConversationChain) => {
var summary = ""
if (pastMessages.length > 2 * k) {
summary = await summarizeMessages(pastMessages.slice(0, -2*k))
}
console.log(`summary: ${summary} for messages til: ${(pastMessages.slice(0, -2*k)).slice(-1)}}`)
try {
const response = await chain.predict({
chat_summary: summary,
human_input: newMessage.content,
})
console.log(response)
return response
} catch (error) {
console.log(error)
throw error
}
}
Суммирование не производится, если длина прошлых сообщений меньше 2*k . Когда мы вызываем chain.predict, нам нужно включить как сводку, так и новое сообщение в качестве входных данных.
Используя приведенную выше функцию, теперь мы можем отправить сообщение, подобное следующему.
const newMessage: Message = {
role: "human",
content: "hey, how's going?"
}
const response = await sendMessage(messages, newMessage, chain)
const responseMessage = {
role: "AI",
content: response
}
messages.push(newMessage)
messages.push(responseMessage)
Поскольку всю историю сообщений мы храним сами, не забудьте добавить новое сообщение и ответ в массив.
Идеальный! Вот и все! Теперь вы можете общаться с OpenAI, обобщая старые разговоры и сохраняя недавние как есть! Надеюсь, это сэкономит вам немного жетонов и немного денег…
Спасибо за чтение! Хорошего хорошего дня!!!