Gradio上でLangChainを使ってwatsonx.aiを呼んでyieldでChatGPTのようにストリーミング表示

# はじめに

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184221/326c56fd-a1c5-f9c8-76e1-2cbf9ed22847.png)

本記事では、GradioとLangChainを使ってIBM watsonx.aiを呼び出し、リアルタイムでストリーミング表示するシンプルなデモを作成します。このデモでは、ユーザーが入力した仕様に対してPythonコードを生成し、その結果をリアルタイムで表示します。

# 説明
上記で説明した通り、このデモはGradioを使ってウェブインターフェースを作成し、LangChainとwatsonx.aiを組み合わせることで、ユーザーの入力に基づいてPythonコードを生成します。生成されたコードは`yield`を使ってリアルタイムでストリーミング表示されます。

- **Gradio**：ウェブインターフェースを作成するためのライブラリ。
- **LangChain**：自然言語処理モデルを簡単に扱うためのフレームワーク。
- **watsonx.ai**：IBMのAIサービスで、様々な言語モデルを提供。

これにより、ユーザーは入力したプロンプトに対して逐次的に生成されるコードをリアルタイムで確認でき、必要に応じて修正や再生成を行うことができます。

このデモのポイントは、以下の通りです。
1. **リアルタイムストリーミング**：`yield`を使って少しずつコードを生成し、リアルタイムで表示。
2. **直感的なUI**：Gradioを使って簡単に操作できるインターフェースを提供。
3. **選べる言語モデル**：watsonx.aiを使用することで、いろいろなモデルを選べます。今回はllama-3を使います。

このデモを実際に動かすことで、LangChainとwatsonx.aiの組み合わせがどのように機能するのかを体験できます。また、Gradioを使って手軽にインターフェースを作成する方法も学べます。

ぜひ試してみてください。

# コード

```python
import gradio as gr
from langchain_ibm import WatsonxLLM
from dotenv import load_dotenv
import os

# .env ファイルを読み込む
load_dotenv()
PROJECT_ID = os.getenv('PROJECT_ID')

parameters = {
    "decoding_method": "sample",
    "max_new_tokens": 2000,
    "min_new_tokens": 1,
    "temperature": 0.05,
    "top_k": 5,
    "top_p": 0.6,
}

watsonx_llm = WatsonxLLM(
    model_id="meta-llama/llama-3-70b-instruct",
    url="https://us-south.ml.cloud.ibm.com",
    project_id=PROJECT_ID,
    params=parameters,
)

def gen_python(doc):
    language = "python"
    prompt = gen_code_prompt(doc, language)
    code = ""
    for chunk in watsonx_llm.stream(prompt):
        code = code + chunk
        yield code

def gen_code_prompt(doc, language):
    return f"""\
あなたは優秀な{language}プログラマーです。
{language}言語で以下の処理をコーディングしてください。

# 処理内容
{doc}

# コード

"""

with gr.Blocks() as demo:
    gr.Markdown("# 🦙 llama-3")
    with gr.Tab("👩‍💻 コード生成"):
        with gr.Row():
            with gr.Column():
                gr.Markdown("### 📜 プロンプト構築")
                doc = gr.Textbox(placeholder="", label='処理内容', lines=8)
                with gr.Row():
                    btn_code = gr.Button("生成", variant="primary")
                    gr.ClearButton(value="クリア")
            with gr.Column():
                gr.Markdown("### ⌨️ 生成結果")
                with gr.Accordion("Python"):
                    out2 = gr.Textbox()
    # 処理
    btn_code.click(fn=gen_python, inputs=[doc], outputs=[out2])

demo.launch()
```

```requirements.txt
aiofiles==23.2.1
aiohttp==3.9.5
aiosignal==1.3.1
altair==5.2.0
annotated-types==0.6.0
anyio==4.3.0
attrs==23.2.0
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
contourpy==1.2.0
cycler==0.12.1
fastapi==0.110.0
ffmpy==0.3.2
filelock==3.13.1
fonttools==4.50.0
frozenlist==1.4.1
fsspec==2024.3.1
gradio==4.22.0
gradio_client==0.13.0
h11==0.14.0
httpcore==1.0.4
httpx==0.27.0
huggingface-hub==0.21.4
ibm-cos-sdk==2.13.5
ibm-cos-sdk-core==2.13.5
ibm-cos-sdk-s3transfer==2.13.5
ibm_watsonx_ai==1.0.8
idna==3.6
importlib_metadata==7.1.0
importlib_resources==6.4.0
Jinja2==3.1.3
jmespath==1.0.1
jsonpatch==1.33
jsonpointer==3.0.0
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
kiwisolver==1.4.5
langchain==0.2.4
langchain-core==0.2.7
langchain-ibm==0.1.7
langchain-text-splitters==0.2.1
langsmith==0.1.77
lomond==0.3.3
markdown-it-py==3.0.0
MarkupSafe==2.1.5
matplotlib==3.8.3
mdurl==0.1.2
multidict==6.0.5
numpy==1.26.4
orjson==3.9.15
packaging==24.0
pandas==2.1.4
pillow==10.2.0
pydantic==2.6.4
pydantic_core==2.16.3
pydub==0.25.1
Pygments==2.17.2
pyparsing==3.1.2
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
referencing==0.34.0
requests==2.32.3
rich==13.7.1
rpds-py==0.18.0
ruff==0.3.4
semantic-version==2.10.0
shellingham==1.5.4
six==1.16.0
sniffio==1.3.1
SQLAlchemy==2.0.30
starlette==0.36.3
tabulate==0.9.0
tenacity==8.3.0
tomlkit==0.12.0
toolz==0.12.1
tqdm==4.66.2
typer==0.9.0
typing_extensions==4.10.0
tzdata==2024.1
urllib3==2.1.0
uvicorn==0.29.0
websockets==11.0.3
yarl==1.9.4
zipp==3.19.2
```

```.env
WATSONX_APIKEY=xxx
PROJECT_ID=yyy
```

# 参考
API KeyやProject IDの種痘方法は以下の記事を参考にしてみてください。

[watsonx.aiのLLMでLangChainを使ってPDFの内容をQ&Aをする #Python - Qiita](https://qiita.com/nishikyon/items/5054209089fc632981f8#1-%E5%89%8D%E6%BA%96%E5%82%99)

[IBM Cloud APIKEYの作成(取得)方法 #ibmcloud - Qiita](https://qiita.com/nishikyon/items/fa8b951267efbe911fe7)