AI+SQLでSlackなどを操作できるMindsDBのアーキテクチャを読み解く
MindsDBというSQLでいろんなSaaS(SlackやGithubなど)からデータを抽出できるプロジェクトをご存知でしょうか?
SQL(Structured Query Language)はデータベースに対して「このデータを取得して」「このデータを更新して」と指示を出すための言語ではあるものの、Slack APIを叩くためのものではありません。しかし、MindsDBは、Slackだけでなく、GitHub、OpenAI、PostgreSQLなど200以上のサービスを、すべて同じSQL構文で扱えるというのです。

いったいどうやってこんな芸当を実現しているのでしょうか。私はソースコードを追いかけながら、その仕組みを解き明かしてみることにしました。
この記事を読むと何ができるようになるか

本記事を読むと、読者の皆さんは以下の3つのことができます。
1つ目は、MindsDBの内部構造を理解した上で、自分のプロジェクトに導入するかどうかを判断できることです。「SQLで外部APIを操作できる」という表面的な理解だけでなく、内部でどのような処理が行われているかを知ることで、パフォーマンス特性や制約を予測できます。
2つ目は、MindsDBを使う際にトラブルシューティングがしやすいことです。たとえば「なぜこのクエリが遅いのか」「なぜこのJOINがうまくいかないのか」といった問題に直面したとき、内部アーキテクチャを知っていれば原因を特定しやすくなります。
3つ目は、同様のアーキテクチャを自分で設計する際の参考になることです。複数のデータソースを統一的に扱うシステムを作りたい場合、MindsDBの設計パターンは非常に参考になります。
MindsDBとは何か?
MindsDBは、AIモデルや外部サービスをデータベースのテーブルのように扱えるオープンソースプロジェクトです。たとえば、次のようなSQLでSlackのメッセージを取得できます。
SELECT * FROM mindsdb_slack.messages
WHERE channel_id = 'C123'
LIMIT 10;
このSQLを見ると、まるでSlackのメッセージがどこかのデータベースに保存されているかのように見えます。しかし実際には、このSQLが実行されるたびにMindsDBがSlack APIを呼び出して、リアルタイムでデータを取得しています。いわば「見せかけのテーブル」、つまり仮想テーブルという仕組みです。仮想テーブルとは、実際にはデータを保持していないものの、あたかも通常のテーブルのようにSQLでアクセスできる仕組みを指します。
さらに面白いのは、PostgreSQLのデータとSlackのメッセージをJOIN(結合)したり、AIモデルで感情分析したりすることも、すべてSQLの構文で記述できる点です。データベースとAPIとAIが、同じ言語で操作できる。これはなかなか面白いコンセプトではないでしょうか。
実際の業務でどう使えるか:3つのシナリオ
では、MindsDBの仕組みを知ることで、具体的にどんな業務に活用できるのでしょうか。職種別に3つのシナリオを紹介します。
営業チームがSlackで交わしている顧客対応の会話を、CRMデータベースの顧客情報と組み合わせて分析したいとします。従来であれば、エンジニアに依頼してSlack APIとデータベースそれぞれに接続するコードを書いてもらい、データを取得し、プログラム上で結合する必要がありました。MindsDBを使えば、営業担当者自身が以下のようなSQLを書くだけで分析できます。
-- Slackでの顧客対応履歴とCRM情報を結合
SELECT
s.text AS 会話内容,
c.company_name AS 会社名,
c.deal_size AS 案件規模,
c.stage AS 商談ステージ
FROM slack.messages s
JOIN crm_db.customers c ON s.mentioned_company = c.company_name
WHERE s.channel_id = 'C営業チャンネル'
AND s.created_at > '2024-01-01';
カスタマーサポートチームが、顧客からの問い合わせ内容をAIで自動分類し、緊急度の高いものを優先対応したいとします。MindsDBを使えば、問い合わせデータに対してAIモデルをJOINするだけで、リアルタイムに分類結果を取得できます。
-- 問い合わせをAIで緊急度分類
SELECT
t.ticket_id,
t.content AS 問い合わせ内容,
p.urgency AS 緊急度,
p.category AS カテゴリ
FROM zendesk.tickets t
JOIN mindsdb.urgency_classifier p ON t.content = p.input_text
WHERE p.urgency = 'HIGH'
ORDER BY t.created_at DESC;
データエンジニアが、複数のSaaSサービスからデータを収集してデータウェアハウスに集約するETL(Extract, Transform, Load)パイプラインを構築したいとします。従来は各サービスのAPIコネクタを個別に実装する必要がありましたが、MindsDBを使えば、SQLベースで統一的にデータ収集ができます。
-- 複数SaaSからデータを収集してデータウェアハウスに投入
INSERT INTO data_warehouse.daily_metrics
SELECT
'slack' AS source,
COUNT(*) AS message_count,
CURRENT_DATE AS date
FROM slack.messages
WHERE created_at >= CURRENT_DATE
UNION ALL
SELECT
'github' AS source,
COUNT(*) AS commit_count,
CURRENT_DATE AS date
FROM github.commits
WHERE created_at >= CURRENT_DATE;
全体像:料理のレシピのように考える

