外部検索、履歴を踏まえた応答するChatをlangchain(LCEL)で実装

書籍[「ChatGPT/LangChainによるチャットシステム構築」](https://amzn.to/3xYzEZ3)の 第6章 **外部検索、履歴を踏まえた応答をするWebアプリの実装**を試していたのですが、LangChainの書き方を最新のお作法に則っていたら大きく変える必要があったので、ここに記録しておきます。
[LangChain公式のStreamlit](https://python.langchain.com/docs/integrations/memory/streamlit_chat_message_history/)にあるサンプルを元に[書籍のコード](https://github.com/yoshidashingo/langchain-book/blob/main/chapter6/app.py)の要素を追加しています。


# サマリ
今回使っている主なモデルなどです。

|種類|内容|備考|
|:-:|:--|:--|
|Chat Model|[ChatOpenAI](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html)|今回のLLM(GPT3.5-Turbo)
|Prompt Template|[hwchase17/openai-functions-agent](https://smith.langchain.com/hub/hwchase17/openai-functions-agent)|会話履歴ありのOpen AI Function Agent
|Tool|[DuckDuckGo Search](https://python.langchain.com/docs/integrations/tools/ddg/)|検索エンジンとして使用
|Tool|[Wikipedia](https://python.langchain.com/docs/integrations/tools/wikipedia/)|Wiki検索に使用
|Callback|[StreamlitCallbackHandler](https://api.python.langchain.com/en/latest/callbacks/langchain_community.callbacks.streamlit.streamlit_callback_handler.StreamlitCallbackHandler.html)|StreamlitへCallback

# 動作
RAGで[DuckDuckGo Search](https://python.langchain.com/docs/integrations/tools/ddg/)と[Wikipedia](https://python.langchain.com/docs/integrations/tools/wikipedia/)から情報取得してLLMに最終回答させます。

## 画面
2024/5時点でGPT3.5 Turboには漫画「極東ネクロマンス」の情報はありませんが、Wikipediaから情報取得して回答しています。また、「今日の東京の天気は？」という問いに対しては、DuckDuckGo Search で情報取得して回答。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/68a9ef72-8c12-4b97-a352-bfce70be3967.png)

試しにChatGPTで同じ質問を投げても欲しい回答が返ってきません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/9b771f19-4a25-f614-fcff-6dc894a4f71f.png)

## 動作の内部的な動き
各Chatのやり取りごとに、LangSmithとターミナルに出力されたログを見ていきます。細かい動きを知るためにLangSmithが非常に便利でした。環境変数設定するだけで自動で出力してくれますし。
※何度か別ログを取得しているので細かい表現や時間など少し異なりますが、基本的にほぼ同じです。

### 1. 「極東ネクロマンスって何？」
LangSmithの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/4d951e65-4492-f73b-c9a0-c95b4390472c.png)

各内容
|種別|内容|入力|出力|
|:-:|:--|:--|:--|
|1. insert_history|会話履歴へ入力内容挿入|Promptに入力した内容|なし|
|2. ChatPromptTemplate|TemplateからPrompt生成|Template内変数|Prompt|
|3. ChatOpenAI|GPT3.5へ問い合わせ|Prompt|Toolのwikipediaを使えという指示(検索語句も)|
|4. wikipedia|Wikipedia検索|検索語句|Wikipediaの検索結果情報|
|5. ChatPromptTemplate|TemplateからPrompt生成|今までの情報|Prompt|
|6. ChatOpenAI|GPT3.5へ問い合わせ|Prompt|最終回答

<details><summary>詳細ログ(1. 「極東ネクロマンスって何？」)</summary>

```terminal: 詳細ログ(1. 「極東ネクロマンスって何？」)
[chain/start] [1:chain:RunnableWithMessageHistory] Entering Chain run with input:
{
  "input": "極東ネクロマンス って何？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] Entering Chain run with input:
{
  "input": "極東ネクロマンス って何？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] Entering Chain run with input:
{
  "input": "極東ネクロマンス って何？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] Entering Chain run with input:
{
  "input": "極東ネクロマンス って何？"
}
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] [4ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] [8ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] Entering Chain run with input:
[inputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] Entering Chain run with input:
{
  "input": ""
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] [2ms] Exiting Chain run with output:
{
  "output": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] [3ms] Exiting Chain run with output:
{
  "agent_scratchpad": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] [6ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "System: You are a helpful assistant\nAI: How can I help you?\nHuman: 極東ネクロマンス って何？"
  ]
}
[llm/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] [992ms] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "",
        "generation_info": {
          "finish_reason": "function_call"
        },
        "type": "ChatGenerationChunk",
        "message": {
          "lc": 1,
          "type": "constructor",
          "id": [
            "langchain",
            "schema",
            "messages",
            "AIMessageChunk"
          ],
          "kwargs": {
            "content": "",
            "example": false,
            "additional_kwargs": {
              "function_call": {
                "arguments": "{\"__arg1\":\"極東ネクロマンス\"}",
                "name": "wikipedia"
              }
            },
            "tool_call_chunks": [],
            "response_metadata": {
              "finish_reason": "function_call"
            },
            "id": "run-be86955a-e729-403c-bb1f-5fe2be2856d7",
            "tool_calls": [],
            "invalid_tool_calls": []
          }
        }
      }
    ]
  ],
  "llm_output": null,
  "run": null
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] [1ms] Exiting Parser run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] [1.01s] Exiting Chain run with output:
[outputs]
Error in StreamlitCallbackHandler.on_agent_action callback: RuntimeError('Current LLMThought is unexpectedly None!')
[tool/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 13:tool:wikipedia] Entering Tool run with input:
"極東ネクロマンス"
[tool/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 13:tool:wikipedia] [3.85s] Exiting Tool run with output:
"Page: Weekly Shōnen Jump
Summary: Weekly Shōnen Jump (Japanese: 週刊少年ジャンプ, Hepburn: Shūkan Shōnen Janpu, stylized in English as WEEKLY JUMP) is a weekly shōnen manga anthology published in Japan by Shueisha under the Jump line of magazines. The manga series within the magazine consist of many action scenes and a fair amount of comedy. Chapters of the series that run in Weekly Shōnen Jump are collected and published in tankōbon volumes under the Jump Comics imprint every two to three months. It is one of the longest-running manga magazines, with the first issue being released with a cover date of August 1, 1968.
The magazine has sold over 7.5 billion copies since 1968, making it the best-selling comic/manga magazine, ahead of competitors such as Weekly Shōnen Magazine and Weekly Shōnen Sunday. The mid-1980s to the mid-1990s represents the era when the magazine's circulation was at its highest, 6.53 million copies per week, with a total readership of 18 million people in Japan. Throughout 2021, it had an average circulation of over 1.3 million copies per week. Many of the best-selling manga series originate from Weekly Shōnen Jump.
Weekly Shōnen Jump has sister magazines such as Jump SQ, V Jump, Saikyō Jump, and digital counterpart Shōnen Jump+ which boasts its own exclusive titles. The magazine has also had several international counterparts, including the current North American Weekly Shonen Jump. It also spawned a crossover media franchise including anime and video games (since Famicom Jump) which bring together various Shōnen Jump characters.

Page: List of series run in Weekly Shōnen Jump
Summary: This is a list of the series that have run in the Shueisha manga anthology book Weekly Shōnen Jump. This list is organized by decade and year of each series' first publication, and lists every single notable series run in the manga magazine, along with the author of each series and the series' finishing date if applicable."
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad> > 17:chain:RunnableLambda] Entering Chain run with input:
{
  "input": ""
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad> > 17:chain:RunnableLambda] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad>] [2ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad>] [5ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 18:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 18:prompt:ChatPromptTemplate] [0ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 19:llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "System: You are a helpful assistant\nAI: How can I help you?\nHuman: 極東ネクロマンス って何？\nAI: {'arguments': '{\"__arg1\":\"極東ネクロマンス\"}', 'name': 'wikipedia'}\nFunction: Page: Weekly Shōnen Jump\nSummary: Weekly Shōnen Jump (Japanese: 週刊少年ジャンプ, Hepburn: Shūkan Shōnen Janpu, stylized in English as WEEKLY JUMP) is a weekly shōnen manga anthology published in Japan by Shueisha under the Jump line of magazines. The manga series within the magazine consist of many action scenes and a fair amount of comedy. Chapters of the series that run in Weekly Shōnen Jump are collected and published in tankōbon volumes under the Jump Comics imprint every two to three months. It is one of the longest-running manga magazines, with the first issue being released with a cover date of August 1, 1968.\nThe magazine has sold over 7.5 billion copies since 1968, making it the best-selling comic/manga magazine, ahead of competitors such as Weekly Shōnen Magazine and Weekly Shōnen Sunday. The mid-1980s to the mid-1990s represents the era when the magazine's circulation was at its highest, 6.53 million copies per week, with a total readership of 18 million people in Japan. Throughout 2021, it had an average circulation of over 1.3 million copies per week. Many of the best-selling manga series originate from Weekly Shōnen Jump.\nWeekly Shōnen Jump has sister magazines such as Jump SQ, V Jump, Saikyō Jump, and digital counterpart Shōnen Jump+ which boasts its own exclusive titles. The magazine has also had several international counterparts, including the current North American Weekly Shonen Jump. It also spawned a crossover media franchise including anime and video games (since Famicom Jump) which bring together various Shōnen Jump characters.\n\nPage: List of series run in Weekly Shōnen Jump\nSummary: This is a list of the series that have run in the Shueisha manga anthology book Weekly Shōnen Jump. This list is organized by decade and year of each series' first publication, and lists every single notable series run in the manga magazine, along with the author of each series and the series' finishing date if applicable."
  ]
}
[llm/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 19:llm:ChatOpenAI] [4.92s] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。",
        "generation_info": {
          "finish_reason": "stop"
        },
        "type": "ChatGenerationChunk",
        "message": {
          "lc": 1,
          "type": "constructor",
          "id": [
            "langchain",
            "schema",
            "messages",
            "AIMessageChunk"
          ],
          "kwargs": {
            "content": "「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。",
            "example": false,
            "additional_kwargs": {},
            "tool_call_chunks": [],
            "response_metadata": {
              "finish_reason": "stop"
            },
            "id": "run-3c6543c3-369c-4fad-a15c-76edfd5190b6",
            "tool_calls": [],
            "invalid_tool_calls": []
          }
        }
      }
    ]
  ],
  "llm_output": null,
  "run": null
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 20:parser:OpenAIFunctionsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 20:parser:OpenAIFunctionsAgentOutputParser] [1ms] Exiting Parser run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence] [4.93s] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] [9.81s] Exiting Chain run with output:
{
  "output": "「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。"
}
[chain/end] [1:chain:RunnableWithMessageHistory] [9.83s] Exiting Chain run with output:
[outputs]
```
</details>

### ２. 「さっきの回答を３０文字に要約して。」
今度は、Toolを使わずにいきない最終回答を出してくれますね。

LangSmithの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/9dfffc20-b6c9-39aa-66bf-e596f388c896.png)

各内容
|種別|内容|入力|出力|
|:-:|:--|:--|:--|
|1. insert_history|会話履歴へ入力内容挿入|Promptに入力した内容|なし|
|2. ChatPromptTemplate|TemplateからPrompt生成|Template内変数|Prompt|
|3. ChatOpenAI|GPT3.5へ問い合わせ|Prompt|最終回答|

<details><summary>詳細ログ（２.さっきの回答を３０文字に要約して。）</summary>

```terminal: 詳細ログ(（２.さっきの回答を３０文字に要約して。)
[chain/start] [1:chain:RunnableWithMessageHistory] Entering Chain run with input:
{
  "input": "さっきの回答を要約して"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] Entering Chain run with input:
{
  "input": "さっきの回答を要約して"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] Entering Chain run with input:
{
  "input": "さっきの回答を要約して"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] Entering Chain run with input:
{
  "input": "さっきの回答を要約して"
}
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] [4ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] [11ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] Entering Chain run with input:
[inputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] Entering Chain run with input:
{
  "input": ""
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] [1ms] Exiting Chain run with output:
{
  "output": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] [2ms] Exiting Chain run with output:
{
  "agent_scratchpad": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] [5ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "System: You are a helpful assistant\nAI: How can I help you?\nHuman: 極東ネクロマンス って何？\nAI: 「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。\nHuman: さっきの回答を要約して"
  ]
}
[llm/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] [2.30s] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "「極東ネクロマンス」は週刊少年ジャンプに掲載されている漫画作品で、アクションとコメディ要素を含む人気のシリーズです。週刊少年ジャンプは集英社が発行しており、多くの人気漫画が連載されています。",
        "generation_info": {
          "finish_reason": "stop"
        },
        "type": "ChatGenerationChunk",
        "message": {
          "lc": 1,
          "type": "constructor",
          "id": [
            "langchain",
            "schema",
            "messages",
            "AIMessageChunk"
          ],
          "kwargs": {
            "content": "「極東ネクロマンス」は週刊少年ジャンプに掲載されている漫画作品で、アクションとコメディ要素を含む人気のシリーズです。週刊少年ジャンプは集英社が発行しており、多くの人気漫画が連載されています。",
            "example": false,
            "additional_kwargs": {},
            "tool_call_chunks": [],
            "response_metadata": {
              "finish_reason": "stop"
            },
            "id": "run-c19f3d06-cc75-4511-bd0f-08437fb54994",
            "tool_calls": [],
            "invalid_tool_calls": []
          }
        }
      }
    ]
  ],
  "llm_output": null,
  "run": null
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] [2ms] Exiting Parser run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] [2.32s] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] [2.33s] Exiting Chain run with output:
{
  "output": "「極東ネクロマンス」は週刊少年ジャンプに掲載されている漫画作品で、アクションとコメディ要素を含む人気のシリーズです。週刊少年ジャンプは集英社が発行しており、多くの人気漫画が連載されています。"
}
[chain/end] [1:chain:RunnableWithMessageHistory] [2.35s] Exiting Chain run with output:
[outputs]
```
</details>

### 3. 「今日の東京の天気は?」
今度はWikiではなく、検索エンジンを使ってくれています。
LangSmithの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/f27cc2c6-7e49-e909-b816-0ec5b1acbd9d.png)

各内容
|種別|内容|入力|出力|
|:-:|:--|:--|:--|
|1. insert_history|会話履歴へ入力内容挿入|Promptに入力した内容|なし|
|2. ChatPromptTemplate|TemplateからPrompt生成|Template内変数|Prompt|
|3. ChatOpenAI|GPT3.5へ問い合わせ|Prompt|ToolのDuckDuck Goを使えという指示(検索語句も)|
|4. duckduckgo_search|DuckDuck Go検索|検索語句|検索結果情報|
|5. ChatPromptTemplate|TemplateからPrompt生成|今までの情報|Prompt|
|6. ChatOpenAI|GPT3.5へ問い合わせ|Prompt|最終回答

<details><summary>詳細ログ（3.今日の東京の天気は?）</summary>

```terminal: 詳細ログ
[chain/start] [1:chain:RunnableWithMessageHistory] Entering Chain run with input:
{
  "input": "今日の東京の天気は？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] Entering Chain run with input:
{
  "input": "今日の東京の天気は？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] Entering Chain run with input:
{
  "input": "今日の東京の天気は？"
}
[chain/start] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] Entering Chain run with input:
{
  "input": "今日の東京の天気は？"
}
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history> > 4:chain:load_history] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history > 3:chain:RunnableParallel<chat_history>] [4ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 2:chain:insert_history] [9ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] Entering Chain run with input:
[inputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] Entering Chain run with input:
{
  "input": ""
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad> > 9:chain:RunnableLambda] [1ms] Exiting Chain run with output:
{
  "output": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad> > 8:chain:RunnableParallel<agent_scratchpad>] [2ms] Exiting Chain run with output:
{
  "agent_scratchpad": []
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 7:chain:RunnableAssign<agent_scratchpad>] [6ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 10:prompt:ChatPromptTemplate] [0ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "System: You are a helpful assistant\nAI: How can I help you?\nHuman: 極東ネクロマンス って何？\nAI: 「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。\nHuman: さっきの回答を要約して\nAI: 「極東ネクロマンス」は週刊少年ジャンプに掲載されている漫画作品で、アクションとコメディ要素を含む人気のシリーズです。週刊少年ジャンプは集英社が発行しており、多くの人気漫画が連載されています。\nHuman: 今日の東京の天気は？"
  ]
}
[llm/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 11:llm:ChatOpenAI] [1.11s] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "",
        "generation_info": {
          "finish_reason": "function_call"
        },
        "type": "ChatGenerationChunk",
        "message": {
          "lc": 1,
          "type": "constructor",
          "id": [
            "langchain",
            "schema",
            "messages",
            "AIMessageChunk"
          ],
          "kwargs": {
            "content": "",
            "example": false,
            "additional_kwargs": {
              "function_call": {
                "arguments": "{\"query\":\"東京の今日の天気\"}",
                "name": "duckduckgo_search"
              }
            },
            "tool_call_chunks": [],
            "response_metadata": {
              "finish_reason": "function_call"
            },
            "id": "run-d064a7eb-1a7c-40c0-b243-d87a16e73184",
            "tool_calls": [],
            "invalid_tool_calls": []
          }
        }
      }
    ]
  ],
  "llm_output": null,
  "run": null
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence > 12:parser:OpenAIFunctionsAgentOutputParser] [1ms] Exiting Parser run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 6:chain:RunnableSequence] [1.13s] Exiting Chain run with output:
[outputs]
Error in StreamlitCallbackHandler.on_agent_action callback: RuntimeError('Current LLMThought is unexpectedly None!')
[tool/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 13:tool:duckduckgo_search] Entering Tool run with input:
"{'query': '東京の今日の天気'}"
[tool/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 13:tool:duckduckgo_search] [1.64s] Exiting Tool run with output:
"4月30日(火) 、東京都の天気をお伝えします。 ... 個々の雨雲が通り過ぎるまでの目安の時間は、多くの場所で40-50分程度の予想です。今日の9時頃にかけて雨雲がたびたび通過することがあるでしょう。 東京都は今日も晴れの天気で、最高気温は25度前後です。関東地方は日差しが降り注いでいますが、暑さ対策を心がけてください。 今日は雲の目立つ時間があっても、日差しが届きます。お出かけも楽しめますが、UVケアを忘れずに。明日は天気が崩れるので日差しの活用がオススメです。昼間は初夏の陽気になるため水分補給が欠かせません。 今朝(30日)の関東地方は、沿岸部を中心に雨雲がかかっています。このあと昼頃にかけては断続的に雨が降り、雷を伴う所も。明日(5月1日)の午後 ... この先1週間の天気のポイントは「5月スタートの明日も関東などで雨」「GW後半の四連休は最終日から天気下り坂」「天気次第では寒暖差が大きく」です。ウェザーニュースの最新の天気予報を随時確認してください。"
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{
  "input": ""
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad> > 17:chain:RunnableLambda] Entering Chain run with input:
{
  "input": ""
}
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad> > 17:chain:RunnableLambda] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad> > 16:chain:RunnableParallel<agent_scratchpad>] [3ms] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 15:chain:RunnableAssign<agent_scratchpad>] [8ms] Exiting Chain run with output:
[outputs]
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 18:prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 18:prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 19:llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "System: You are a helpful assistant\nAI: How can I help you?\nHuman: 極東ネクロマンス って何？\nAI: 「極東ネクロマンス」は、週刊少年ジャンプという週刊漫画雑誌に掲載されている作品の一つです。週刊少年ジャンプは集英社が発行しており、多くのアクションシーンとコメディ要素を含む漫画シリーズが掲載されています。週刊少年ジャンプは1968年に初版が発行されて以来、7.5億部以上の売り上げを記録しており、最も売れている漫画雑誌の一つです。多くの人気漫画シリーズが週刊少年ジャンプから生まれています。\nHuman: さっきの回答を要約して\nAI: 「極東ネクロマンス」は週刊少年ジャンプに掲載されている漫画作品で、アクションとコメディ要素を含む人気のシリーズです。週刊少年ジャンプは集英社が発行しており、多くの人気漫画が連載されています。\nHuman: 今日の東京の天気は？\nAI: {'arguments': '{\"query\":\"東京の今日の天気\"}', 'name': 'duckduckgo_search'}\nFunction: 4月30日(火) 、東京都の天気をお伝えします。 ... 個々の雨雲が通り過ぎるまでの目安の時間は、多くの場所で40-50分程度の予想です。今日の9時頃にかけて雨雲がたびたび通過することがあるでしょう。 東京都は今日も晴れの天気で、最高気温は25度前後です。関東地方は日差しが降り注いでいますが、暑さ対策を心がけてください。 今日は雲の目立つ時間があっても、日差しが届きます。お出かけも楽しめますが、UVケアを忘れずに。明日は天気が崩れるので日差しの活用がオススメです。昼間は初夏の陽気になるため水分補給が欠かせません。 今朝(30日)の関東地方は、沿岸部を中心に雨雲がかかっています。このあと昼頃にかけては断続的に雨が降り、雷を伴う所も。明日(5月1日)の午後 ... この先1週間の天気のポイントは「5月スタートの明日も関東などで雨」「GW後半の四連休は最終日から天気下り坂」「天気次第では寒暖差が大きく」です。ウェザーニュースの最新の天気予報を随時確認してください。"
  ]
}
[llm/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 19:llm:ChatOpenAI] [2.00s] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "東京の今日の天気は晴れで、最高気温は25度前後です。日差しが強いので暑さ対策をしてお出かけすると良いでしょう。UVケアも忘れずに行いましょう。",
        "generation_info": {
          "finish_reason": "stop"
        },
        "type": "ChatGenerationChunk",
        "message": {
          "lc": 1,
          "type": "constructor",
          "id": [
            "langchain",
            "schema",
            "messages",
            "AIMessageChunk"
          ],
          "kwargs": {
            "content": "東京の今日の天気は晴れで、最高気温は25度前後です。日差しが強いので暑さ対策をしてお出かけすると良いでしょう。UVケアも忘れずに行いましょう。",
            "example": false,
            "additional_kwargs": {},
            "tool_call_chunks": [],
            "response_metadata": {
              "finish_reason": "stop"
            },
            "id": "run-c6675583-e630-4c45-849c-c495dedbba2b",
            "tool_calls": [],
            "invalid_tool_calls": []
          }
        }
      }
    ]
  ],
  "llm_output": null,
  "run": null
}
[chain/start] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 20:parser:OpenAIFunctionsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence > 20:parser:OpenAIFunctionsAgentOutputParser] [2ms] Exiting Parser run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor > 14:chain:RunnableSequence] [2.03s] Exiting Chain run with output:
[outputs]
[chain/end] [1:chain:RunnableWithMessageHistory > 5:chain:AgentExecutor] [4.82s] Exiting Chain run with output:
{
  "output": "東京の今日の天気は晴れで、最高気温は25度前後です。日差しが強いので暑さ対策をしてお出かけすると良いでしょう。UVケアも忘れずに行いましょう。"
}
[chain/end] [1:chain:RunnableWithMessageHistory] [4.84s] Exiting Chain run with output:
[outputs]
```
</details>

# 実装
## 事前準備
### APIキー取得
OPEN AI とLangSmithでキーを取得し、ファイル".env"に書き込みます。Pythonプログラムと同じディレクトリに置きます。キー取得方法は省略します。LangSmithは個人勉強でやっているので無償でした。
キー以外にもモデルやTemperatureなど設定しています。

```text:.env
OPENAI_API_KEY=<key>
OPENAI_API_MODEL=gpt-3.5-turbo
OPENAI_API_TEMPERATURE=0.5
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=<key>
```

### Package Install
以下のPythonパッケージをインストールしています。ちなみにPythonは3.12.2を使っています。

|Package|Version|備考|
|:--|:-:|:--|
|openai|1.23.6|langchain-openaiがあれば不要？|
|streamlit|1.33.0||
|langchain|0.1.16||
|langchain-openai|0.1.4||
|langchain-community|0.0.34||
|langchainhub|0.1.15||
|python-dotenv|1.0.1|.envファイルのロード|
|duckduckgo-search|5.3.0||
|wikipedia|1.4.0||

### Python Program
Pythonプログラムの全体です。
※後で`wikipedia.set_lang('ja')`を追加してwikipediaの検索を日本語にしています。ログで記録していたときは、デフォルトの英語検索でした。

```app.py
import os

from dotenv import load_dotenv
from langchain import hub
from langchain.agents import AgentExecutor, load_tools, create_openai_functions_agent
from langchain_community.chat_message_histories import StreamlitChatMessageHistory
from langchain_community.callbacks import StreamlitCallbackHandler
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain.globals import set_debug
from langchain_openai import ChatOpenAI
import streamlit as st
import wikipedia

set_debug(True)

st.set_page_config(page_title="StreamlitChatMessageHistory", page_icon="📖")
st.title("📖 StreamlitChatMessageHistory")

"""
A basic example of using StreamlitChatMessageHistory to help LLMChain remember messages in a conversation.
The messages are stored in Session State across re-runs automatically. You can view the contents of Session State
in the expander below. View the
[source code for this app](https://github.com/langchain-ai/streamlit-agent/blob/main/streamlit_agent/basic_memory.py).
"""

# Set up memory
msgs = StreamlitChatMessageHistory(key="langchain_messages")
if len(msgs.messages) == 0:
    msgs.add_ai_message("How can I help you?")

# メッセージのログをExpander内に参照可能(最後に中身を設定)
view_messages = st.expander("View the message contents in session state")

load_dotenv()

llm = ChatOpenAI(
    model_name=os.environ["OPENAI_API_MODEL"],
    temperature=os.environ["OPENAI_API_TEMPERATURE"],
    streaming=True,
)
prompt = hub.pull("hwchase17/openai-functions-agent")
tools = load_tools(["ddg-search", "wikipedia"])
wikipedia.set_lang('ja')
_agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=_agent, 
                                    tools=tools, 
                                    verbose=True,
                                    callbacks=[StreamlitCallbackHandler(st.container())]
                                    )
# 変数箇所はHub参照
# https://smith.langchain.com/hub/hwchase17/openai-functions-agent
agent = RunnableWithMessageHistory(
        agent_executor,
        lambda session_id: msgs, # msgsだけではなくlambda関数にする
        input_messages_key="input",
        history_messages_key="chat_history",
    )

# Render current messages from StreamlitChatMessageHistory
for msg in msgs.messages:
    st.chat_message(msg.type).write(msg.content)

# If user inputs a new prompt, generate and draw a new response
# := はウォルラス演算子(IFと代入を一度に行う)
if prompt := st.chat_input():
    st.chat_message("human").write(prompt)

    # Note: new messages are saved to history automatically by Langchain during run
    # session_id は 固定で"any"にしているが、何でもOK("any"以外でもOK)
    config = {"configurable": {"session_id": "any"}}
    response = agent.invoke({"input": prompt}, config)
    st.chat_message("ai").write(response['output'])

# Draw the messages at the end, so newly generated ones show up immediately
with view_messages:
    """
    Message History initialized with:
    ```python
    msgs = StreamlitChatMessageHistory(key="langchain_messages")
    ```

    Contents of `st.session_state.langchain_messages`:
    """
    view_messages.json(st.session_state.langchain_messages)
```

Pythonプログラムをいくつか説明

#### メッセージ履歴の設定
メッセージ履歴の設定と初期表示するメッセージの設定。

```python
# Set up memory
msgs = StreamlitChatMessageHistory(key="langchain_messages")
if len(msgs.messages) == 0:
    msgs.add_ai_message("How can I help you?")
```

#### Expander設定
折りたたみ可能な以下の画面 Expanderの設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134273/52a62982-21b3-2c52-3317-1c46a1108e1a.png)

`view_messages`の中身は最後に設定します。

```python
# メッセージのログをExpander内に参照可能(最後に中身を設定)
view_messages = st.expander("View the message contents in session state")
```

#### LLMの設定
.envファイルから環境変数を設定し、環境変数からモデル名とTemeratureを設定。`streaming`を`True`にすることで、結果のテキストが少しずつ画面表示されます。

```python
load_dotenv()

llm = ChatOpenAI(
    model_name=os.environ["OPENAI_API_MODEL"],
    temperature=os.environ["OPENAI_API_TEMPERATURE"],
    streaming=True,
)
```

### 起動
以下のコマンドで起動します。ブラウザに表示されるはずです。
今回はサーバでの実行はしておらず、ローカルPCでの実行までです。

```terminal
streamlit run app.py
```