ollamaを使ったSlack AIボット開発1
以下の手順で開発を進めます。
- ①ollama導入
- ②ollama APIの使用(この記事ではここまで)
- ③Slack Socket modeを用いたボット作成
- ④ollamaとボットを組み合わせてAIボットの完成
環境
- ollama サーバPC:MacBookPro2018(inteli5), Ventura13.2.1
- 開発PC:Ubuntu 24.04.1 LTS(WSL2, windows11)
私は開発PCからMacにSSH接続して作業しましたが、Macだけでも十分作成可能です。
①ollama導入
ollamaを使えばローカル環境で(GPUなしで)LLMを動かすことができます。
ollamaはLLMの推論を高速に行えるllama.cppを内部的に用いたアプリで以下のリンクからダウンロードできます。
基本的にはインストーラの指示に従ってゆくだけで推論を行うことができます。
~$ ollama --help
return
Large language model runner
Usage:
ollama [flags]
ollama [command]
Available Commands:
serve Start ollama
create Create a model from a Modelfile
show Show information for a model
run Run a model
stop Stop a running model
pull Pull a model from a registry
push Push a model to a registry
list List models
ps List running models
cp Copy a model
rm Remove a model
help Help about any command
Flags:
-h, --help help for ollama
-v, --version Show version information
Use "ollama [command] --help" for more information about a command.
~$ ollama run llama3.2
return
>>> Send a message (/? for help)
>>> 札幌市の特産品を教えてください。
札幌市は北海道の中核的な都市であり、多くの特産品があります。
1. **ハマシリ**: ハマシリは野菜が中心の日本料理です。主に夏季に作られる海老や大根などを使用します。札幌
市ではハマシリを初期段階で成功させたことで、北海道と日本の知名度が高まりました。
2. **アイスクリーム**: 札幌には多くのアイスクリーム屋があります。特にマウントエンアイスクリームやサルベ
デールアイスクリームなどは有名です。
3. **カニ**: 札幌市ではカニが豊富な魚種で、食べ物としても知られています。
4. **バラ**: 札幌市には多くの花壇があり、札幌市のバラは日本でも有名です。特に「さくら」や「ひまわり」な
どの季節ごとに生まれるバラが人気です。
5. **カニタマゴ**: カニタマゴはアリゲーターやエビなどをベースにして作るもので、食べ物としても知られてい
ます。札幌市ではこの食材を多く利用しています。
6. **ホットドッグ:北海道のホットドッグは、ほとんどが牛肉を使っています。これらのホットドッグは特に札幌
市で有名です。
7. **マルサカ**: マルサカとは北海道で生産される野菜や水産物を使用したお弁当です。札幌市ではこの食材を多
く利用しています。
8. 札幌みそ:札幌市では「さくら」などの花が生まれた時期に作られる、特有の味の味噌を使ったみそです。
サポートされているモデルはこちらのサイトで確認することができます。基本的にはサポートされているモデルで困ることはなさそうですが、カスタムモデルを使う方法もあるので必要に応じて試すのが良いと思います。
~$ ollama list
上記のコードでロードしたモデルを確認できます。
②ollama APIを使う
ollamaでは先ほどのollama run llama3.2
を実行した時点で特に設定を行わなくても、http://localhost:11434
へアクセス可能です。アクセスすると、Ollama is runningの文字が表示されます。
デフォルトの状態ではローカルホストからの接続のみを受け付ける設定になっているので、環境変数として、
export OLLAMA_HOST=0.0.0.0
export OLLAMA_ORIGINS=192.168.*
を設定する必要があります。これはローカルネットワーク内の192.168.hoge.hoge
のアドレスを持つ機器からのアクセスを許可する設定です。
この設定を終えた状態でOllamaのAPIサーバを起動させます。
~$ ollama serve
この状態でhttp://LLMサーバアドレス:11434
にアクセス(ブラウザで入力)してOllama is runningが表示されれば接続がうまくいっています。この場合に
Error: listen tcp 0.0.0.0:11434: bind: address already in use
のように表示されることがありますが、これはポート11434をほかのプロセスが使用しているためにListenできないと言っています。先ほどのollama run llama3.2
の後すぐに実行するとこのようになることが多いです。この場合は、
~$ lsof -i :11434
ex. return
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ollama 8524 myname 3u IPv4 gggggg 0t0 TCP localhost:11434 (LISTEN)
として使用中のプロセスを調べてプロセスをkillしてください。
その後にもう一度ollama serve
を実行すればうまくいくはずです。
APIについて
-
https://www.postman.com/postman-student-programs/ollama-api/documentation/suc47x8/ollama-rest-api
-
http://localhost:11434/api/generate
エンドポイントへのリクエストでモデルの推論結果がストリーミング形式で返ってくるので、それを表示させれば終わりです。 -
http://localhost:11434/api/chat
エンドポイントでは文脈を維持した会話が可能です。システムプロンプトを作れます。
http://localhost:11434/api/generate
とPythonを利用してコマンドライン上で簡単に単発の質問に対する応答を得る機能を作成します。(Python 3.12.3)
import requests
import json
from config import API_SERVER_URL, MODEL
def generate_text(prompt):
url = API_SERVER_URL + "/api/generate"
headers = {
"accept": "application/json",
"Content-Type": "application/json",
}
data = {
"model": MODEL,
"prompt": prompt,
}
response = requests.post(url, headers=headers, json=data, stream=True)
full_response = ""
for line in response.iter_lines(decode_unicode=True):
if not line:
continue
try:
response_json = json.loads(line)
text_chunk = response_json.get("response", "")
print(text_chunk, end="", flush=True)
full_response += text_chunk
except json.JSONDecodeError as e:
print(f"\n[JSON decode error]: {e}")
return full_response
if __name__ == "__main__":
prompt = "札幌市の名物について教えてください。"
user_input = input("プロンプトを入力してください (デフォルト: '札幌市の名物について教えてください。'): ")
if user_input:
prompt = user_input
response = generate_text(prompt)
print("\n")
以下にはhttp://localhost:11434/api/chat
を用いた同様のサンプルコードを記述します。
import requests
import json
from config import API_SERVER_URL, MODEL
def stream_chat(messages):
url = API_SERVER_URL + "/api/chat"
headers = {
"accept": "application/json",
"Content-Type": "application/json",
}
data = {
"model": MODEL,
"messages": messages,
"stream": True,
}
response = requests.post(url, headers=headers, json=data, stream=True)
full_response = ""
for line in response.iter_lines(decode_unicode=True):
if not line:
continue
try:
response_json = json.loads(line)
text_chunk = response_json.get("message", {}).get("content", "")
print(text_chunk, end="", flush=True)
full_response += text_chunk
except json.JSONDecodeError as e:
print(f"\n[JSON decode error]: {e}")
return full_response
if __name__ == "__main__":
messages = [
{
"role": "system",
"content": "あなたは日本語を流ちょうに話す猫です。語尾には「にゃ」をつけてください。",
}
]
print("Ollama Chat CLI (終了するには 'exit' と入力)")
while True:
user_input = input("\n>>> ")
if user_input.lower() in ["exit", "quit"]:
print("チャットを終了します。")
break
messages.append({
"role": "user",
"content": user_input
})
print("アシスタント: ", end="")
assistant_reply = stream_chat(messages)
print("") # 改行
messages.append({
"role": "assistant",
"content": assistant_reply
})
- 多数のユーザがいる場合にはaiohttpで非同期処理をするとよいかもしれませんが、とりあえずここまでとしてSlackBotの方に進みます。