MindsDBの内部処理は、料理に例えるとわかりやすいかもしれません。
- 注文を受け付ける(ユーザーがSQLクエリを送信)
- レシピを読み解く(パーサーがSQLを抽象構文木に変換)
- 調理手順を決める(クエリプランナーが実行ステップを生成)
- 実際に調理する(エグゼキュータが各ステップを実行し、外部APIやデータベースを呼び出す)
- 盛り付けて提供(結果をまとめてユーザーに返却)
それでは、実際のコードを追いながら、この流れを一緒に見ていきましょう。
エントリーポイント:main.py
MindsDBはpython -m mindsdbで起動します。__main__.pyがエントリーポイント(プログラムの実行開始地点)となり、複数のAPIプロセスを立ち上げます。
# mindsdb/__main__.py より抜粋
class TrunkProcessEnum(Enum):
HTTP = "http"
MYSQL = "mysql"
JOBS = "jobs"
TASKS = "tasks"
ML_TASK_QUEUE = "ml_task_queue"
LITELLM = "litellm"
ここで注目したいのは、HTTP APIとMySQL APIという2つの入口が用意されている点です。HTTP APIはREST(Representational State Transfer)形式、つまりWebブラウザやcurlコマンドからHTTPリクエストを送る形式でクエリを受け付けます。一方、MySQL APIはMySQLプロトコル(MySQLクライアントとサーバー間の通信規約)を喋るため、既存のMySQLクライアントからそのまま接続できます。
実務で役立つポイント:既存ツールがそのまま使える
この設計が実務で意味するのは、普段使っているMySQLのツールがそのまま使えるということです。たとえば以下のようなツールやライブラリから、MindsDBに接続できます。
- MySQL Workbench(GUIでSQLを実行)
- DBeaver(複数データベース対応のGUIツール)
- Pythonの
mysql-connector-pythonライブラリ - PHPの
mysqli拡張 - TableauなどのBIツール
実際の業務では、すでにMySQLを使ったBIツールやダッシュボードが稼働していることが多いでしょう。MindsDBはその接続先を変えるだけで、SlackやOpenAIのデータも同じツールから扱えるようになります。つまり、新しいツールの使い方を覚える必要がないのです。
すぐに試せる手順として、まずMindsDBをDockerで起動します。docker run -p 47334:47334 mindsdb/mindsdbを実行してください。次に、普段使っているMySQLクライアントでlocalhost:47334に接続します。最後にSHOW DATABASES;を実行して、MindsDBが応答することを確認してみてください。
SQLクエリの受信
HTTP APIでSQLクエリを受け取る部分を見てみましょう。
# mindsdb/api/http/namespaces/sql.py より
def post(self):
query = request.json["query"] # SQLクエリ文字列を取得
# FakeMysqlProxyを使ってクエリを処理
mysql_proxy = FakeMysqlProxy()
result: SQLAnswer = mysql_proxy.process_query(query)
query_response: dict = result.dump_http_response()
ここで私が「おっ」と思ったのは、FakeMysqlProxyという名前です。HTTP APIなのに、なぜMySQLプロキシ(仲介役)という名前なのでしょうか。
答えは、コードの再利用にあります。MindsDBの開発者は、HTTPでもMySQLでも内部的には同じコードパスを通るように設計しました。つまり、MindsDBの開発チームは「SQLを処理するロジック」を1か所に集約し、そのロジックをHTTP APIからもMySQL APIからも呼び出せるようにしたのです。これは「一度書いたロジックを何度も使い回す」というプログラミングの基本原則が、アーキテクチャレベルで徹底されている良い例だと感じました。
Executorでの実行
mysql_proxy.process_query()の中身を追っていくと、Executorクラスに行き着きます。
# mindsdb/api/executor/mysql_executor.py より
def query_execute(self, sql, params=None):
self.parse(sql) # SQL文字列をASTにパース
if params:
self.apply_parameters(params)
self.do_execute() # 実際の実行
def parse(self, sql):
self.query = parse_sql(sql) # mindsdb_sql_parserでパース
def do_execute(self):
executor_answer: ExecuteAnswer = self.command_executor.execute_command(self.query)
SQLパーサーはmindsdb_sql_parserという別パッケージを使っています。このパーサーがSQLの文字列を受け取り、AST(Abstract Syntax Tree、抽象構文木)に変換します。
ASTとは何か:なぜ文字列を木構造に変換するのか
ASTというのは、SQLの構造を木構造で表現したものです。たとえば SELECT name FROM users WHERE age > 20 というSQLは、以下のような木構造に変換されます。
SELECT文
├── 取得する列: name
├── テーブル: users
└── 条件: age > 20
この変換が重要な理由は、構造化されたデータのほうがプログラムで処理しやすいからです。文字列のままだと「SELECT」という単語がどこにあるか、「FROM」の後に何があるかを毎回文字列検索で探す必要があります。しかしASTに変換すれば「このノードの子ノードを見ればテーブル名がわかる」というように、決まった場所にアクセスするだけで済みます。
ASTの構造を活用した例は以下のとおりです。
- query.targets → SELECT句の列(name)
- query.from_table → FROM句のテーブル(users)
- query.where → WHERE句の条件(age > 20)
- query.limit → LIMIT句
- query.order_by → ORDER BY句
MindsDBでは、このように文字列検索ではなく、オブジェクトの属性として直接アクセスできます。
コマンド振り分け
ExecuteCommandsクラスがSQLの種類に応じて処理を振り分けます。
# mindsdb/api/executor/command_executor.py より
def execute_command(self, statement: ASTNode, database_name: str = None) -> ExecuteAnswer:
statement_type = type(statement)
if statement_type is CreateDatabase:
return self.answer_create_database(statement)
elif statement_type is Select:
query = SQLQuery(statement, session=self.session, database=database_name)
return self.answer_select(query)
# ... その他のコマンドタイプ ...
これは電話の自動音声案内のようなものです。「SELECT文の方は1番を、CREATE文の方は2番を...」という具合に、SQLの種類によって処理を振り分けています。SELECT文の場合はSQLQueryクラスに処理が委譲されます。
この振り分け処理を「ディスパッチャー」と呼びます。MindsDBはこのディスパッチャーパターンを使って、新しいSQLコマンドへの対応を追加しやすくしています。新しいコマンドタイプをサポートしたい場合は、このif文に条件を追加し、対応する処理メソッドを実装するだけです。
クエリプランナー:ルールベースでの実行計画
SQLQueryクラスの中では、QueryPlanner(クエリプランナー)が実行ステップを生成します。
# mindsdb/api/executor/sql_query/sql_query.py より
class SQLQuery:
def execute_query(self):
steps = list(self.planner.execute_steps()) # プランナーがステップを生成
for step in steps: # 各ステップを順次実行
step_result = self.execute_step(step)
self.steps_data[step.step_num] = step_result
self.fetched_data = step_result # 最終結果を保存
クエリプランナーは「どのデータソースから何を取得するか」「どう結合するか」「どのAIモデルで予測するか」といった実行計画を立て、その計画を順番に実行していきます。LLMは使わずにルールベースで計画を立てています。
具体例:複数データソースを横断するクエリの実行計画
たとえば「SlackのメッセージをPostgreSQLのユーザー情報とJOINして、AIモデルで感情分析する」というクエリがあったとしましょう。クエリプランナーは以下のような手順を自動的に組み立てます。
| ステップ | 処理内容 | 呼び出されるハンドラー |
|---|---|---|
| 1 | Slackからメッセージデータを取得 | SlackHandler |
| 2 | PostgreSQLからユーザー情報を取得 | PostgresHandler |
| 3 | 両者のデータをメモリ上で結合 | 内部JOINエンジン |
| 4 | 結合結果をAIモデルに渡して感情分析 | OpenAIHandler |
| 5 | 最終結果をユーザーに返却 | - |
この「実行計画を立てる」という概念は、PostgreSQLやMySQLなどの一般的なデータベースにも存在します。EXPLAINコマンドでクエリの実行計画を確認したことがある方なら、イメージしやすいのではないでしょうか。
トラブルシューティングに役立つ知識
クエリプランナーの存在を知っていると、パフォーマンス問題のトラブルシューティングがしやすくなります。たとえば「このクエリが遅い」という問題に直面したとき、以下のような切り分けができます。
- ステップ1(Slack API呼び出し)が遅い → Slack側のレート制限に引っかかっている可能性
- ステップ3(JOIN処理)が遅い → データ量が多すぎてメモリ上での結合に時間がかかっている可能性
- ステップ4(AIモデル呼び出し)が遅い → OpenAI APIの応答が遅い、またはデータ量が多すぎる可能性
外部サービス統合の仕組み:216個のレゴブロック
ここからが私が特に興味を惹かれた部分です。MindsDBは216個ものハンドラー(サービス統合コンポーネント)を持っています。Slack、GitHub、Notion、PostgreSQL、MySQL、OpenAI、Anthropic...。これだけの数をどうやって管理しているのでしょうか。
答えは「共通フレームワーク」にあります。レゴブロックのように、すべてのハンドラーが同じ規格に従っているのです。
# mindsdb/integrations/libs/api_handler.py より
class APIHandler(BaseHandler):
"""
Base class for handlers associated to the applications APIs
(e.g. twitter, slack, discord etc.)
"""
def __init__(self, name: str):
super().__init__(name)
self._tables = {}
def _register_table(self, table_name: str, table_class: Any):
"""APIリソースをテーブルとして登録"""
self._tables[table_name.lower()] = table_class
def query(self, query: ASTNode):
if isinstance(query, Select):
result = self._get_table(query.from_table).select(query)
elif isinstance(query, Insert):
result = self._get_table(query.table).insert(query)
# ...
各サービスのハンドラーは、このAPIHandlerを継承して、自分専用のテーブルを登録するだけで済みます。新しいサービスを追加したい開発者は、ゼロから作る必要はなく、この「型」に従ってテーブルを定義すればいいわけです。
設計パターンとしての学び
これは「テンプレートメソッドパターン」と呼ばれるデザインパターンの一種です。基底クラス(親クラス)で処理の大枠を定義し、具体的な実装は派生クラス(子クラス)に任せます。この設計には以下のメリットがあります。
| メリット | 具体的な効果 |
|---|---|
| コントリビューションのハードルが下がる | 既存のハンドラーを参考にしながら、決められたインターフェースに従って実装するだけで新しいサービス統合を追加できる |
| コードの一貫性が保たれる | すべてのハンドラーが同じパターンに従うため、1つ理解すれば他も理解しやすい |
| テストが書きやすい | 共通のインターフェースに対してテストを書けるため、新しいハンドラーでも既存のテストを流用できる |
自分がライブラリやフレームワークを設計する際にも、この「共通インターフェースを定義して、具体的な実装は派生クラスに任せる」というパターンは非常に参考になります。
OpenAPI仕様からの自動生成
さらに興味深いのは、OpenAPI仕様からハンドラーを自動生成できる機能です。
# mindsdb/integrations/libs/api_handler_generator.py より
class APIResourceGenerator:
"""
A class to generate API resources based on the OpenAPI specification.
"""
def __init__(self, url, connection_data, url_base=None, options=None) -> None:
self.openapi_spec_parser = OpenAPISpecParser(url)
self.connection_data = connection_data
self.resources = {}
def generate_api_resources(self, handler, table_name_format='{url}'):
paths = self.openapi_spec_parser.get_paths()
schemas = self.openapi_spec_parser.get_schemas()
endpoints = self.process_endpoints(paths)
for endpoint in endpoints:
table_name = table_name_format.format(url=url, method=endpoint.method)
self.resources[table_name] = RestApiTable(handler, endpoint=endpoint)
return self.resources
OpenAPI仕様とは、RESTful APIを記述するための標準フォーマットです。YAMLまたはJSONで記述され、APIのエンドポイント、パラメータ、レスポンスの形式などを定義します。多くのAPIプロバイダーがこの仕様を公開しています。たとえばGitHub(https://api.github.com/openapi.json)やStripe、Twitterなどが該当します。
MindsDBでは、このOpenAPI仕様のURLを指定するだけで、各エンドポイントが自動的にテーブルとして生成されます。たとえば、/users/{username} というエンドポイントは users_x というテーブルになり、パラメータ username はWHERE句で使用できるようになります。
# OpenAPI仕様を解析してエンドポイント情報を抽出
def get_paths(self) -> dict:
return self.openapi_spec.get('paths', {})
def get_schemas(self) -> dict:
return self.openapi_spec.get('components', {}).get('schemas', {})
def get_security_schemes(self) -> dict:
return self.openapi_spec.get('components', {}).get('securitySchemes', {})
これにより、OpenAPI仕様を提供しているAPIであれば、手動でハンドラーを実装しなくても、MindsDBに統合できます。216個以上のサービス統合を実現できている背景には、手動実装だけでなく、このような自動生成の仕組みも貢献しているのです。
Slackハンドラーの例
具体的に、Slackの統合がどのように実装されているか見てみましょう。
# mindsdb/integrations/handlers/slack_handler/slack_handler.py より
class SlackHandler(APIChatHandler):
def __init__(self, name: Text, connection_data: Dict, **kwargs: Any) -> None:
super().__init__(name)
self.connection_data = connection_data
# テーブルを登録
self._register_table('conversations', SlackConversationsTable(self))
self._register_table('messages', SlackMessagesTable(self))
self._register_table('threads', SlackThreadsTable(self))
self._register_table('users', SlackUsersTable(self))
def connect(self) -> WebClient:
"""Slack APIへの接続"""
self.web_connection = WebClient(token=self.connection_data['token'])
return self.web_connection
_register_table()メソッドで4つのテーブル(conversations、messages、threads、users)を登録しています。たったこれだけで、SELECT * FROM slack.messagesのようなSQLが書けるようになります。
ここで気づくのは、Slackの概念(チャンネル、メッセージ、スレッド、ユーザー)が、そのままテーブルとして表現されている点です。SQLを知っている人なら、Slack APIのドキュメントを読まなくても、直感的にデータにアクセスできるわけです。
すぐに試せるSlack統合のセットアップ
MindsDBでSlackを使い始める手順は以下の通りです。
まず、Slack APIにアクセスして「Create New App」をクリックします。必要な権限(channels:history, channels:read など)を追加し、「OAuth & Permissions」からBot User OAuth Tokenをコピーしておきます。
次に、MindsDBでデータベースを作成します。
CREATE DATABASE mindsdb_slack
WITH ENGINE = 'slack',
PARAMETERS = {
'token': 'xoxb-your-token-here'
};
これでセットアップは完了です。あとはデータを取得するだけです。
-- チャンネル一覧を確認
SELECT * FROM mindsdb_slack.conversations;
-- 特定チャンネルのメッセージを取得
SELECT * FROM mindsdb_slack.messages
WHERE channel_id = 'C0123456789'
LIMIT 10;
仮想テーブルの正体
SlackMessagesTableの実装を見ると、SELECT文がどのようにSlack APIの呼び出しに変換されるかがわかります。
# mindsdb/integrations/handlers/slack_handler/slack_tables.py より(簡略化)
def list(self, conditions: List[FilterCondition] = None, limit: int = None, ...):
client = self.handler.connect()
params = {}
# WHERE句の条件をAPIパラメータに変換
for condition in conditions:
if condition.column == "channel_id":
params["channel"] = condition.value
elif condition.column == "created_at":
# 日時条件をタイムスタンプに変換
date = dt.datetime.fromisoformat(condition.value)
params["oldest"] = date.timestamp()
# Slack APIを呼び出し
response = client.conversations_history(**params)
messages = response["messages"]
# DataFrameに変換して返却
return pd.DataFrame(messages)
これが「仮想テーブル」の正体です。データはMindsDBのどこにも保存されていません。SQLクエリが実行されるたびにMindsDBがSlack APIを呼び出して、最新データを取得しているのです。
最後の行で登場するpd.DataFrameは、Pythonのpandasライブラリが提供するデータ構造で、表形式のデータを扱うためのものです。ExcelのシートやSQLのテーブルのように、行と列からなるデータを効率的に処理できます。Slack APIから返ってきたJSONデータをDataFrameに変換することで、MindsDBの内部で統一的にデータを扱えるようになっています。
SQLからAPI呼び出しへの変換フロー
つまり、処理の流れは以下のようになります。
- ユーザーが
SELECT * FROM slack.messages WHERE channel_id = 'C123'を実行する - MindsDBのパーサーがSQLを解析し、SlackMessagesTableの
list()メソッドを呼び出す -
list()メソッド内で、WHERE句の条件(channel_id = 'C123')がSlack APIのパラメータ(channel: 'C123')に変換される - Slack APIの
conversations_history(channel='C123')が呼び出される - 結果がDataFrameに変換されてMindsDBに返される
- MindsDBがDataFrameをSQLの結果形式に整形してユーザーに返す
SQLという「見慣れた顔」の裏で、実際にはAPI呼び出しが行われている。このギャップこそが、MindsDBが「SQLで何でも操作できる」ように見せている仕組みの核心です。
なぜSQLなのか?
ここまで読んで、「外部APIを呼ぶだけなら、SQLを使う必要はないのでは?」という疑問が浮かんだ方もいるかもしれません。正直なところ、私も最初はそう思いました。
でも、コードを追いかけているうちに、SQLを採用する理由が見えてきました。3つの観点から説明します。
理由1:統一されたインターフェース
Slack、PostgreSQL、OpenAI、すべて同じSQL構文で操作できます。
-- Slack
SELECT * FROM slack.messages WHERE channel_id = 'C123';
-- PostgreSQL
SELECT * FROM postgres_db.users WHERE age > 25;
-- 同じ構文で両方にアクセス
新しいサービスを使うたびに、そのAPIの仕様を覚える必要がありません。学習コストをかなり抑えられます。
たとえば、Slack APIではconversations.historyエンドポイントにパラメータchannelを渡す必要がありますが、GitHub APIでは/repos/{owner}/{repo}/issuesエンドポイントにパスパラメータを渡します。さらにOpenAI APIでは/v1/chat/completionsエンドポイントにJSONボディを送る必要があります。MindsDBを使えば、このような違いを意識せずに済みます。
理由2:複数データソースの結合
これがSQLの真骨頂ではないでしょうか。
-- SlackのメッセージとPostgreSQLのユーザー情報を結合
SELECT s.text, u.name, u.email
FROM slack.messages s
JOIN postgres_db.users u ON s.user = u.slack_user_id
WHERE s.channel_id = 'C123';
Slackのメッセージを送った人の詳細情報を、社内データベースから引っ張ってきて一緒に表示する。こういうことが、たった数行のSQLで書けてしまうわけです。
従来であれば、以下のようなコードを書く必要がありました。
# 従来のアプローチ(イメージ)
slack_messages = slack_client.conversations_history(channel='C123')
user_ids = [m['user'] for m in slack_messages]
users = db.execute("SELECT * FROM users WHERE slack_user_id IN (%s)", user_ids)
# 手動でJOINロジックを実装
result = []
user_dict = {u['slack_user_id']: u for u in users}
for msg in slack_messages:
user = user_dict.get(msg['user'], {})
result.append({
'text': msg['text'],
'name': user.get('name'),
'email': user.get('email')
})
MindsDBを使えば、このような「データを取得して、別のデータを取得して、マッチングする」という面倒なコードを書く必要がなくなります。
理由3:AIモデルとの統合
MindsDBでは、AIモデルもテーブルとして扱えます。ただし、使用する前にまず CREATE MODEL 文でモデルを作成する必要があります。
-- まずモデルを作成(OpenAIを使った感情分析モデルの例)
CREATE MODEL mindsdb.sentiment_model
PREDICT sentiment
USING
engine = 'openai',
prompt_template = 'Analyze the sentiment of this text and respond with only one word - positive, negative, or neutral: {{text}}';
モデルを作成したら、JOINでデータを渡すだけで予測結果が得られます。
-- Slackメッセージの感情分析
SELECT m.text, p.sentiment
FROM slack.messages m
JOIN mindsdb.sentiment_model p ON m.text = p.text
WHERE m.channel_id = 'C123';
たとえば、カスタマーサポートチームが使っているダッシュボードがあるとします。そこに「顧客からのメッセージの感情スコア」という列を追加したい場合を考えてみましょう。
従来のアプローチでは、まずPythonで感情分析のコードを書き、次に定期的にバッチ処理を回す仕組みを構築し、分析結果をデータベースに書き込み、最後にダッシュボードからそのテーブルを参照するという手順が必要でした。
MindsDBを使えば、モデルを一度作成しておけば、ダッシュボードのSQLクエリを以下のように書き換えるだけで、リアルタイムに感情分析結果を表示できます。
SELECT
t.ticket_id,
t.customer_message,
t.created_at,
s.sentiment
FROM support_tickets t
JOIN mindsdb.sentiment_model s ON t.customer_message = s.text
ORDER BY t.created_at DESC;
自然言語エージェント機能
とはいえ、SQLを書くのが面倒という声もあるでしょう。MindsDBは自然言語エージェント機能も提供しています。
# エージェントに自然言語で質問
agent.invoke("Slackのメッセージを10件取得して")
# → エージェントが自動的にSQLを生成して実行
この機能では、内部でLLM(大規模言語モデル)が自然言語をSQLに変換しています。ユーザーは「Slackの最新メッセージを見せて」と言うだけで、MindsDBがそれを SELECT * FROM slack.messages ORDER BY created_at DESC LIMIT 10 のようなSQLに変換して実行してくれます。
SQLと自然言語の使い分け
ただし、SQLには明確性という強みがあります。「最近のメッセージを10件取得して」という自然言語だと、「最近」がどのくらいの期間を指すのか曖昧です。1時間前なのか、1日前なのか、1週間前なのか。SQLなら created_at > '2024-01-01' と明示できます。
| 用途 | 推奨する方法 | 理由 |
|---|---|---|
| 探索的なデータ確認 | 自然言語 | 素早く結果を確認したいときに便利 |
| 本番運用のクエリ | SQL | 明確で再現性があり、バージョン管理もしやすい |
| 複雑な結合クエリ | SQL | JOINの条件を正確に指定できる |
| 非エンジニアの利用 | 自然言語 | SQLを知らなくてもデータにアクセスできる |
私の感覚としては、探索的にデータを眺めたいときは自然言語が便利で、本番運用や複雑な結合クエリにはSQLが向いているように思います。両方使えるというのは、状況に応じて使い分けられるという意味で、良い選択肢ではないでしょうか。
SQLという馴染みのある言語で、データベースもAPIもAIモデルも統一的に扱えるというのは、考えていた以上に実用的なコンセプトだと感じました。複数のデータソースを横断して分析したい場合や、既存のBIツールからAIモデルを呼び出したい場合に、このアプローチは役立ちそうです。
参考リンク
About me
現在、市場調査やデスクリサーチの生成AIエージェントを作っています 仲間探し中 / Founder of AI Desk Research Agent @deskrex , https://deskrex.ai
ぜひお気軽にチャットしましょう!
お仕事のご相談は以下まで、AIエージェントの開発や研修、調査代行やビジネスコンサルなどの対応も可能です。
生成AIデスクリサーチサービス Deskrex | サービスページ
生成AIデスクリサーチエージェント Deskrex App | アプリケーションサイト
DeskrexAIリサーチ | メディア
株式会社Deskrex | 会社概要
Deskrex | Xページ
- 会社概要:https://www.deskrex.ai/
- Deskrex App:https://app.deskrex.ai/
- サービスページ:https://lp.deskrex.ai/
- メディア:https://media.deskrex.ai/
- X:https://x.com/deskrex
Discussion