Agent (Python)
役割
AgentはStarnionのAIの脳です。Pythonで書かれており、LangGraph ReActアーキテクチャ上で動作します。GatewayからのgRPCリクエストを受け取り、AIの推論とスキルの実行、メモリの取得を行い、最終的な応答を返します。
コアの役割:
- ユーザーメッセージを分析して意図を理解する
- 適切なスキル(ツール)を選択して実行する
- 4層メモリシステムを検索して関連情報を取得する
- マルチLLMルーティング(ユーザー設定に基づいてモデルを選択)
- gRPCストリーミングを通じてリアルタイム応答を配信する
LangGraph ReActアーキテクチャ
AgentはLangGraphのReAct(Reasoning + Acting)パターンを使用します。
ユーザーメッセージ
│
▼
┌─────────────────────────────────────────┐
│ ReActループ │
│ │
│ ┌──────────┐ 思考 │
│ │ LLM │──────────────────────┐ │
│ │(推論) │ │ │
│ └──────────┘ ▼ │
│ ▲ ┌─────────────────┐│
│ │ 観察 │ スキル選択 ││
│ │ │(ツール選択) ││
│ ┌────┴───────┐ └────────┬────────┘│
│ │ スキル │ │ 実行 │
│ │ 結果 │◄──────────────┘ │
│ │(ツール結果)│ │
│ └────────────┘ │
│ │
│ [繰り返し:追加スキルが必要な場合は続行] │
└─────────────────────────────────────────┘
│ 最終応答が決定
▼
gRPCストリーミング応答
動作フローの概要
- 入力の受信:GatewayからgRPCリクエストを受信(ユーザーメッセージ + 会話ID + ユーザーID)
- コンテキストの読み込み:会話履歴、ユーザープロフィール、現在のペルソナを読み込む
- メモリ検索:関連情報を4層メモリで検索(pgvector類似性検索)
- LLM推論:システムプロンプト + 会話履歴 + メモリコンテキストをLLMに渡す
- スキル実行:LLMが必要なスキルを選択したら、対応する関数を実行する
- ループ:スキル結果に基づいて追加推論が必要な場合はループを繰り返す
- ストリーム応答:最終的な回答をgRPCストリームでリアルタイムに送信する
- メモリの保存:会話内容をデイリーログに記録する
メッセージ処理フロー
ユーザー入力:「今月の食費はいくら?」
│
▼
[意図を特定]
→ 「費用照会」の意図を検出
│
▼
[メモリ検索]
→ 関連する費用データを検索(レイヤー4:SQL)
→ 以前の類似の質問をメモリで検索(レイヤー1:pgvector)
│
▼
[スキル選択]
→ get_finance_summary(category="food", period="this_month") を呼び出す
│
▼
[スキル実行]
→ DBから今月の食費取引を集計
→ 結果:{"total": 23450, "transactions": [...]}
│
▼
[LLM最終応答生成]
→ 「今月の食費は23,450円です。先月(19,800円)比18%増です。」
│
▼
[gRPCストリーミング]
→ 応答トークンをリアルタイムでGatewayにストリーミング
│
▼
[メモリの保存]
→ この会話をデイリーログに記録
マルチLLMルーティング
Agentは、ユーザーごとに登録されたLLMプロバイダーと現在選択されているペルソナに基づいて、どのモデルを呼び出すかを決定します。
モデル選択の優先順位
1. 現在の会話で明示的に選択されたモデル
↓ (なければ)
2. 現在のペルソナにリンクされたモデル
↓ (なければ)
3. ユーザーのデフォルトプロバイダーの最初のアクティブなモデル
↓ (なければ)
4. システムデフォルト(Gemini Flash)
対応プロバイダー
| プロバイダー | 実装 |
|---|---|
| Gemini | google-generativeai SDK |
| OpenAI | openai SDK(ChatCompletion API) |
| Anthropic | anthropic SDK(Messages API) |
| Z.AI | OpenAI互換エンドポイント |
| カスタム | OpenAI互換ベースURL |
4層メモリシステム
Agentは4つのレイヤーで構成されるメモリシステムを通じてユーザーコンテキストを管理します。
┌─────────────────────────────────────────────────────┐
│ 4層メモリ │
│ │
│ レイヤー1:デイリーログ │
│ ┌──────────────────────────────┐ │
│ │ pgvector、768次元埋め込み │ │
│ │ 会話記録、 │ │
│ │ 感情、キーワード │ │
│ └──────────────────────────────┘ │
│ ↑ 類似性検索 │
│ レイヤー2:ナレッジベース │
│ ┌──────────────────────────────┐ │
│ │ pgvector、768次元埋め込み │ │
│ │ ユーザーの好み、 │ │
│ │ 学習したパターン │ │
│ └──────────────────────────────┘ │
│ ↑ 類似性検索 │
│ レイヤー3:ドキュメントセクション │
│ ┌──────────────────────────────┐ │
│ │ pgvector、768次元埋め込み │ │
│ │ アップロードされたドキュメント│ │
│ │ のチャンク │ │
│ └──────────────────────────────┘ │
│ ↑ SQLクエリ │
│ レイヤー4:最近の財務 │
│ ┌──────────────────────────────┐ │
│ │ PostgreSQL SQL │ │
│ │ 過去30日間の取引 │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
レイヤー1:デイリーログ
- ストア:PostgreSQL + pgvector拡張機能
- 埋め込み次元数:768(Gemini
text-embedding-004) - コンテンツ:会話内容、感情状態、重要なキーワード、サマリー
- 検索方法:コサイン類似性ベースのセマンティック検索
- ユースケース:「前回何を言ったっけ?」のような過去の会話の想起
レイヤー2:ナレッジベース
- ストア:PostgreSQL + pgvector
- 埋め込み次元数:768
- コンテンツ:ユーザーの好み、繰り返しのパターン、学習したパーソナライズデータ
- ユースケース:「ユーザーはコーヒーが好き」や「毎月25日に給料が入る」などのパーソナライゼーションコンテキスト
レイヤー3:ドキュメントセクション
- ストア:PostgreSQL + pgvector
- 埋め込み次元数:768
- コンテンツ:ユーザーがアップロードしたPDF、Wordドキュメントなどのチャンク
- チャンキング方法:セマンティック単位に分割(デフォルト512トークン)
- ユースケース:「アップロードした契約書の違約金条項を探して」
レイヤー4:最近の財務
- ストア:PostgreSQL(プレーンSQL、ベクターなし)
- コンテンツ:過去30日間の取引
- 検索方法:SQL集計クエリ
- ユースケース:「今月の食費はいくら?」、「昨日カフェの費用はあった?」
埋め込み
すべてのベクター埋め込みにはGoogleの text-embedding-004 モデルを使用しています。
| 項目 | 値 |
|---|---|
| モデル | text-embedding-004 |
| 次元数 | 768 |
| 類似性関数 | コサイン類似性(<=> 演算子) |
| 言語 | 日本語を含む多言語 |
埋め込み生成フロー:
テキスト入力
│
▼
Gemini Embedding APIを呼び出す
│
▼
768次元浮動小数点ベクターを返す
│
▼
PostgreSQL pgvectorカラムに保存
(例:VECTOR(768))
gRPCインターフェース
Agentはデフォルトポート 50051 でgRPCサーバーとして動作します。
サービス定義(protobuf)
service AgentService {
// 単項チャットリクエスト/レスポンス
rpc Chat(ChatRequest) returns (ChatResponse);
// サーバーストリーミング:応答トークンをリアルタイムで送信
rpc ChatStream(ChatRequest) returns (stream ChatStreamResponse);
}
通信フロー
Gateway (Go) Agent (Python)
│ │
│── ChatRequest ──────────────►│
│ (message, user_id, │
│ conversation_id, │ ReActループが実行
│ context, files) │ スキル実行
│ │
│◄── ChatStreamResponse ────────│ (トークンごとのストリーミング)
│◄── ChatStreamResponse ────────│
│◄── ChatStreamResponse ────────│
│ ... │
│◄── [ストリーム終了] ──────────│
Gatewayはストリーミング応答を受け取り、WebSocketまたはSSE(Server-Sent Events)を通じてクライアントに配信します。
スキル実行メカニズム
スキルはLangChainツールとして実装されています。LLMがどのスキルをどのパラメータで呼び出すかをJSON形式で決定すると、Agentは対応するPython関数を実行します。
スキルカテゴリー
| カテゴリー | スキル例 |
|---|---|
| 財務管理 | 取引の追加/表示、予算確認、統計 |
| スケジュール | Googleカレンダー連携 |
| メモ | メモの作成/表示/削除 |
| 日記 | 日記の書き込み/表示 |
| 目標 | 目標の設定/チェックイン/評価 |
| Dデイ | Dデイの登録/表示 |
| ドキュメント | ドキュメント検索、PDFの要約 |
| ウェブ検索 | Tavily、Naver Search API |
| 天気 | 現在の天気検索 |
| 電卓 | 数式の計算 |
| 翻訳 | 多言語翻訳 |
スキルの有効化
スキルはユーザーごとに有効/無効を設定できます。無効化されたスキルはLLMのツールリストに含まれないため、まったく呼び出せません。
設定 → スキルのトグルか、API POST /api/v1/skills/:id/toggle で制御します。
Docker設定
Agentは docker/Dockerfile.agent を使用し、docker-compose.yml に以下のように定義されています。
agent:
build:
context: ../agent
dockerfile: ../docker/Dockerfile.agent
container_name: starnion-agent
ports:
- "${GRPC_PORT:-50051}:50051" # gRPCサーバー
environment:
DATABASE_URL: postgres://... # PostgreSQL接続
GRPC_PORT: 50051
depends_on:
postgres:
condition: service_healthy
AgentはPostgreSQLが準備できた後に起動します。GatewayはAgentが起動した後に接続を試みます。
技術スタックの概要
| 項目 | 選択 | バージョン |
|---|---|---|
| 言語 | Python | 3.13+ |
| AIオーケストレーション | LangGraph | 0.4+ |
| LLMクライアント | langchain-google-genai、langchain-anthropic、langchain-openai | latest |
| 会話状態ストレージ | langgraph-checkpoint-postgres | 2.0+ |
| DBドライバー | psycopg (psycopg3) + psycopg-pool | 3.2+ |
| gRPCサーバー | grpcio | 1.70+ |
| 画像生成/分析 | google-genai (Gemini) | 1.0+ |
| ドキュメント解析 | pypdf、python-docx、openpyxl、python-pptx | latest |
| ウェブ検索 | tavily-python | 0.5+ |
| ブラウザ自動化 | playwright | 1.40+ |
| QRコード | qrcode[pil] | 8.0+ |
| PDF生成 | reportlab | 4.4+ |
スキルアーキテクチャ
各スキルは独立したPythonパッケージとして実装されています。
agent/src/starnion_agent/skills/
├── finance/ # 費用管理
│ ├── __init__.py # スキル登録
│ ├── tools.py # LangChainツール関数の定義
│ └── SKILL.md # スキルの説明(LLMシステムプロンプトに注入)
├── weather/
│ ├── __init__.py
│ ├── tools.py
│ └── SKILL.md
├── loader.py # 動的スキルロード
├── guard.py # スキルアクセス権限チェック
└── registry.py # 全スキルレジストリ
SKILL.mdの役割
各スキルディレクトリの SKILL.md ファイルはLLMシステムプロンプトに直接注入されます。これにより、LLMは各スキルをいつ、どのように使用するかを正確に認識できます。
システムプロンプト = ベースペルソナ + アクティブなスキルのSKILL.mdコンテンツ
スキルガード
ユーザーによって無効化されたスキルは guard.py でブロックされます。非アクティブなスキルのツールはLLMに公開されないため、まったく呼び出すことが不可能です。
ログとHTTPサーバー
gRPCポート(50051)に加えて、AgentはHTTPサーバー(ポート8082)も実行します。
| ポート | 目的 |
|---|---|
50051 |
gRPCサーバー(Gatewayとの通信) |
8082 |
HTTPサーバー(ログストリーミング、ドキュメントインデックス、検索埋め込み) |
Gatewayの /api/v1/logs/agent エンドポイントはAgentのポート8082にプロキシして、リアルタイムのAgentログを提供します。