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

前回は必要最低限の機能を持たせたチャットボット「最小のChatbot」を実装しました。
引き続き、今回は「会話履歴つきChatbot」を作っていきます。

会話履歴つきChatbotとは?

会話履歴つきChatbotは、直前までのやり取りを覚えた状態で返答するchatbot です。

たとえば、最小の1問1答の chatbot だと、

  • ユーザー: 私は東京に住んでいます
  • ユーザー: おすすめの散歩場所は?

という 2回目の質問で、「東京に住んでいる」が引き継がれません。

一方、会話履歴つきChatbotでは、過去の発言もまとめてモデルに渡すので、

  • 「東京に住んでいるなら…」
  • 「さっき話していた内容を踏まえると…」

のように、文脈を保った会話 ができます。


最小のChatbotとの違い

結論、「過去の会話情報を持っているか」の違いがあります。

最小のChatbotは、以下をやっていました。

  1. ユーザー入力を受け取る
  2. モデルに送る
  3. 返答を表示する

会話履歴つきChatbot は、このようになります。

  1. ユーザー入力を受け取る
  2. 過去の会話履歴に追加する
  3. モデルに送る(履歴ごと
  4. 返答を表示する
  5. 返答も履歴に追加する

どういうデータを持てばよいか

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

messages = [
    {"role": "system", "content": "あなたは丁寧な日本語で答えるアシスタントです。"},
    {"role": "user", "content": "私は東京に住んでいます。"},
    {"role": "assistant", "content": "東京にお住まいなのですね。"},
    {"role": "user", "content": "おすすめの散歩場所は?"}
]

役割は主に 3 つです。

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

モデルは、この並びを見て「ここまでの会話」を理解します。


前回までのおさらい

構成

chatbot-test/
├─ .venv/
└─ app.py

コード

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"


def main():
    user_input = input("You: ")

    response = chat(
        model=MODEL_NAME,
        messages=[
            {"role": "user", "content": user_input}
        ]
    )

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


if __name__ == "__main__":
    main()

会話履歴つきChatbotの実装

前回のコードから何を変えるのか

今回の変更点は大きく3つです。

  1. 会話を1回で終わらせず、繰り返し会話できるようにする
  2. ユーザーの会話履歴をためる
  3. botの返答も履歴に追加する

会話を続けられるようにする

最初にやることは、入力と返信のやり取りを1回だけで終わらせないことです。
while True: で繰り返せるようにします。
また、ユーザーのインプットの改行除去や、会話の終了ができるようにもしておきます。

chatbot-test/app.py
from ollama import chat


MODEL_NAME = "gemma3"


def main():
    while True: # → 差分
        user_input = input("You: ").strip() # → 差分

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

        response = chat(
            model=MODEL_NAME,
            messages=[
                {"role": "user", "content": user_input}
            ]
        )

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


if __name__ == "__main__":
    main()

会話履歴を保存する(user)

次に、毎回その場で messages=[...] を作るのをやめて、
会話全体で使う messages を用意します。

ここが、会話履歴つき chatbot に変わる最初のポイントです。

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}")


if __name__ == "__main__":
    main()

この時点で、ユーザーの発言が蓄積される ようになります。
ただし、まだ bot の返答は履歴に入っていません。

bot の返答も履歴に追加する(assistant)

会話として成立させるには、assistant 側の返答も次回に引き継ぐ必要があります。

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()

ここまで来ると、前のやり取りを踏まえて返答する chatbot になります。
たとえば、

  • You: 私は東京に住んでいます
  • You: 有名な観光スポットを教えてください

のように送ると、2回目で東京の文脈を踏まえた返答が出やすくなります。

実際に起動してやってみると、、

ちゃんと文脈を理解して「東京」の観光スポットを教えてくれてますね!

おわり

いかがだったでしょうか。
今回は 会話履歴を覚えてもらうところ までを実装しました。

ここまでくると、ChatGPTみたいに会話ができるようになってきましたね…!

次回は「会話履歴つきChatbotの改善」をやっていきます。

お楽しみに〜