はじめに
以前の記事で Ollama と Livebook を連携する手法を紹介しました
本記事では Ollama と Livebook で簡易的な AI チャットを実装してみます
AI には比較的軽量で CPU 環境でもそれなりに速く動いてくれる Phi-4 を使用します
実装したノートブックはこちら
コンテナの準備
本記事では Livebook と Ollam をコンテナで起動します
docker-compose.with-ollama.yml
---
services:
livebook_with_ollama:
image: ghcr.io/livebook-dev/livebook:0.14.7
container_name: livebook_with_ollama
ports:
- '8080:8080'
- '8081:8081'
volumes:
- ./tmp:/tmp
ollama:
image: ollama/ollama:0.5.11
container_name: ollama_for_livebook
ports:
- '11434:11434'
volumes:
- ollama:/root/.ollama
volumes:
ollama:
コンテナのビルド・起動
以下のコマンドでコンテナをビルド、起動します
docker compose --file docker-compose.with-ollama.yml up
両方のビルドが完了したら Livebook の URL にアクセスします
Livebook 上での AI チャットの実装
セットアップ
Livebook で新しいノートブックを開き、セットアップセルで以下のコードを実行します
Mix.install([
{:ollama, "~> 0.8"},
{:kino, "~> 0.14"}
])
モデルの用意
コンテナで起動している Ollama にアクセスするためのクライアントを用意します
URLでしていしている「ollama」の部分は docker-compose で指定したサービス名です
応答に時間がかかることを想定して、 receive_timeout
に 300 秒を指定します
client = Ollama.init(base_url: "http://ollama:11434/api", receive_timeout: 300_000)
クライアントで Phi-4 のモデルをダウンロードします
Ollama.pull_model(client, name: "phi4")
読み込みに時間がかかるので、実行前に読み込んでおきます
Ollama.preload(client, model: "phi4")
Ollama によるストリーミング応答
ストリーミングで受け取ったトークン毎に表示を更新します
回答表示用のフレームを用意します
answer_frame = Kino.Frame.new()
回答用の関数を用意します
ストリーミングで処理した後、全部のトークンを結合して返すようにしています
answer = fn input, frame ->
{:ok, stream} =
Ollama.completion(
client,
model: "phi4",
prompt: input,
stream: true
)
stream
|> Stream.transform("AI: ", fn chunk, acc ->
response = acc <> chunk["response"]
markdown = Kino.Markdown.new(response)
Kino.Frame.render(frame, markdown)
{[chunk["response"]], response}
end)
|> Enum.join()
end
Phi-4 は日本文化についても結構知っているので、ドラえもんについて質問してみましょう
answer.("ドラえもんについて100文字程度で説明して", answer_frame)
実行結果
フォームの作成
チャットアプリらしいフォームを用意します
# 出力用フレーム
output_frame = Kino.Frame.new()
# ストリーミング用フレーム
stream_frame = Kino.Frame.new()
# 入力用フォーム
input_form =
Kino.Control.form(
[
input_text: Kino.Input.textarea("メッセージ")
],
submit: "送信"
)
Kino.Frame.render(output_frame, Kino.Markdown.new(""))
Kino.Frame.render(stream_frame, Kino.Markdown.new(""))
# フォーム送信時の処理
Kino.listen(input_form, fn %{data: %{input_text: input}} ->
Kino.Frame.append(output_frame, Kino.Markdown.new("あなた: " <> input))
full_response = answer.(input, stream_frame)
Kino.Frame.render(stream_frame, Kino.Markdown.new(""))
Kino.Frame.append(output_frame, Kino.Markdown.new("AI: " <> full_response))
end)
# 入出力を並べて表示
Kino.Layout.grid([output_frame, stream_frame, input_form], columns: 1)
実行結果
AI チャットが実装できました
まとめ
Livebook と Ollama で AI チャットが実装できました
Phi-4 であればローカルマシンでも十分な速度で応答してくれます