はじめに
少し前にClaudeに実装されたArtifactsはLLM・プログラマー界隈には大きな影響を与えました。
また、同時に発表されたClaude 3.5 Sonnetと同時に使うことで、Webアプリを誰でも作れる時代を感じさせてくれました。
しかし、Claude 3.5 Sonnetは無料で使うと、1日数回のやり取りしかできません。なんとかして、(無料で)もっと使ってみたいという欲望が生まれてきました。
とはいえ、Artifactsの実装は一見するとそこまで難しいようには見えないので、そのうちGoogleやOpenAIも同様の対話型ビジュアルチャットを実装する気がします。
でも、それまで待てない私はGoogle検索の海を彷徨い、見つけてしまいました。
prompt2ui
見つけたのはこの動画です。prompt2uiというLLMのWeb UI(Artifacts like)とOllamaを組み合わせて、ローカルLLMでアプリを開発してみる動画です。
動画を見た所、prompt2uiが動けばollamaに限らず、様々なLLM APIが使えそうです。
今回はこれをDocker環境で試してみる話となります。
環境構築
Web UIなのでリモートのDocker上に環境構築しました。いつものようにDev Containerを使っています。
以下、プロジェクトのディレクトリ構成です。
ollama_prompt2ui_test
├── .devcontainer
│ ├── Dockerfile
│ ├── devcontainer.json
│ └── docker-compose.yml
├── .ollama
└── prompt2ui (git clone)
.ollamaディレクトリだけ作っておいてください。その後は、.devcontainer
以下のファイルを作成して、コンテナをビルドしてください
Dockerfile
ベースはdebian:bullseye-slimにしました。node:20とかをベースにすると、vscodeのユーザーとUIDが衝突して、vs code上でファイルの権限が無くなってしまったので、バニラのdebian上にnode jsの実行環境を作りました。
node.jsはversion 22を入れると、後で怒られるのでversion 20を入れました。
FROM debian:bullseye-slim
ENV DEBIAN_FRONTEND=noninteractive
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get update \
&& groupadd --gid $USER_GID $USERNAME \
&& useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
&& apt-get install -y sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME \
&& apt-get -y install locales \
&& localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
RUN apt-get -y install git
RUN apt-get -y install curl
RUN curl -SL https://deb.nodesource.com/setup_20.x | bash
RUN apt-get install -y nodejs
devcontainer.json
名前とサービス名を設定してください。私は、面倒なので同じものを設定しています。拡張機能好きなものを入れてください。今回はほぼプログラムをいじらないので、拡張機能はなくてもいいです。
{
"name": "ollama_prompt2ui_test",
"service": "ollama_prompt2ui_test",
"dockerComposeFile": "docker-compose.yml",
"remoteUser": "vscode",
"workspaceFolder": "/work",
"customizations": {
"vscode": {
"extensions": []
}
}
}
docker-compose.yml
サービス名を先ほど設定したものと同一にしてください。
また、ollamaを別サービスとして入れて、API経由で利用できるようにしておきます。
version: '3'
services:
ollama_prompt2ui_test:
container_name: 'ollama_prompt2ui_test-container'
hostname: 'ollama_prompt2ui_test-container'
build: .
restart: always
working_dir: '/work'
tty: true
volumes:
- type: bind
source: ..
target: /work
ollama:
volumes:
- type: bind
source: ../.ollama
target: /root/.ollama
container_name: ollama
tty: true
image: ollama/ollama:latest
ports:
- 11434:11434
environment:
- OLLAMA_KEEP_ALIVE=24h
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
prompt2uiのクローン
コンテナに入れたら、prompt2uiをクローンします。
git clone https://github.com/sullyo/prompt2ui.git
クローン後は、ディレクトリ移動して依存ライブラリ・モジュールをインストールします。
cd prompt2ui
npm install
数分待つと依存ライブラリ・モジュールのインストールが終わります。
OllamaでArtifacts
モデルのダウンロード
Ollamaを使ってArtifactsライクなWeb UIを試してみます。
まずは、Ollamaが正しく起動しているかを確かめます。
{Dockerを立てたサーバーのIP}:11434でブラウザでアクセスすると、Ollama is runningとでます。これでOKです。リモート上に立ててない人は、localhost:11434でOKです。
そしたら、モデルをダウンロードします。ターミナルを開いて、{ip-address:11434}/api/pullでモデルをダウンロードできます。
curl 192.168.0.200:11434/api/pull -d '{"name":"deepseek-coder-v2:16b"}'
今回は、プログラムに特化したローカルLLMであるdeepspeek-coder-v2を試してみます。
数十分待つとダウンロードが終わります。
prompt2uiのapi呼び出しの編集
次に、prompt2uiのapi呼び出し部分を編集します。
編集するファイルはprompt2ui/src/app/api/chat/route.ts
です。
import { systemPrompt } from "@/app/api/chat/prompt";
import {createOpenAI} from "@ai-sdk/openai";
import { CoreMessage, streamText } from "ai";
export async function POST(req: Request) {
const { messages }: { messages: CoreMessage[] } = await req.json();
const openai = createOpenAI({
baseURL:"http://192.168.0.200:11434/v1/",
apiKey:"ollama",
});
const result = await streamText({
model: openai("deepseek-coder-v2:16b"),
system: systemPrompt,
messages,
});
return result.toAIStreamResponse();
}
もともとは、anthropic(Claude)のAPIを呼び出すようになっていましたが、これをOpen AIの呼び出しへと書き換えます。OllamaのAPIサーバーはOpen AI準拠なので、これで動作します。
起動
書き換えたら、prompt2uiを起動します。
npm run dev
正しく起動できれば以下のようなログが流れます。
vscode@ollama_prompt2ui_test-container:/work/prompt2ui$ npm run dev
> sonnet-coder@0.1.0 dev
> next dev
▲ Next.js 14.2.4
- Local: http://localhost:3000
- Environments: .env
✓ Starting...
/bin/sh: 1: pnpm: not found
✓ Ready in 2.8s
http://localhost:3000へアクセスしてみます。
数秒待たないと、Web UIが表示されませんが、少し待つと表示されます。
左側にチャット、右側に実行画面とコードが見れるようになっています。
「簡単なTodoアプリをReactを使って作成して」とチャットすると、コードが表示されましたが、Web UIは固まってしまいました。
何度か試しましたが、Youtubeの動画のようにスムーズにいきませんでした。実行マシンのスペック不足でしょうか?
GeminiでArtifacts
prompt2uiのapi呼び出しの編集
次は、Geminiでも試したいと思います。要はAPIを正しく呼び出せれば、動くと思うので。
まず、GeminiのAPI KEYを取得してきます。これはGoogle AI Studioから取得できます。
そしたら、prompt2uiの.env.exampleを.envへと名前変更し、中身を書き換えます。ここにGeminiのAPI Keyを書き込んでおきます。
GOOGLE_API_KEY="xxxxx"
そしたら、ai-sdk/googleを追加パッケージに追記します。パッケージの管理はpackage.jsonっぽいのでここに@ai-sdk/googleを追記しました。
{
"name": "sonnet-coder",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@ai-sdk/anthropic": "^0.0.23",
"@ai-sdk/openai": "^0.0.33",
"@ai-sdk/google": "^0.0.24",
...
その後、route.tsを編集します。パスはprompt2ui/src/app/api/chat/route.ts
です。
import { systemPrompt } from "@/app/api/chat/prompt";
import { Google } from "@ai-sdk/google";
import { CoreMessage, streamText } from "ai";
export async function POST(req: Request) {
const { messages }: { messages: CoreMessage[] } = await req.json();
const google = new Google({ apiKey: process.env.GOOGLE_API_KEY || '' })
const result = await streamText({
model: google.generativeAI("models/gemini-1.5-flash"),
system: systemPrompt,
messages,
});
return result.toAIStreamResponse();
}
パッケージを追記したのでnpm installをしておきます
npm install
あとはollamaのときと同様です。
起動
npm run devでprompt2uiを起動します。
npm run dev
正しく起動できれば以下のようなログが流れます。
vscode@ollama_prompt2ui_test-container:/work/prompt2ui$ npm run dev
> sonnet-coder@0.1.0 dev
> next dev
▲ Next.js 14.2.4
- Local: http://localhost:3000
- Environments: .env
✓ Starting...
/bin/sh: 1: pnpm: not found
✓ Ready in 2.8s
http://localhost:3000へアクセスしてみます。少し待つと、Web UIが表示されました。
先ほどと同様に「簡単なTodoアプリをReactを使って作成して」とチャットすると、今度はフリーズすることなく、プログラムを吐き出しました。
しかし、youtubeの動画では自動でプログラムをコンパイルして、右側の実行画面が更新されていた様に感じましたが、自動ではコンパイルされていません。
codeタブを開いて、出力されたプログラムを貼り付けると、previewが更新されました。
追加のボタンが白くて見えませんが、タスクの記入欄の横にあります。
英語でも聞いてみましたが、自動で実行画面を表示してくれませんでした。
うまくプログラム部分を認識できていないような気がします。横にコピペするだけですが、この一手間が面倒に感じます。
まとめ
今回はprompt2uiというArtifactsライクなWeb UIを使ってみました。
左側にチャット、右側にコードとプレビューという配置になっており、Claude Artifactsを再現したようなチャット画面が使えました。
そして、ollamaやgeminiで利用できたので、Claudeの無料制限を気にせず使えるかもしれません。
ollamaではdeepseek-coder-v2を使いましたが、マシンのスペック不足が原因で、快適には利用できませんでした。
geminiの無料APIを利用すると、快適にチャットができることも確認しました。
しかし、自動でプログラムを読み取って、プレビューしてくれる機能がうまく動作しませんでした。これは今後の課題とします(解決するまでにGeminiがArtifactライクなチャット画面を実装する気もする)。