Skip to content

Commit 3e55741

Browse files
feat: Add local LLM support (v1.1)
Merge branch 'feature/local-llm-support' Switch all LLM calls from Claude CLI to local LLM (OpenAI-compatible API) for information security — meeting minutes and Slack messages are no longer sent to external services. Key changes: - cli_utils.py: call_claude() routes to call_local_llm() when OPENAI_API_BASE is set; includes strip_think_blocks(), load_claude_md_context() - generate_minutes_local.py: copied from Minutes repo; high-quality meeting minutes generation with streaming, CoT stripping, multi-stage processing - pm_from_recording.sh: rewritten for local execution (no SLURM); new pipeline: generate_minutes_local.py → pm_minutes_import.py --no-llm → pm_minutes_to_pm.py - pm_minutes_import.py: table-format action items parser, project context in prompt - pm_from_slack.sh: OPENAI_* env vars added - pm_extractor.py, pm_report.py: NULL content guards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents 003b97b + df0d9c2 commit 3e55741

9 files changed

Lines changed: 1202 additions & 137 deletions

CLAUDE.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Slackの日常的なやり取りと会議議事録を統合し、決定事項・
5656
(LLM不使用) (担当者・期限を直接転記)
5757
```
5858

59-
`pm_from_recording.sh --meeting-name``pm_minutes_import.py` `pm_minutes_to_pm.py` の順で呼び出す。
59+
`pm_from_recording.sh --meeting-name``generate_minutes_local.py`(ローカルLLMで高品質議事録生成)→ `pm_minutes_import.py --no-llm`(DB保存)`pm_minutes_to_pm.py`(pm.db転記)の順で呼び出す。
6060

6161
**各DBの役割分担**:
6262
- `{channel_id}.db` — Slackデータ専用。チャンネルごとに独立。
@@ -83,9 +83,10 @@ slack/
8383
│ ├── pm_goals_import.py # goals.yaml → pm.db 完全同期
8484
│ ├── canvas_utils.py # Slack Canvas 操作の共通ユーティリティ(sanitize_for_canvas・post_to_canvas・セクション削除ロジック)
8585
│ ├── db_utils.py # DB接続の一元管理・平文DB暗号化変換(SQLCipher対応)。open_pm_db・fetch_milestone_progress・fetch_assignee_workload も提供
86-
│ ├── cli_utils.py # 共通CLIユーティリティ(argparse ヘルパー・make_logger・load_claude_md・call_claude)
86+
│ ├── cli_utils.py # 共通CLIユーティリティ(argparse ヘルパー・make_logger・load_claude_md・call_claude)。OPENAI_API_BASE が設定されている場合はローカルLLMを使用(call_local_llm 経由)
87+
│ ├── generate_minutes_local.py # ローカルLLMを使って文字起こしから高品質議事録を生成。マルチステージ処理・CoT除去・ストリーミング対応。../Minutes/scripts/ からコピー
8788
│ ├── pm_from_recording_auto.sh # data/*.m4a を検出して pm_from_recording.sh を自動投入。-c CHANNEL_ID でSlack投稿も自動化
88-
│ ├── pm_from_recording.sh # 会議録音をテキスト化するSlurmジョブスクリプト。文字起こし後 pm_minutes_import.py → pm_minutes_to_pm.py を自動実行
89+
│ ├── pm_from_recording.sh # 会議録音をローカルで処理するスクリプト。文字起こし後 generate_minutes_local.py → pm_minutes_import.py --no-llm → pm_minutes_to_pm.py を自動実行
8990
│ ├── pm_from_slack.sh # Slack取得・要約 → pm.db抽出を連続実行(slack_pipeline.py + pm_extractor.py)
9091
│ ├── canvas_report.sh # Canvas同期 → PMレポート生成・Canvas投稿(pm_sync_canvas.py + pm_report.py)
9192
│ ├── slack_post_minutes.sh # 議事録DBの内容をSlackチャンネルに投稿(pm_minutes_import.py --post-to-slack)
@@ -119,16 +120,21 @@ python3 scripts/slack_pipeline.py ...
119120

120121
ローカルLLM(OpenAI互換API)を使う場合:
121122
```sh
122-
export OPENAI_API_BASE="http://..."
123-
export OPENAI_API_KEY="..."
124-
export OPENAI_MODEL="..."
123+
export OPENAI_API_BASE="http://localhost:8000/v1" # vLLM エンドポイント
124+
export OPENAI_API_KEY="dummy" # 認証不要のローカルサーバは "dummy" で可
125+
export OPENAI_MODEL="google/gemma-4-26B-A4B-it" # サーバに読み込まれているモデル名
126+
export OPENAI_MAX_TOKENS="8192" # Slack 要約用(pm_extractor・slack_pipeline)
125127
```
126128

129+
`OPENAI_API_BASE` が設定されている場合、`call_claude()` は Claude CLI の代わりにローカルLLMを呼び出す(`cli_utils.py``call_local_llm()` 経由)。会議録音処理(`pm_from_recording.sh`)はこれらの変数をスクリプト内でハードコードしているため、別途設定は不要。
130+
131+
`OPENAI_MAX_TOKENS` は Slack 要約・抽出(`pm_extractor.py``slack_pipeline.py`)の最大出力トークン数。会議録音処理では `generate_minutes_local.py``--max-tokens 16384` が使われるため本変数は影響しない。
132+
127133
---
128134

129135
## 注意事項
130136

131-
- `claude -p` はClaude Codeセッション内からは実行不可(ネストセッション制限)。各スクリプトはClaude Codeの外のターミナルから実行すること。
137+
- `claude -p` はClaude Codeセッション内からは実行不可(ネストセッション制限)。各スクリプトはClaude Codeの外のターミナルから実行すること。ローカルLLM(`OPENAI_API_BASE` 設定時)はこの制限を受けない。
132138
- `call_claude()` 内で `CLAUDECODE` 環境変数を子プロセスから除外する処理を実装済み。
133139
- `slack-mcp-server` は不要(`slack_pipeline.py` が Slack SDK に移行済み)。
134140
- Python仮想環境は `~/.venv_x86_64` を使用。`~/.venv_x86_64/bin/python3 scripts/xxx.py` で実行する。

README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ LLMは「情報処理の自動化」に使い、「何を目指すか」「ど
4444

4545
## 概要
4646

47-
会議議事録(Whisper文字起こし)とSlackチャンネルの投稿から、LLM(Claude)を使って決定事項とアクションアイテムを自動抽出し、SQLiteデータベース(pm.db)に蓄積する。蓄積した情報を週次進捗レポートとしてSlack Canvasに投稿し、Canvas上で対応状況を記入することでDBを更新するワークフローを提供する。
47+
会議議事録(Whisper文字起こし)とSlackチャンネルの投稿から、**ローカルLLM**(OpenAI互換API経由)を使って決定事項とアクションアイテムを自動抽出し、SQLiteデータベース(pm.db)に蓄積する。情報セキュリティ上の理由から、議事録・Slackメッセージ等の機密情報は外部サービスに送出せず、R-CCS内で稼働するローカルLLM(vLLMサーバ)で処理する。`OPENAI_API_BASE` 未設定時は Claude CLI(`claude -p`)にフォールバックする。蓄積した情報を週次進捗レポートとしてSlack Canvasに投稿し、Canvas上で対応状況を記入することでDBを更新するワークフローを提供する。
4848

4949
---
5050

@@ -60,7 +60,7 @@ LLMは「情報処理の自動化」に使い、「何を目指すか」「ど
6060
|---|---|
6161
| `pm_from_slack.sh` | Slack Channel から投稿を取得、スレッド単位で要約し pm.db へ登録 |
6262
| `pm_from_recording_auto.sh` | 会議録音データ(m4aフォーマット)から文字起こしを行い、議事録を作成し pm.db へ登録 |
63-
| `pm_from_recording.sh` | 会議録音データ(m4aフォーマット)から文字起こしを実施 |
63+
| `pm_from_recording.sh` | 会議録音データ(m4aフォーマット)から文字起こしを実施し、ローカルLLMで議事録を生成して pm.db へ登録 |
6464
| `canvas_report.sh` | Canvas同期 → PMレポート生成・Canvas投稿(上書き事故防止のため同期が先に実行される) |
6565
| `slack_post_minutes.sh` | 議事録DBの内容をSlackチャンネルに投稿 |
6666
| `pm_goals_import.py` | `goals.yaml` を pm.db に完全同期(ゴール・マイルストーン管理) |
@@ -75,7 +75,8 @@ LLMは「情報処理の自動化」に使い、「何を目指すか」「ど
7575
| `pm_relink.py` | アクションアイテムの各フィールド(担当者・期限・内容・マイルストーン・status)をCSV経由で一括編集(LLM不使用)。`note`列は参照用として出力 |
7676
| `canvas_utils.py` | Slack Canvas 操作の共通ユーティリティ(`sanitize_for_canvas``post_to_canvas`・セクション削除ロジック)。各スクリプトから内部利用 |
7777
| `db_utils.py` | DB接続の一元管理・平文DBの暗号化変換(SQLCipher対応)。`open_pm_db``fetch_milestone_progress``fetch_assignee_workload``normalize_assignee` も提供 |
78-
| `cli_utils.py` | 共通CLIユーティリティ(argparse ヘルパー・`make_logger``call_claude`)。各スクリプトから内部利用 |
78+
| `generate_minutes_local.py` | ローカルLLMを使った高品質議事録生成(ストリーミング・CoT除去・多段階処理対応)。`pm_from_recording.sh` から呼び出される |
79+
| `cli_utils.py` | 共通CLIユーティリティ(argparse ヘルパー・`make_logger``call_claude`)。`OPENAI_API_BASE` が設定されている場合はローカルLLMにルーティング。各スクリプトから内部利用 |
7980

8081
---
8182

@@ -154,7 +155,8 @@ bash scripts/pm_from_recording.sh file1.m4a [file2.mp4 ...] [--meeting-name NAME
154155
```
155156

