Cloudbotが何かというと、何だろう。なんでも出来る子?
PCの中に入れたAIが、ツール起動したりWEB巡回したりとか。
こう、アニメや漫画の中のAIがやってるようなことを出来る仕組みみたいなものぽい。
詳細はよくわかってない。
ただ、機能を絞って使えば、例えば会話だけする子で、記憶を完全に保持出来る。
みたいなことも出来るみたい。よくわからない。よしやってみよう。
【1:Cloudbotの種類を選ぶ】
雑にこんな風に分かれてるらしい。私も詳細はよくわかってない。
1:フレームワーク型 自分で使う機能を入れてく感じ。
2:アプリケーション型 多機能、全部入り。次回はこれやってみる。
3:SaaS / クラウド前提型 外部連携前提のやつ。
私はローカル完結で、LM StudioやSillyTavernと内部連携させることを考えてます。
ということで、フレームワーク型から選びます。
大雑把に、以下の系統があります。
1:bot用フレームワーク 最小構成な、いわゆるcloudbotぽいやつ。
2:エージェント寄りフレームワーク 考えて判断して行動するAI。
3:LLMラッパー拡張型 LangChainやLlamaIndexを土台とした、DB連携とか強いやつ。
まずは最小構成で試したいので、1のやつから選びます。
その中には、更に以下のような系統があります。
1:Open-Source Chatbot Boilerplate 系 最低限な子。
2:FastAPI + LLM Wrapper 型 WEB UIくらいはついてる系。
3:Persona-first Bot フレームワーク 会話ガチ特化系。
4:Discord/Slack bot 派生型 Discordに常駐させて家から繋いだり系。
この中の2のやつから選びます。
系統としてはopenai-compatible fastapi chat server系だそうで。
特徴としてはこんな感じとのこと。
・LM Studioと“そのまま”繋がる
・SillyTavernとも将来そのまま繋がる
・人格も記憶も、後から足せる
【2:導入開始】
1:適当な場所に、格納するフォルダを作る。
2:そこで仮想環境を作る。作らなくてもいい。この記事の4のPowerShell系の話参照。
python -m venv venv
venv\Scripts\activate
3:必要なものをDL。さっきのやつにそのままコマンド投入。
pip install fastapi uvicorn requests
4:main.py を作る
さっきのフォルダの中にテキストファイル作る。
ちゃっぴーにコード作って貰ってこぴぺして保存して名前をmain.pyに変更。
5:起動
uvicorn main:app –reload
6:ブラウザでアクセスする
http://127.0.0.1:8000/docs
完成です。
何を導入するか選ぶのに2時間くらい。
導入自体は5分で終わりました。
Cloudbotの導入お疲れ様でした。
ここでいろいろテストしたりなんだり。数時間ごちゃごちゃやってた。
さー、こっからが、いよいよ本番です。
【3:SillyTavernと連携させる】
仕組みとしてはこんな感じ。
AI本体をLM Studioで読み込み、Cloudbotを経由してSillyTavernに出力させます。
Cloudbotが何するかというと、例えばえーと。
私の黒い砂漠の記事を、テキストデータで全部放り込みます。
そして、特定のキーワードでテキストを呼び出し、要約してSillyTavernに渡せます。
つまり、Cloudbot使うと、私の手持ちのデータを雑に渡してAIに読ませることが出来ます。
キーワードファイルは必要ですが、それを作れば、黒い砂漠知識AIが作れる。
ということで連携開始。
SillyTavernのConnection Profileのカスタムエンドポイントを以下に変更。
http://127.0.0.1:8001/v1
続いてCloudbotをちょっとポートずらして起動。
uvicorn main:app –reload –port 8001
【4:図書館司書AIの最小形が完成】
こんな感じのコード↓で完成。言うまでもなく、全部ちゃっぴーが作ってくれました。
ばんざーい。全くプログラミング出来なくても、こんなの出来ちゃうのね。
ほんと凄い世の中になったもんだ。
あとはファイルが増えてきたら、学習させるかどうか考えます。
次は画像認識とか、音声読み上げとか、WEB巡回だとかですかね。
1個づつ追加してこうと思います。
from fastapi import FastAPI
import requests
import os
import random
#from config import EXTERNAL_TRIGGERS
from config_books import EXTERNAL_TRIGGERS as BOOK_TRIGGERS
from config_memory import EXTERNAL_TRIGGERS as MEMORY_TRIGGERS
from config_persona import EXTERNAL_TRIGGERS as PERSONA_TRIGGERS
ALL_TRIGGERS = {
**BOOK_TRIGGERS,
**MEMORY_TRIGGERS,
**PERSONA_TRIGGERS,
}
app = FastAPI()
def load_system_prompt(path="system_prompt.txt"):
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return ""
# LM Studio
LM_STUDIO_API = "http://127.0.0.1:1234/v1/chat/completions"
MODEL_NAME = "dummy-model"
# --- SillyTavern用:モデル ---
@app.get("/v1/models")
def get_models():
return {
"data": [
{
"id": MODEL_NAME,
"object": "model"
}
]
}
# --- テキストファイル読み込み ---
def load_external_text(user_message: str):
for keyword, rule in ALL_TRIGGERS.items():
if keyword in user_message:
folder = rule["folder"]
num = rule.get("num_files", 1)
if not os.path.isdir(folder):
return None
files = [
os.path.join(folder, f)
for f in os.listdir(folder)
if f.endswith(".txt")
]
if not files:
return None
chosen = random.sample(files, min(num, len(files)))
texts = []
for path in chosen:
with open(path, "r", encoding="utf-8") as f:
texts.append(f.read())
return "\n\n".join(texts)
return None
# --- SillyTavern エンドポイント ---
@app.post("/v1/chat/completions")
def chat_completions(req: dict):
original_messages = req["messages"]
user_message = original_messages[-1]["content"]
external_text = load_external_text(user_message)
if external_text:
print("本のテキストを読み込みました")
# system_prompt
system_prompt = load_system_prompt()
messages = []
# system_prompt
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
# 資料
if external_text:
messages.append({
"role": "system",
"content": "以下は参考資料です。\n\n" + external_text
})
# SillyTavern側の会話履歴を合流
messages.extend(original_messages)
payload = {
"model": MODEL_NAME,
"messages": messages,
}
res = requests.post(LM_STUDIO_API, json=payload)
data = res.json()
reply = data["choices"][0]["message"]["content"]
# OpenAI互換レスポンス
return {
"id": "chatcmpl-cloudbot",
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": reply
},
"finish_reason": "stop"
}
]
}
