ローカルでChatbotを作ってみよう4(会話履歴つきChatbotの改善)

前回は、「会話履歴つきChatbot」を実装しました。
今回はこのチャットボットを改善していきます。

何を改善する?

botの性格や役割を追加しよう

前回の記事では、以下のように記載していました。

一般的には、会話履歴を messages という配列で持ちます。
役割は主に 3 つです。

  • assistant:chatbot の返答
  • system:chatbot の振る舞い
  • user:ユーザーの発言

この「system」について触れていなかったので、こちらを追加していこうと思います。

履歴が長くなりすぎないようにする

会話が長くなると、ローカルモデルでは重くなりやすいという問題があります。
そのため、会話をリセットする直近の履歴だけ送る ことで履歴を長くなりすぎないように対策することができます。


前回までのおさらい

コード

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"


def main():
    messages = []

    while True:
        user_input = input("You: ").strip()

        if user_input in ["exit", "quit", "終了"]:
            print("Bot: 終了します。")
            break

        messages.append({"role": "user", "content": user_input})

        response = chat(
            model=MODEL_NAME,
            messages=messages
        )

        answer = response["message"]["content"]
        print(f"Bot: {answer}")

        messages.append({"role": "assistant", "content": answer})


if __name__ == "__main__":
    main()

botの性格や役割を追加しよう

会話履歴を管理している配列 messages に、system メッセージを追加することで、
bot の性格や役割を固定することができます。

今回は「日本の観光ガイド」という役割を振ってみます。

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"
SYSTEM_PROMPT = "あなたは丁寧でわかりやすい日本の観光ガイドです。" # → 差分


def main():
    messages = [ # → 差分
        {"role": "system", "content": SYSTEM_PROMPT} # → 差分
    ] # → 差分

    while True:
        user_input = input("You: ").strip()

        if user_input in ["exit", "quit", "終了"]:
            print("Bot: 終了します。")
            break

        messages.append({"role": "user", "content": user_input})

        response = chat(
            model=MODEL_NAME,
            messages=messages
        )

        answer = response["message"]["content"]
        print(f"Bot: {answer}")

        messages.append({"role": "assistant", "content": answer})


if __name__ == "__main__":
    main()

早速、「これから東京に行きます」とだけ伝えたらどのような回答が来るでしょうか…?

観光ガイドとして、「目的」「旅行期間」「誰と」など、旅行計画を明確にするための質問をしてくれるようになりました!


履歴が長くなりすぎないようにする

会話をリセット

現状の会話履歴つきChatbotは、前の文脈をずっと引き継いで回答してくれます。
話を変えたい時など、途中でリセットしたくなることもあると思います。

そのため、コマンド「/reset」を1つ追加します。

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"
SYSTEM_PROMPT = "あなたは丁寧でわかりやすい日本の観光ガイドです。"


def main():
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT}
    ]

    while True:
        user_input = input("You: ").strip()

        if user_input in ["exit", "quit", "終了"]:
            print("Bot: 終了します。")
            break

        if user_input == "/reset": # → 差分
            messages = [ # → 差分
                {"role": "system", "content": SYSTEM_PROMPT} # → 差分
            ] # → 差分
            print("Bot: 会話履歴をリセットしました。") # → 差分
            continue # → 差分

        messages.append({"role": "user", "content": user_input})

        response = chat(
            model=MODEL_NAME,
            messages=messages
        )

        answer = response["message"]["content"]
        print(f"Bot: {answer}")

        messages.append({"role": "assistant", "content": answer})


if __name__ == "__main__":
    main()

会話履歴のリセットをすることで、会話が変わるかを確認してみます。

質問:東京に行きます

回答:渋谷、浅草〜

質問:紹介してくれたのはどこ?

回答:東京だよ!

(会話履歴リセット)

質問:紹介してくれたのはどこ?

回答:何も紹介してないよ!?

となっているので、ちゃんとリセットできていそうですね…!

直近の履歴だけ送る

会話が長くなりすぎてローカルモデルでは重くならないように、直近の履歴だけモデルに送るように改善していきます。

以下の流れで改善していきます。

  1. どれくらいの会話を保存するのか「最大の履歴数」を決める
  2. 最大の履歴数を超えた古い会話がモデルに送られないようにする

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"
SYSTEM_PROMPT = "あなたは丁寧でわかりやすい日本の観光ガイドです。"
MAX_HISTORY = 10 # → 差分


def trim_messages(messages): # → 差分
    system_message = messages[0] # → 差分
    conversation = messages[1:] # → 差分
    trimmed_conversation = conversation[-MAX_HISTORY:] # → 差分
    return [system_message] + trimmed_conversation # → 差分


def main():
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT}
    ]

    while True:
        user_input = input("You: ").strip()

        if user_input in ["exit", "quit", "終了"]:
            print("Bot: 終了します。")
            break

        if user_input == "/reset":
            messages = [
                {"role": "system", "content": SYSTEM_PROMPT}
            ]
            print("Bot: 会話履歴をリセットしました。")
            continue

        messages.append({"role": "user", "content": user_input})

        response = chat(
            model=MODEL_NAME,
            messages=trim_messages(messages) # → 差分
        )

        answer = response["message"]["content"]
        print(f"Bot: {answer}")

        messages.append({"role": "assistant", "content": answer})


if __name__ == "__main__":
    main()

サンプルとして最大履歴数を2にして(MAX_HISTORY = 2)試してみると、、、

しっかり忘れてくれていますね…!
会話履歴の変化は以下のとおりです。

質問1:私は男性です

回答1:わかりました(会話履歴:質問1)

質問2:私の性別は?(会話履歴:質問1、回答1)

回答2:わかりません(会話履歴:回答1、質問2)

このように、回答2では「質問1」は会話履歴から取り除かれているため、性別を覚えていません。


おわり

いかがだったでしょうか。
今回は botの性格や役割を与える、履歴を調整する 改善を行いました。

動くchatbotを作ることで、なんとなく雰囲気が掴めてきたのではないでしょうか…?

次回は「chatbotの仕組みの理解を深めよう」をやっていきます。

お楽しみに〜