はじめに
社内でMacBook Proを1台購入し、ローカルLLMを動かせる環境を用意しました。
せっかく社内ネットワーク内でAIを動かせるなら、RedmineやSlackの情報を参照して、プロジェクトの進捗をまとめてもらいたいところです。
そこで今回は、以下を組み合わせた構成を考えます。
- ローカルLLM
- MCP
- LangGraph
目標は、
「プロジェクトAの進捗どう?」
と聞くだけで、RedmineとSlackを横断して状況をまとめてくれる社内向けAIです。
1. MCPで社内システムに接続する
MCP(Model Context Protocol)は、LLMと外部ツールを接続するためのプロトコルです。
今回の構成は次のようになります。
┌──────────────┐
│ LLM │
└──────┬───────┘
│
│ MCP
▼
┌──────────────┐
│ MCP Server │
└──────┬───────┘
│
┌──────────┴──────────┐
▼ ▼
┌────────┐ ┌────────┐
│ Redmine│ │ Slack │
└────────┘ └────────┘
MCP Serverでは、例えば次のようなツールを提供します。
get_issue(issue_id)
search_issues(query)
list_open_issues()
search_messages(query)
get_thread(channel_id, thread_ts)
get_channel_history(channel_id)
LLMはこれらのツールを呼び出し、RedmineやSlackの情報を取得します。
2. 最初は読み取り専用にする
社内データを扱うため、最初のPoCでは読み取り専用にします。
Redmine
├── list_issues ○
├── get_issue ○
├── search_issues ○
├── create_issue ×
├── update_issue ×
└── delete_issue ×
Slack
├── search_messages ○
├── get_thread ○
├── get_history ○
└── send_message ×
重要なのは、プロンプトで「書き込み禁止」と指示するのではなく、MCP Serverに書き込み用ツールを実装しないことです。
これなら、LLMが誤って判断しても、RedmineやSlackを変更する操作自体を呼び出せません。
3. LangGraphで処理フローを組み立てる
MCPはツールを提供しますが、ツールを呼ぶ順番や、結果をどう処理するかまでは管理しません。
そこでLangGraphを使います。
┌─────────────┐
│ User │
└──────┬──────┘
▼
┌─────────────┐
│ LangGraph │
│ Agent │
└──────┬──────┘
│
▼
┌─────────────┐
│ LLM │
│ oMLX │
└──────┬──────┘
│
Tool Call
│
▼
┌─────────────┐
│ MCP Client │
└──────┬──────┘
│
▼
┌─────────────┐
│ MCP Server │
└──────┬──────┘
│
┌────────┴────────┐
▼ ▼
┌────────┐ ┌────────┐
│Redmine │ │ Slack │
└────────┘ └────────┘
例えば、次のような処理をGraphとして定義できます。
LLM
↓
Redmineを検索
↓
結果を整理
↓
Slackを検索
↓
情報を比較
↓
進捗レポートを生成
4. LLMはどうやってツールを知るのか
LLMがMCP Serverを直接知っているわけではありません。
MCP ClientがMCP Serverからツール定義を取得し、それをLangGraph経由でLLMに渡します。
例えば、ツール定義は次のような形式です。
{
"name": "get_issue",
"description": "Get a Redmine issue",
"input_schema": {
"type": "object",
"properties": {
"issue_id": {
"type": "integer"
}
},
"required": ["issue_id"]
}
}
LangGraph側では、概念的には次のようにLLMへツールをバインドします。
tools = mcp_client.get_tools()
llm_with_tools = llm.bind_tools(tools)
流れは次のとおりです。
MCP Server
│
│ tools/list
▼
MCP Client
│
│ ツール定義
▼
LangGraph
│
│ bind_tools()
▼
LLM
LLMはRedmine APIやSlack APIを直接扱うのではなく、get_issue や search_messages といったツールとして認識します。
5. Tool Callの流れ
ユーザーが、
「プロジェクトAの未完了チケットを教えて」
と質問したとします。
LLMは search_issues を使うと判断し、次のようなTool Callを返します。
{
"name": "search_issues",
"arguments": {
"status": "open",
"project": "project-a"
}
}
LangGraphはこのTool Callを受け取り、MCP経由でRedmineを検索します。
LLM
↓
search_issues
↓
MCP Client
↓
MCP Server
↓
Redmine
取得結果はLLMに戻され、最終的な回答が生成されます。
Redmine
↓
MCP Server
↓
MCP Client
↓
LangGraph
↓
LLM
↓
回答
6. LangGraphの基本的な構成
Agentの処理は、単純化すると次のようになります。
START
│
▼
┌──────────┐
│ LLM │
└────┬─────┘
│
├── 通常回答 ──────► END
│
└── Tool Call
│
▼
┌──────────┐
│ ToolNode │
└────┬─────┘
│
▼
MCP
│
▼
Redmine
│
▼
┌──────────┐
│ LLM │
└────┬─────┘
│
▼
END
単純なチャットなら User → LLM → Answer で済みます。
一方、RedmineとSlackを順番に検索したり、結果を比較したりする場合は、LangGraphで処理を明示的に管理できます。
7. 目指す構成
最終的には、次のような処理を実現します。
ユーザー
「プロジェクトAの進捗どう?」
↓
LangGraph
↓
LLM
↓
┌──────┴──────┐
▼ ▼
Redmine Slack
│ │
▼ ▼
チケット 会話
│ │
└──────┬──────┘
▼
LLM
↓
進捗を分析
↓
回答
回答例は次のようなものです。
プロジェクトAは現在78%のチケットが完了しています。
未完了チケットのうち3件は期限を超過しています。
SlackではAPI開発の遅延を示すやり取りが確認されており、Redmine上の期限とSlack上の認識にも差があります。
8. 計算はプログラム、解釈はLLM
進捗率のような計算は、LLMに任せずプログラムで処理します。
全チケット: 100
完了: 75
であれば、
progress = completed / total
で計算します。
LLMに任せるのは、次のような処理です。
- Slackの文脈を理解する
- チケットの状況を要約する
- 遅延リスクを読み取る
- 情報同士の矛盾を見つける
- 人間向けの報告文にまとめる
役割分担は次のとおりです。
正確な処理
↓
プログラム
意味理解・要約・推論
↓
LLM
9. ローカルLLMを使う理由
今回扱うのは、RedmineやSlackなどの社内データです。
これらを外部のLLM APIへ送信する場合、セキュリティやデータ取り扱いを検討する必要があります。
一方、社内ネットワーク内で処理を完結できれば、社内データを外部に出さずに済みます。
Slack
↓
MCP
↓
LangGraph
↓
oMLX
↓
ローカルLLM
ただし、ローカルだから自動的に安全というわけではありません。
- APIキーの管理
- MCP Serverの認証
- ネットワーク制御
- ログ・監査
- 最小権限
- 読み取り専用
といった対策は必要です。
10. 構成要素の役割
| コンポーネント | 役割 |
|---|---|
| oMLX | ローカルLLMを動かす |
| LLM | 推論・自然言語理解 |
| LangGraph | Agentの処理フローを制御 |
| MCP Client | MCP Serverと接続 |
| MCP Server | AIにツールを提供 |
| Redmine | プロジェクト情報 |
| Slack | コミュニケーション情報 |
全体の構成は次のとおりです。
┌──────────────┐
│ User │
└──────┬───────┘
│
▼
┌──────────────┐
│ LangGraph │
│ Agent │
└──────┬───────┘
│
▼
┌──────────────┐
│ LLM │
│ oMLX │
└──────┬───────┘
│
Tool Calling
│
▼
┌──────────────┐
│ MCP Client │
└──────┬───────┘
│
▼
┌──────────────┐
│ MCP Server │
└──────┬───────┘
│
┌────────┴────────┐
▼ ▼
┌────────┐ ┌────────┐
│Redmine │ │ Slack │
└────────┘ └────────┘
おわりに
今回の構成では、次のように役割を分担します。
- oMLX:ローカルLLMを動かす
- MCP:RedmineやSlackをツールとして公開する
- LangGraph:ツール呼び出しや処理フローを制御する
まずは読み取り専用でRedmineのチケット検索から始め、最終的にはRedmineとSlackを横断した進捗報告Agentを目指します。
次のステップでは、実装編に移りたいと思います。