ActQL
AIエージェントのための、SQLベースのデータ&ワークフローエンジン。
SaaS、データベース、ファイルをまたいでデータを読み、結合・加工する。非構造データはLLMで解析し、結果は承認を挟んで安全に書き戻す。これまでワークフローSaaSとグルーコードを組み合わせて実現していた処理を、SQLで1つのプログラムとして書いて、そのまま安定して動かせます。
ActQLを使うと、CRM、ファイル、SaaSのAPI、データウェアハウスといった、つないだシステム全体を1つのデータベースのように扱えます。
npm install -g actqlnpm install @actql/nodeuv add actqlnpx skills add actql/skillsエージェントへのプロンプト:ActQL をセットアップして
1.ActQLで何ができるか
商談の議事録はGoogle Driveに、商談そのものはSalesforceにある。商談ごとに最新の議事録を読み、フェーズや次のステップが変わったかをLLMに判断させて、更新をSalesforceに書き戻す。これを一本のSQLパイプラインで書けます。基本はSQL。LLMを呼ぶ INFER と、外部システムに書き込む EFFECT が加わります。この例は両方を使っていますが、表を返すだけの問い合わせや、LLMを使わないワークフローも、その手順を省いた同じプログラムです。
SELECT o.id AS id, o.StageName, o.NextStep, c.content AS notes FROM salesforce.opportunities AS o JOIN drive.files AS f ON f.name = o.name JOIN drive.contents AS c USING (resource, version) |> INFER `model-task:deal-review.v1` INPUT (StageName, NextStep, notes) AS review |> WHERE review.outcome = 'ok' AND review.value.changed |> RESULT updates |> EFFECT UPDATE salesforce.Opportunity ID id SET ( StageName = review.value.stage, NextStep = review.value.next_step );
salesforce.opportunities ─┐
├─► JOIN ─► INFER ─► WHERE ─► EFFECT ─► Salesforce
drive.contents (notes) ─┘ │ │
│ └─ Checkpointで一時停止
│ 承認されるまで待つ
└─ 型付きの結果- 複雑な処理も、SQL1つ。SaaSのAPI、DWH、データベースをまたぐ処理が、途中でLLMを呼んでも呼ばなくても、一つのプログラムになります。SaaSのデータも、DWHのデータも、LLMの出力も型の決まった同じ表として扱えるので、普通のSQLで結合・絞り込みでき、食い違いは実行前に見つかります。
- 運用の基盤は、実行エンジンにお任せ。認証・リトライ・状態管理、途中で落ちたときの再開まで、エンジンが引き受けます。Run(1回の実行)は完了した処理を繰り返さず、結果が不明な書き込みはむやみに再送しません。
- AIが書き、エンジンが検証し、人が承認する。書き込みは、実際に変わるレコードと値を見て承認します。承認の流れは自分で組めます。件数や対象で分岐して、影響の小さいものはそのまま実行し、一部はルールで承認し、リスクの高いものだけ人に回す、といった振り分けを書けます(後述)。
- オープンソースで、ローカルですぐ始められる。実行エンジンはApache 2.0のオープンソースです。認証も、リトライも、再開も、書き込み前の検証も、中身を読んで確かめられます。SQLiteやDuckDBと同じように、サーバーを立てずアカウントも作らず、CLIからでもライブラリとしてでも、そのまま使い始められます。
動かし方は3つ。CLIから実行する(自分でも、コーディングエージェントに任せても)、TypeScript・Python SDKであなたのアプリに組み込む、ActQL Cloudでそのまま実行する。
いま使っている道具から、ActQLへ:
手元で管理しているモデルを、SaaSのデータと合わせて使う。いまのSQLツールからActQLにつなぐ。
自分のアプリからシステムをまたいで問い合わせ、ローカルのConnectorでテストして、機能として出す。
同じプログラムを型付きのツールとしてエージェントに渡し、検査・権限・Runの記録を共通にする。
2.はじめる
1CLIで商談レビューの例を動かす
CLIをインストールしてサンプルのプロジェクトを作り、Salesforce、Google Drive、LLMをモックに置き換えて実行します。認証情報は不要です。
$ npm install -g actql $ actql init --template deal-review-mock deal-review && cd deal-review $ actql run deal-review.aql --stop-before commit
RunはSalesforceに書き込む前で止まり、これから何を変更するのかをそのまま表示します。
Run run_... Status waiting_checkpoint Checkpoint ckpt_... Effect update (Salesforce Opportunity) Items 2 Changes Acme Corp - Renewal StageName Proposal → Negotiation NextStep (empty) → Send revised pricing by Friday Globex - Expansion StageName Negotiation → Closed Lost NextStep Legal review → Revisit next fiscal year
承認してから、Runの状態を確認します。
$ actql approve '<checkpoint-id>' $ actql status '<run-id>'
これはコーディングエージェントが使うのと同じ流れです。実行の前には check で検査し、explain で計画を確かめられます。--json を付ければ、エージェントやCIが読み取れる形式で出力されます。エージェントに書き方を覚えさせるにはプログラムを書くを、コマンドの一覧はCLIリファレンスを参照してください。
2自分のアプリに組み込む
同じ商談レビューの例をライブラリとしてアプリの中から動かせます。ワークフローサーバーは要りません。
import {readFile} from 'node:fs/promises'; import {connect} from '@actql/node'; const client = await connect({mode: 'local', installation: {directory: '.actql'}, profile: 'mock'}); try { const source = await readFile('deal-review.aql', 'utf8'); const execution = await client.startSource(source, {format: 'aql', stopBefore: 'commit'}); const state = await execution.wait(); console.log(state.status); // 'waiting_checkpoint' console.log(state.checkpoint?.id); // approve it: actql approve <checkpoint-id> } finally { await client.close(); }
import actql with actql.connect(mode="local", installation=".actql", profile="mock") as client: source = open("deal-review.aql").read() execution = client.start_source(source, format="aql", stop_before="commit") state = execution.wait() print(state.status) # "waiting_checkpoint" print(state.checkpoint.id) # approve it: actql approve <checkpoint-id>
どちらもCLIの例と同じくCheckpointで止まります。コードから始めたRunも、actql status <run-id> で状態を確認し、actql approve <checkpoint-id> で承認できます。.actql には実行の状態が保存されます。バージョン管理には含めないでください。
3.なぜ、新しいエンジンを作るのか
ActQLは、iPaaS、ETL、ワークフローエンジンの今の使われ方に対する4つの問いから始まりました。
Q1なぜ、データ量や用途によって別々の製品が必要なのか
SaaS間で数件のレコードを同期するならiPaaS。数TBを動かすならETL。DWHのデータをSaaSに戻すならReverse ETL。途中で失敗するかもしれない処理を確実に続けるならワークフローエンジン。でも、根っこではどれも同じことをしています。読む、計算する、書く。それを安定して運用する。1レコードと数TBでは、最適な実行方法は違います。それでも、本当に別々の製品が必要なのでしょうか。
Q2AIがワークフローを書くのに、なぜ人間がまだ手でノードをつないでいるのか
ワークフローのUIは、人間がGUIでノードをつなぐために作られてきました。でも、エージェントがプログラムを書くなら、仕事の現場はもうキャンバスの上にはありません。しかもノードは重ねるほど複雑になります。数個のうちはわかりやすくても、何十ものノードの設定に、APIごとの認証やページングの違いが混ざり込み、全体で何が行われるのかを見通せなくなります。
Q3なぜ、ワークフローをアプリに組み込むのは大仕事なのか
アプリでSQLを使いたいなら、SQLiteやDuckDBを組み込めばいい。ところが、アプリから数個のSaaSを操作したいだけなのに、別のワークフローサーバーやSaaSを運用するか、認証・状態管理・再試行・復旧を自前で作るかの二択になります。
Q4AIが処理を書くなら、誰がそれを確かめるのか
AIを使えば、ワークフローもスクリプトも一瞬で書けます。でも、書かれたものが何を書き換えるのか、失敗したら何が確定して何が不明なのかを、人がすべて読んで確かめることはできません。
エージェントは速く書き、人はゆっくり読む。この差は速く読むことでは埋まりません。処理を、機械が検査・実行・許可の判断までできる形で書くことで埋まります。そうすれば、人が呼ばれるのは、本当に人の判断が必要なときだけになります。
4.ActQLの答え:5つの設計
先の4つの問いに、ActQLは次の設計で順に答えます。5つめは、その先にある拡張です。
1Any scale:データ量が変わっても、同じプログラムのまま
SQLでは「どんなデータが欲しいか」を書くだけで、どう取ってくるかはデータベースが自動で最適化して実行します。ActQLはこの仕組みを、システムをまたぐ計算とアクションに広げます。プログラム(Program)には何をしたいかだけを書き、どこで計算するかの計画(Plan)はエンジンが決めます。
計画の仕組みは一つです。Snowflakeのような DWH も、SalesforceのようなSaaSのAPIも、DuckDBも、自分にできる演算をCapabilityとして宣言しています。エンジンはそれを見て、処理の各部分を実行できる場所に任せます(Pushdown)。Salesforceのように問い合わせAPIが柔軟なら、そのCapabilityを最大限使い、全体で最適な実行計画を組み立てます。SalesforceとSnowflakeをまたぐJOINのように、どのソースにも任せられない処理は、手元のマシンで内蔵のエンジンを使って実行します。入力が大きければ、DuckDBを同梱したパッケージでDuckDBに任せます。JOINのために新しいDWHを用意する必要はなく、大きな計算はPushdownでデータのある場所に任せます。
Salesforceの取引先とSnowflakeの利用データをJOINし、LLMで解約リスクを判定して書き戻すプログラム(クイックスタートの例)なら、actql explain でこんな計画が見えます。JOINをどのエンジンで実行するかは、件数が分かってからRunの中で決まります。
$ actql explain churn-risk.aql
[1] READ salesforce.accounts
Site: connector:salesforce
Pushdown: selected
[2] READ snowflake.usage_30d
Site: connector:snowflake
Pushdown: selected
[3] JOIN [1] + [2]
Site: host:local (fixed)
Engine: deferred
Candidates: ActQL RelA, DuckDB
[4] INFER churn-risk.v1
Site: model provider
[5] WHERE on risk
Site: host:local
Engine: deferred
Pushdown: not eligible
Reason: depends on INFER output
[6] EFFECT UPDATE salesforce.Account
Site: connector:salesforce
Checkpoint: conditional処理をどこに移しても、プログラムの意味は変わらず、権限の範囲を越えて実行されることもありません。
ActQL Cloudを使えば、同じプログラムを、自分でHostを運用せずにマネージド環境で動かせます。手元のマシンに収まらない規模の処理も任せられます。
データが増えれば、実行方法は変わる。書くプログラムは変わらない。
2Cross-system:ノードの代わりに、読めるプログラムで
データの取得、変換、必要ならLLMによる推論、外部への書き込みを、一つのプログラムに上から順に書きます。全体で何が起きるかは、プログラムを読めばわかります。
テキストのプログラムは、エージェントが一番得意な形式でもあります。差分でレビューでき、Gitで履歴を残せ、プルリクエストで変更を確かめられます。キャンバス上のノードでは難しかったことです。
APIごとの認証、ページング、レート制限、書き込みの作法は、Connectorの内側で引き受けます。プログラムに出てくるのは salesforce.opportunities や drive.contents といった名前だけで、APIごとの違いをプログラムの側で意識する必要はありません。使えるConnectorは対応Connectorを参照してください。
3Embeddable:サーバーなしで、アプリに組み込める
TypeScriptやPythonから、ライブラリとして読み込むだけで使えます。同じAPIでリモートのエンジンにも接続できます。コアはJavaScript、WebAssembly、ネイティブで動くので、サーバーからEdgeまで同じエンジンを持ち込めます。
認証、状態管理、再試行、復旧を自前で作る必要はありません。Runはアプリのディレクトリに永続化され、プロセスが落ちても続きから再開できます。認証情報とポリシーは、エンジンを動かす側(Host:あなたのアプリやCLI、サーバー)が持ち、プログラムには書きません。
- アプリに組み込む:ユーザーが操作したときだけ、システムをまたぐ処理を自分のアプリの中で実行する。専用のワークフロー基盤は要りません。
- ローカル開発:テスト用Connectorと手元の状態で失敗を再現する。本番のSaaSをデバッガ代わりにする必要はありません。
- CI:プルリクエストごとに型と入出力の取り決めを検査し、テスト用Connectorで期待どおりの結果になるかを確かめる。
- 本番へ:同じプログラムのまま、手元から本番のHostへ移す。変わるのは接続先、認証情報、ポリシーだけです。
4AI-native:AIが書き、エンジンが検査し、Hostが決める
構文はGoogleSQLとそれを自然に拡張したものなので、エージェントはすでに書き慣れています。対応するGoogleSQLの構文は、そのまま使えます。どのエージェントフレームワークで書かれたプログラムも、同じ仕組みで検査・実行されます。
ActQLは見た目だけSQLに似せたDSLではありません。リレーショナル代数を土台に、グラフ・ベクトル・イベント系列の演算にも型を定義し、データの取得や外部への書き込みも明示的な演算として扱います。そして、あえてチューリング完全にはせず、プログラムの停止性と事前の検査しやすさを選んでいます。だから型、必要な権限、起こりうる変更を実行前に検査でき、入力に依存する条件や実際の認可は実行時に確認します。
外部への書き込み(Effect)を実行するかどうかは、すべてHostのポリシーが決めます。そのまま通すか、拒否するか、判断に回すか。判断に回った書き込みは、あなたのコードがプレビューを受け取ります。ルールで自動承認するか、人や承認アプリに振り分けるかは、ユースケースに合わせて決められます。決められるのは「通すか否か」だけで、書き込む内容は変えられません。エージェントが自分に許可を与えることもできません。
承認に回った書き込みは Checkpoint で止まります(はじめるの例)。レビュー担当者が見て承認するのは、これから書き込まれる実際のレコードと値で、エージェントの説明文ではありません。書き込む内容は記録済みの結果から決まるので、承認した書き込み内容がcommitまでの間に変わることはありません。結果が不明な書き込みも、やみくもに再送せず、Connectorの仕組みで確かめてから進めます。
最初は人がすべて確認し、安定したものから無人の実行へ移していく、という段階的な任せ方も、この同じ仕組みのままできます。
5Beyond tables:ベクトル検索やグラフも、同じプログラムで
ベクトル検索、グラフ探索、イベント系列のマッチングも、型の決まったレコードとして結果を返すので、通常のJOINやフィルタ、INFER、EFFECT とそのまま組み合わせられます。たとえば、緊急の問い合わせ(Case)ごとに似たナレッジベース記事を3件探し、それを材料に INFER で返信案を作るならこうです。
SELECT c.id, c.description AS question, c.embedding AS query_vec FROM salesforce.cases AS c WHERE c.priority = 'urgent' |> NEAREST kb.articles USING (query_vec) K 3 AS hit |> AGGREGATE ARRAY_AGG(hit.body ORDER BY hit.rank) AS refs GROUP BY id, question |> INFER `model-task:draft-reply.v1` INPUT (question, refs) AS draft
いわゆるRAGを、検索基盤やグルーコードを別に用意せずに書けます。
テーブルの扱いがGoogleSQLに沿っているのと同じく、グラフ探索にはISO標準のGQLやSQL/PGQに由来するパターン構文を採用しています。顧客・取引先・製品のつながりのような関係グラフや、グラフとして表した業務のオントロジーも、SQLと組み合わせて問い合わせられます。(内部では、リレーション、グラフ、ベクトル、イベント系列を、RelA、SGA、VecA、TEAという4つの代数で一つの体系として扱っています。)
5.対応Connector
以下のConnectorは、いずれも読み取りと書き込みの両方に対応しています。
| DB・DWH | Snowflake · BigQuery · DuckDB |
|---|---|
| SaaS | Salesforce · HubSpot · Slack · Notion · kintone · GitHub · Tableau |
| Google Sheets · Google Drive · Gmail · Google Calendar | |
| ストレージ・ファイル | Amazon S3 · Google Cloud Storage · ローカルファイル |
他のシステムにつなぐConnectorは、Connectorを作るを参照して自作できます。そのガイドを参照するSkillsを使えば、コーディングエージェントにも任せられます。
6.既存ツールとの関係
既存のツールとの関係は、使い分けるもの、組み合わせるもの、計算を任せる先の3つに分かれます。
使い分ける
| ツール | そのツールが向いている場面 | ActQLが向いている場面 |
|---|---|---|
| 自前のスクリプトPython、TypeScript | 一度きりの小さな連携を、すぐに書きたい | 再試行・復旧・承認・権限の管理まで含めて、続けて運用したい |
| iPaaSn8n、Zapier | 少数のSaaSを、GUIでさっとつなぎたい | 連携が増えても全体を見通したい。エージェントに書かせたい。自分のアプリに組み込みたい。書き込みを承認つきで回したい |
| Reverse ETLCensus、Hightouch | DWHで整えたデータを、決まった形でSaaSへ定期的に同期したい | SaaSのデータとJOINしたり、LLMで判断したりしてから、承認つきで書き戻したい |
組み合わせる
| ツール | 使い方 |
|---|---|
| ワークフローエンジンTemporal | Temporalの代わりではない。何日も続く長期のジョブはTemporalが管理し、その中の一手順として、システムをまたぐ読み書きをActQLに任せる |
| dbt | モデル、依存関係、テストはdbtのまま。整えたデータをActQLがSaaSのデータと合わせて、システムをまたぐ問い合わせや書き込みに使う。dbt-actqlを使えば、対応するモデルの実行先をActQLにもできる |
| エージェントフレームワークLangGraph、Mastra | エージェントそのものはフレームワークで作り、エージェントが書いた処理の検査・承認・実行をActQLに任せる。ActQLのプログラムは、エージェントのツールとして呼べる |
計算を任せる先
| ツール | 使い方 |
|---|---|
| DWHSnowflake | 置き換えない。大規模な計算は、PushdownでDWHに任せる |
| 組み込みDBDuckDB、SQLite | アプリにSQLを組み込むのと同じ考え方を、システムをまたぐ処理に広げる。DuckDBは、どのソースにも任せられない処理のPushdown先としても使う |