★ Architecture
Raspberry Pi 5 · 完全ローカル · 猫キャラクター家電AIエージェント
システム概要
6–9tok/s テキスト
~10sレイテンシ ツール無
~30sレイテンシ ツール有
3.4GBllama-server RAM
6ツール数
7SwitchBot devices
768d埋込ベクトル次元
30–50自律呟き / 日
全体アーキテクチャ
すべての入力・処理・出力・接続の関係
graph LR
subgraph HW["HARDWARE — Pi 5 + Whisplay HAT"]
MIC["Mic · WM8960"]
BTN["Button · GPIO"]
end
subgraph SENSE["SENSORS + CAMERA"]
SBM["Meter Pro · 温湿度"]
CAMDEV["Pan/Tilt Cam 2K · RTSP+ONVIF"]
end
subgraph NEKO["neko.py — メインループ"]
VAD["silero-vad · 16kHz"]
SM["State Machine · 5 states"]
SHRT["deque x 5 · 短期記憶"]
end
subgraph AUTOMOD["autonomous.py"]
ALOOP["15-90min 乱数ループ"]
end
subgraph INFER["llama-server :8080"]
GEMMA["Gemma 4 E2B · Q4_K_M + mmproj-BF16 · 6-9 tok/s"]
end
subgraph ADK_MOD["agent.py — Google ADK + LiteLLM"]
AGENT["LlmAgent · function calling"]
end
subgraph TOOLS_MOD["tools.py — 6 Tools"]
T1["observe(target)"]
T2["control_device(name, action)"]
T3["remember(query)"]
T4["activity(op, ...)"]
T5["broadcast(channel, text)"]
T6["web_search(query)"]
end
subgraph STORE["STORAGE · memory.db"]
CONV[("conversations + conv_vec 768d")]
ACTDB[("activities")]
end
subgraph SBDEV["SwitchBot v1.1 — 7 devices"]
BULB["Color Bulb 金"]
CUR["Curtain 0-100%"]
BOT["Bot 開錠"]
HUB["Hub Mini -> IR AC"]
end
subgraph OUTCH["OUTPUT CHANNELS"]
VOICEVOX["VOICEVOX :50021 · ずんだもん"]
BSKY["Bluesky · atproto"]
MOLT["Moltbook · Bearer"]
ALEXA["Alexa · Voice Monkey"]
end
MIC --> VAD --> SM
BTN --> SM
SM --> AGENT
ALOOP --> AGENT
SHRT --> AGENT
AGENT <--> GEMMA
AGENT --> T1 & T2 & T3 & T4 & T5 & T6
T1 --> SBM & CAMDEV
T2 --> BULB & CUR & BOT & HUB
T3 --> CONV
T4 --> ACTDB
T5 --> VOICEVOX & BSKY & MOLT & ALEXA
会話データフロー
1ターンのシーケンス — 録音から発話まで
sequenceDiagram
actor U as ユーザー / Button
participant VAD as silero-vad
participant SM as State Machine
participant LLAMA as llama-server
participant ADK as ADK Agent
participant TOOLS as Tools x6
participant TTS as VOICEVOX
participant LCD as LCD + LED
U->>VAD: 発話 or ボタン押下
VAD->>SM: speech_start (3ブロック検出)
SM->>LCD: RECORDING(赤LED)
VAD->>SM: speech_end(1秒無音 / 10秒経過)
SM->>LCD: THINKING(黄LED)
SM->>LLAMA: 16kHz WAV base64 -> 書き起こし要求
LLAMA-->>ADK: 日本語テキスト
ADK->>LLAMA: テキスト + tools=[6] + system_prompt
loop ツール呼び出し(0-N回)
LLAMA-->>ADK: tool_calls JSON
ADK->>TOOLS: 実行
TOOLS-->>ADK: 結果テキスト
ADK->>LLAMA: 結果 + continue
end
LLAMA-->>ADK: 最終回答テキスト
ADK->>TTS: 日本語 1-3文
SM->>LCD: SPEAKING(緑LED)
TTS-->>U: 24kHz WAV 音声再生
SM->>LCD: LISTENING(青LED)
状態機械
5状態 · LED色 · LCD吹き出し · 遷移条件
stateDiagram-v2
direction LR
[*] --> IDLE : 起動
IDLE --> LISTENING : マイク常時開放
LISTENING --> RECORDING : VAD 3block or Button押下
RECORDING --> THINKING : 1秒無音 / 10秒経過
THINKING --> SPEAKING : 意味ある応答取得
THINKING --> LISTENING : skip / JSON漏れ / 空応答
SPEAKING --> LISTENING : WAV再生終了
| 状態 | LED | LCD 表示 | 遷移条件 |
|---|---|---|---|
| IDLE | 暗青 呼吸 | 「高所より人間を観察中である」 | 起動 → LISTENING |
| LISTENING | 明青 | 「声を待っておる」 | VAD 3block or BTN → RECORDING |
| RECORDING | 赤 | 「聞いておるぞ…」 | 無音1s / 10s経過 → THINKING |
| THINKING | 黄 | 「聞いた: <書き起こし>」 | 応答 → SPEAKING / skip → LISTENING |
| SPEAKING | 緑 | 応答テキスト | 再生終了 → LISTENING |
自律ループ
猫が勝手に観察・呟く仕組み — autonomous.py
flowchart TD
START(["起動 60秒待機"])
BUSY{"busy?
RECORDING / THINKING / SPEAKING"}
SKIP["20秒スキップ"]
RESET["reset_session()"]
PROMPT["OBSERVATION_PROMPT 送信"]
RESULT{"出力タイプ"}
SOCIAL["broadcast('social')
-> Bluesky / Moltbook"]
VOICE["broadcast('voice')
-> VOICEVOX スピーカー"]
SILENT["沈黙 (skip)"]
WAIT(["15-90分 乱数スリープ"])
START --> BUSY
BUSY -->|Yes| SKIP --> BUSY
BUSY -->|No| RESET --> PROMPT --> RESULT
RESULT -->|social| SOCIAL --> WAIT
RESULT -->|voice| VOICE --> WAIT
RESULT -->|skip| SILENT --> WAIT
WAIT --> BUSY
ツール — 6個
ADK Agent が呼び出すすべてのツール — tools.py
observe(target)
センサ読取・カメラ描写・家電一覧・PTZ首振りを統合
"room""view""devices""meter:NAME""look:DIR"
control_device(name, action, ...)
SwitchBot 7デバイス + Hub Mini IR を1関数で統合制御
"on"/"off""brightness""curtain""color""aircon"
remember(query, limit)
SQLite + sqlite-vec ベクトル類似検索で過去会話を想起
gemini-embedding-001768次元
activity(op, ...)
人間活動 DB の CRUD。食事・作業・睡眠等を記録・検索・編集
"record""list""search""get""update""delete"
broadcast(channel, text)
外部発信チャネルへの統合インターフェース
"voice""social""alexa_say""alexa_routine"
web_search(query)
Tavily → Brave → DuckDuckGo フォールバックチェーン
TavilyBraveDuckDuckGo
ファイル構成
neko/
src/
neko.pyメインループ · VAD · 状態機械 · web_api 起動
agent.pyADK LlmAgent + LiteLLM → llama-server :8080
tools.py6ツール定義 + @_trace デコレータ
memory.py短期 deque + 長期 SQLite+sqlite-vec+Gemini埋込
activity.py人間活動 DB CRUD (activities テーブル)
home_control.pySwitchBot デバイス抽象化レイヤー
switchbot.pySwitchBot v1.1 API · HMAC-SHA256署名
camera.pyRTSP取得 + ONVIF PTZ制御 · cv2 / PIL
whisplay_ui.pyLCD / LED / Button · rpi-lgpio + PIL 描画
tts.pyVOICEVOX → pyopenjtalk フォールバック
autonomous.py15〜90分乱数の自律観察ループ
web_api.pyHTTP API :4001 · /ask /broadcast /tool /ui_state
scripts/systemd/4 services
.env.localシークレット(git 管理外)
systemd サービス
Pi 起動時に自動立ち上がる 4 サービス
llama-server.service
:8080 · OpenAI互換API
Gemma 4 E2B Q4_K_M + mmproj-BF16。音声・画像・テキスト 3モーダル入力。
voicevox-engine.service
:50021 · REST API
VOICEVOX 0.25.2 ARM64。ずんだもん (id=3)。43話者対応。
neko.service
:4001 · Web API
メインアプリ。VAD常時待機・ADKエージェント・自律ループ・Web API 同居。
neko-bt-autoconnect.service
Bluetooth
Echo Dot BT自動再接続。bluez_output 経由で音声出力。
| コマンド | 用途 |
|---|---|
ssh neko-pi | Pi接続 |
sudo systemctl restart neko.service | メインアプリ再起動 |
sudo journalctl -u neko.service -f | リアルタイムログ追跡 |
curl http://localhost:8080/health | llama-server ヘルスチェック |
rsync -avz --exclude='.git' --exclude='venv' ./ neko-pi:~/neko/ | Mac → Pi 同期 |