見出し画像

FastChatサーバでLangchainを使いましょう。

OpenAIがDevDayで新機能の発表を行い、界隈では大騒ぎになっています。何でも出来てしまうわけで、「ローカルLLMを続ける意義はあるのでか」と問われると、色々理由をつけて「ある」と答えるのがロカールLLMを諦めずに触り続けている私達です。理由はともあれ、今回もロカールLLMで動かしたLangchainについて記事にしました。既に他の方々も記事を出されているので、この記事では動かし方とテストを説明します。

サーバを立てる

FastChatのOpenAI互換サーバの立て方は、以下の記事で解説しました。この記事にはllama-cpp-pythonの互換サーバいついても説明していますが、Langchaiのテストが上手く動きませんでした。多分、コードを少し変えれば動くんだろうと思いますが、今回はFastChatを使うので、調べていません。

もう一度おさらいです。FastChatは以下にサーバを動かすコマンドや、Langchainの動かし方も説明があります。

環境

環境構築はこちらで解説しています。minigpt4で構築した環境を引き継いでいます。面倒ですが順番に記事を追いかけてください。

git clone https://github.com/lm-sys/FastChat.git
conda activate minigpt4
cd FastChat
pip install --upgrade pip  # enable PEP 660 support
pip install -e ".[model_worker,webui]"

事前にCUDAのインストールは済ませましょう。モデルの準備も必要です。最初は自動的にダウンロードするはずです。
Vicuna-7B-v1.5 とVicuna-13B-v1.5が標準ですが、他のモデルも使えるようです。VRAMは7Bで9G強、13Bで16G強です。13Bだと通常のチャットは動きますが、Langchainのembeddingでは16GVRAMでも不足してエラーになります。

サーバを立ち上げるコマンド

Step1 コントローラを動かします。 ターミナルを開いて以下を実行

conda activate minigpt4
cd FastChat
python3 -m fastchat.serve.controller

Step2 モデルワーカを動かします。 新たにターミナルを開いて以下を実行

conda activate minigpt4
cd FastChat
python3 -m fastchat.serve.model_worker --model-names "gpt-3.5-turbo,text-davinci-003,text-embedding-ada-002" --model-path lmsys/vicuna-7b-v1.5 --load-8bit

Step4 OpenAI互換サーバの立上げ。新たにターミナルを開いて以下を実行
以下の例ではサーバはローカルで立ち上がります。

python3 -m fastchat.serve.openai_api_server --host localhost --port 8000

Langchainの準備

以下に説明があります。
新たに環境を作成してもいいと思います。

pip install langchain

簡単です。

テスト

1,embeddingのテスト

テストコードが公開されているので実行します。テキストファイルを読んで順次質問に答えるテストです。

事前にテスト用のテキストをダウンロード。どんな内容でも日本語でもいいです。テキストを変えたら質問もテキストに沿うように変更しましょう。

wget https://raw.githubusercontent.com/hwchase17/langchain/v0.0.200/docs/modules/state_of_the_union.txt

事前にOpenAI-BaceとKEYを設定します。KEYは何でもOKです。

export OPENAI_API_BASE=http://localhost:8000/v1
export OPENAI_API_KEY=EMPTY

コードを実行すると2つのエラーが出来ます。以下で事前にインストール。

pip install openai
pip install chromadb

コード

import os
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import TextLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.indexes import VectorstoreIndexCreator

def test_chain():
    embedding = OpenAIEmbeddings(model="text-embedding-ada-002")
    loader = TextLoader("state_of_the_union.txt")
    index = VectorstoreIndexCreator(embedding=embedding).from_loaders([loader])

    llm = ChatOpenAI(model="gpt-3.5-turbo")

    questions = [
        "Who is the speaker",
        "What did the president say about Ketanji Brown Jackson",
        "What are the threats to America",
        "Who are mentioned in the speech",
        "Who is the vice president",
        "How many projects were announced",
    ]

    for query in questions:
        print("Query:", query)
        print("Answer:", index.query(query, llm=llm))

if __name__ == "__main__":
    os.environ["OPENAI_API_BASE"] = "http://localhost:8000/v1"
    os.environ["OPENAI_API_KEY"] = "empty"
    test_chain()

2,メモリーのテスト

会話の履歴をメモるテストです。

import os
import json
from langchain.chat_models import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.schema import messages_to_dict
from langchain.prompts.chat import (
  ChatPromptTemplate,
  MessagesPlaceholder, 
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate,
)
from langchain.document_loaders import TextLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.indexes import VectorstoreIndexCreator

def test_chain():
  chat = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0.7)
  memory = ConversationBufferMemory(return_messages=True)

  with open("test_cli_inputs.txt") as f:
    template = f.read()
  prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(template),
    MessagesPlaceholder(variable_name="history"),
    HumanMessagePromptTemplate.from_template("{input}")
  ])
  conversation = ConversationChain(llm=chat, memory=memory, prompt=prompt)
  command = input("You: ")
#会話
  while True: 
    response = conversation.predict(input=command)
    print(f"AI: {response}")
    command = input("You: ")
    if command == "exit":
      break
    #履歴の確認
  history = memory.chat_memory
  messages = json.dumps(messages_to_dict(history.messages), indent=2, ensure_ascii=False)
  print(f"memory: {messages}")

if __name__ == "__main__":
  os.environ["OPENAI_API_BASE"] = "http://localhost:8000/v1"
  os.environ["OPENAI_API_KEY"] = "empty"
  test_chain()

まとめ

ローカルでLLMを高度に利用する手段としてLangchainは有効な手段だと思います。繰り返しますが、OpenAIのAPIを使えば敢えてローカルで動かす必要はなのですが、万が一に備えて常にフリーハンドは持っていたいものです。