156157
注意事項:
157-
- `--meeting-name` を指定すると文字起こし後に `pm_minutes_import.py`(議事録DB保存)→ `pm_minutes_to_pm.py`(pm.db転記)の順で自動実行し、.md ファイルを削除する(平文ファイルがディスクに残らない)。
158+
- `--meeting-name` を指定すると文字起こし後に `generate_minutes_local.py`(ローカルLLMで議事録生成)→ `pm_minutes_import.py --no-llm`(議事録DB保存)→ `pm_minutes_to_pm.py`(pm.db転記)の順で自動実行し、.md ファイルを削除する(平文ファイルがディスクに残らない)。
159+
- ローカルLLMのエンドポイントは環境変数 `OPENAI_API_BASE``OPENAI_API_KEY``OPENAI_MODEL` で指定(`~/.secrets/slack_tokens.sh` に記載推奨)。
158160
- 推奨: 議事録DBとpm.dbの両方に保存(.md は削除)
159161
```sh
160162
bash scripts/pm_from_recording.sh GMT20260302-032528_Recording.mp4 --meeting-name Leader_Meeting
@@ -165,7 +167,7 @@ bash scripts/pm_from_recording.sh GMT20260302-032528_Recording.mp4 --meeting-nam
165167
```
166168
- 冒頭スキップ
167169
```sh
168-
sbatch scripts/pm_from_recording.sh GMT20260302-032528_Recording.mp4 --skip 30 --meeting-name Leader_Meeting
170+
bash scripts/pm_from_recording.sh GMT20260302-032528_Recording.mp4 --skip 30 --meeting-name Leader_Meeting
169171
```
170172
- インポート後の確認・削除:
171173
```sh
@@ -381,7 +383,8 @@ python3 scripts/db_utils.py --audit-log --output audit.txt # ファイルにも
381383
### 動作要件
382384

383385
- Python 3.10 以上
384-
- Claude Code(`claude` コマンド): [インストール手順](https://docs.anthropic.com/en/docs/claude-code)
386+
- **ローカルLLM(推奨)**: OpenAI互換APIサーバ(vLLM等)を `http://localhost:8000/v1` 等で起動し、環境変数 `OPENAI_API_BASE``OPENAI_API_KEY``OPENAI_MODEL` を設定する。機密情報を外部送信しないために必須。
387+
- **Claude CLI(フォールバック)**: `OPENAI_API_BASE` 未設定時のみ使用。Claude Code(`claude` コマンド)が必要: [インストール手順](https://docs.anthropic.com/en/docs/claude-code)
385388
- 文字起こし機能(`whisper_vad.py`)を使用する場合: GPU環境(NVIDIA L40S / GH200 等)および Singularity コンテナ
386389

387390
### Python環境
@@ -430,11 +433,19 @@ Slack の「Settings & administration」→「Manage apps」または [api.slack
430433
mkdir -p ~/.secrets && chmod 700 ~/.secrets
431434
cat > ~/.secrets/slack_tokens.sh << 'EOF'
432435
export SLACK_USER_TOKEN="xoxp-..." # 全スクリプト共通(xoxp- ユーザートークン)
436+
437+
# ローカルLLM設定(OPENAI_API_BASE を設定するとローカルLLMを使用)
438+
export OPENAI_API_BASE="http://localhost:8000/v1"
439+
export OPENAI_API_KEY="dummy"
440+
export OPENAI_MODEL="google/gemma-4-26B-A4B-it"
433441
EOF
434442
chmod 600 ~/.secrets/slack_tokens.sh
435443
```
436444

437445
- `SLACK_USER_TOKEN`: 全スクリプト共通(Canvas投稿・Slack取得・ファイルアップロード)。xoxp- ユーザートークンを使用する。
446+
- `OPENAI_API_BASE`: ローカルLLMのエンドポイント。設定するとすべての LLM 呼び出しがローカルLLMにルーティングされる(未設定時は Claude CLI フォールバック)。
447+
- `OPENAI_API_KEY`: ローカルLLMのAPIキー(vLLMは認証不要なため `dummy` で可)。
448+
- `OPENAI_MODEL`: 使用するモデル名(vLLM の場合はモデルのパスまたは HuggingFace モデルID)。
438449

439450
---
440451

@@ -468,7 +479,7 @@ python3 scripts/db_utils.py --migrate data/pm.db --no-backup
468479

469480
## 注意事項
470481

471-
- `claude -p` はClaude Codeセッション内からは実行不可(ネストセッション制限)。`pm_minutes_import.py``pm_extractor.py``pm_report.py`**Claude Codeの外のターミナル**から実行すること。
482+
- ローカルLLMモード(`OPENAI_API_BASE` 設定時)は Claude Code セッション内からも実行可能。Claude CLI フォールバックモード(`OPENAI_API_BASE` 未設定時)は `claude -p` を使うためネストセッション制限があり、`pm_minutes_import.py``pm_extractor.py``pm_report.py`**Claude Codeの外のターミナル**から実行すること。
472483
- Slack Canvasは表示できない文字(特殊Unicode)を含むとAPIエラーになる。スクリプト内で`sanitize_for_canvas()`による除去処理を実施済み。
473484
- `pm_report.py` および `slack_pipeline.py --canvas-id` は実行のたびにCanvasを全置換する(`url_private` のHTMLからセクションIDを全件取得→全削除→新規挿入)。手動での事前削除は不要。
474485
- Slack DBはチャンネルごとに独立(`data/{channel_id}.db`)。pm.dbは複数チャンネル・複数会議を横断して統合する。

0 commit comments

Comments
 (0)