LoginSignup
2
0

LangChain + ClaudeV3 + Streamlit サンプルコード

Last updated at Posted at 2024-05-07

やりたいこと

以前、練習目的で、あちらこちらのサイトを参考に ClaudeV2 + LangChain + Streamlitのサンプルコードを書いてみたことがある。
が、時代はあっという間にClaudeV3(半年もすればV4とかV5とかなってそうだが...)になり、LangChainも日進月歩で変わっているようなので、2024年4月時点での魚拓として作成してみた。

コード

いきなりだがコード。
なお下記コードを動作させるにあたっては、同僚M.O.氏に多大なるご助力をいただいた。この場を借りて感謝申し上げます。

streamlit-claudev3.py
import langchain
import streamlit as st
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_community.chat_message_histories import StreamlitChatMessageHistory
from boto3 import client
from botocore.config import Config
# これ要らなくなったかも
# from langchain.memory import ConversationBufferMemory

# BedrockChatは今後はChatBedrockに置き換わる模様
# 参考: https://python.langchain.com/docs/integrations/chat/bedrock/
from langchain_aws import ChatBedrock

# デバッグ用
langchain.verbose = True
langchain.debug = True

# 1. Streamlitセッションステートを使ってチャット履歴を格納
history = StreamlitChatMessageHistory(key="chat_messages")

# 2. Streamlitセッションステートを元にLangChainのチャット履歴を格納
# これ要らなくなったかも
# memory = ConversationBufferMemory(memory_key="history", chat_memory=history)

# 3. メッセージ履歴が空の場合、システムメッセージを追加
if len(history.messages) == 0:
    history.add_ai_message("ようこそ。どのようなご用件ですか?")

# 4. ClaudeV3から追加されたシステムプロンプトに基本的な指示を記述
# ここでは投資に関する質問には回答しないよう指示している
system_prompt = """You are an AI chatbot having a conversation with a human.
You answer always in japanese.
You must not answer questions on investment with the aim of making a profit.
"""

# 5. 最近のLangChainではPromptTemplateではなくChatPromptTemplateが使用される模様
# 前半にシステムプロンプト、後半にチャット履歴とヒューマンプロンプトを記載
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system_prompt),
        (
            "human",
            """
            Current conversation:
            <conversation_history>
            {history}
            </conversation_history>

            Here is the human's next reply:
            <human_reply>
            {human_input}
            </human_reply>
            """,
        ),
    ]
)

# 6. ChatBedrockでboto3を使えるようにしておく(必須ではない)
bedrock_client = client(
    service_name="bedrock-runtime",
    region_name="us-east-1",
    config=Config(read_timeout=300, retries={"max_attempts": 1}),
)

# 7. LLMをインスタンス化
# 最近はBedrockやBedrockChatの代わりにChatBedrockを使用するのがよいらしい
llm = ChatBedrock(
    model_id = "anthropic.claude-3-sonnet-20240229-v1:0",
    region_name = "us-east-1",
    verbose = True,
    model_kwargs = {
        "anthropic_version": "bedrock-2023-05-31",
        "temperature": 0.1,
        "top_p": 0.9,
        "top_k": 50,
        "max_tokens": 1000,
    },
#    client = bedrock_client,  # ここはオプション
)

# 8. チェインを作成し、以下を実行させる
# 8-1. ヒューマンプロンプトを処理
# 8-2. LLMに渡す
# 8-3. アウトプットをパースしAIレスポンスからcontent部を抽出
chain = prompt | llm | StrOutputParser()

for msg in history.messages:
    st.chat_message(msg.type).write(msg.content)

# 9. Streamlitを使ってUIを構築
if human_input := st.chat_input(key="input"):
    st.chat_message("human").write(human_input)

    # 9-1. LLMを実行
    # 最近はLLMChain()ではなくinvoke()を使う模様
    response = chain.invoke(
        {
            "history": history,
            "human_input": human_input
        }
    )

    # 9-2. ヒューマンプロンプト、AIレスポンスをチャット履歴に追加
    history.add_user_message(human_input)
    history.add_ai_message(response)

    # 9-3. AIレスポンスを表示
    st.chat_message("ai").write(response)

要点解説

モジュール変更

from langchain_aws import ChatBedrock

以前はfrom langchain.llms import Bedrockを使用していたが、これがBedrockChatに変わり、さらに個別モジュールに分離された上でChatBedrockに変わった。今後(しばらく)はこれが推奨値になると考えてよさそう。

システムプロンプト

system_prompt = """You are an AI chatbot having a conversation with a human.
You answer always in japanese.
You must not answer questions on investment with the aim of making a profit.
"""

LLMへの基本的な指示は、これまではコンテキスト内に入れてきたが、現在はシステムプロンプトとして分離する形が取れるようになっている。わかりやすくてよい。

メモリ作成

# memory = ConversationBufferMemory(memory_key="history", chat_memory=history)

従来はConversationBufferMemory()に会話履歴を代入していたが、試した限りStreamlitChatMessageHistory()を直接渡せているので、要らなくなった可能性が高い。

チェイン記載

chain = prompt | llm | StrOutputParser()

...

    response = chain.invoke(
        {
            "history": history,
            "human_input": human_input
        }
    )

従来は以下のようにチェインを記載していたが、パイプで繋いでパーサーに渡すし、その後invoke()メソッドを呼び出す形に変わった(もしくはそちらが主流となった)。

llm_chain = LLMChain(
    llm=Bedrock(
        model_id='anthropic.claude-v2:1',
        model_kwargs={
            "temperature":0.0,
            "max_tokens_to_sample": 500
        }
    ),
    prompt=prompt,
    memory=memory
)

まとめ

この短期間でBedrockBedrockChatChatBedrockと推奨の関数がめまぐるしく変遷していることからも分かるとおり、非常に変化が速い。このポストも時を置かず過去のものとなりそうだが、その時々に、同様のハマリに苦しんでいる方の一助となるであろうと信じて。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0