腾讯 Omega:次世代 AI BI の可能性を検証
本文の状態
日本語全文を表示中
詳細モードで約40分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
Tencent Engineering
腾讯エンジニアリングは、従来の ChatBI の限界を克服し、データソースの理解から分析計画、可視化まで一貫して実行する次世代 AI BI「Omega」の開発プロセスと、安定したデータ連携を実現する技術的アプローチについて発表した。
AI深層分析を開く2026年8月3日 22:06
AI深層分析
キーポイント
ChatBI の限界と Omega の方向性
従来の ChatBI は SQL 生成に留まり分析の継続性が欠けていたが、Omega は自然言語から分析計画を立て、Dashboard を自動生成・編集する完全なワークフローを担う。
静的ページと動的データの分離
AI が生成した HTML/CSS だけでなく、「QueryRegistry」と呼ばれる構造化データ契約を定義し、DTBridge ランタイムがこれに基づいてリアルタイムでデータを取得・更新する仕組みを採用している。
システムによる品質保証とセキュリティ
モデルにコード生成を任せるのではなく、フィールド名不一致やパラメータエラーを検知するバリデーション層を設け、データベース認証情報の漏洩を防ぐための権限管理をシステム側で完結させている。
データセキュリティと生成体験の分離
データベース認証情報はサーバー側に保持され、クライアントは制限された iframe で動作する。ストリーミング生成時には安全なスタイルのみを逐次更新し、スクリプトは完了後に一括読み込みすることで白屏を防ぐ。
指標定義の根拠に基づく推論
モデルは単なる常識ではなく、既存の SQL 実行履歴やユーザー指定データセット、および明確に引用された業務ドキュメントといった「証拠」を優先して指標口径(例:税込み/抜きの区別)を特定する。
重要な引用
AI と元の BI システムは常に二つの別物として存在し、一つの質問には答えられても、その後の分析作業を引き継げない
ページが「生きている」かどうかの真の難しさは、生成されたページと実際のデータの間を絶えず繋ぐ信頼性の高いパイプにある
LLM はデータの活用门槛を下げられるが、企業が未整備であるデータガバナンスを勝手に補完することはできない。
セマンティックモデルは既存のガバナンス価値を増幅するものであり、存在しない口径を創出するものではない。
編集コメントを表示
編集コメント
本記事は、AI が生成した UI を単なる「見た目」で終わらせず、実務で使える「生きたデータツール」として機能させるための重要な設計思想を提示している。特に、モデルの出力と実行環境を分離し、契約(Contract)によって整合性を保証するアプローチは、今後の AI アプリケーション開発における標準的なベストプラクティスとなり得る。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
腾讯 Omega:次世代「AI BI」の答えはここにあるか?
AI によるデータダッシュボードで最も難しいのは、生成そのものではない。
著者:jiaxingjin
Excel ファイルをアップロードし、一言入力して数十秒待てば、KPI やトレンドグラフ、ランキングが表示されたページが画面に現れる。鮮やかな色彩で滑らかなアニメーション、それをグループチャットに共有すれば「すごいね」という声が返ってくるのは容易い。
しかし、実際に使い始めると、問題は翌日から始まるのだ。
時間範囲を「今月」から「先月」に変えても数字は止まったまま。地域フィルターをクリックすると、3 つのグラフがそれぞれバラバラに計算される。データテーブルに新しいフィールドが追加されただけで、ページ全体が真っ白になる。棒グラフを折れ線グラフに変えようとしても、AI は最初から生成し直し、すでに確認済みのレイアウトまで勝手に変更してしまう。
その時になって初めて気づく。昨日見たのはデータ製品ではなく、AI が描いた「データ製品に似せた一枚の画像」だったのだ。
2024 年、DataTalk では ChatBI を試みた。ユーザーがサイドバーで質問し、AI が SQL を生成して結果をグラフ化する仕組みだ。方向性は間違ってはいないが、実際に使ってみると非常に使いにくいものとなった。SQL は結局人間が手直しする必要があり、グラフの品質も不安定だった。何より重要なのは、AI と既存の BI システムが常に別々の存在として扱われていたことだ。一つの質問には答えられるが、その後の分析作業までは引き継げない。
そこで私たちは、このアプローチを根本から見直したのだ。
チャットボックスに別のモデルを搭載するだけではないし、ボタンの配置を並べ替えるわけでもない。ゼロからデータ分析製品を設計し直すなら、AI はどこに位置すべきか——そう改めて問い直した。
チャットボックスが BI の次世代ではない
従来の BI ツールの使い方は明確だ。データの選定、フィールドの選択、コンポーネントのドラッグ&ドロップ、フィルターの設定、スタイルの調整。これらは非常に強力かつ専門的だが、ユーザー自身が「何を分析したいか」をすでに把握しており、その問いを一連の操作に翻訳できることが前提となる。
ChatBI はこの壁を越えたように見える。ユーザーはドラッグ&ドロップをする必要なく、「先月の各カテゴリの販売状況はどうだった?」と自然言語で質問するだけでよい。AI がそれを SQL に変換し、結果としてグラフが返ってくる。
しかし、分析は最初の一枚のグラフでは終わらない。
「なぜ南地区の売上がこれほど急落したのか?」「注文数が減ったのか、それとも客単価が下がったのか?」「大企業と中小企業のトレンドは同じなのか?」
「最も異常な値を示す 3 つの都市だけを抽出して見てほしい」
最後に、結果をまとめてチームに共有し、来週のデータ更新時に再度確認する必要があるかもしれない。
AI が「質問から SQL へ」というステップしか担えないなら、それは単なるクエリの短縮に過ぎず、一連の分析作業全体を代行したわけではない。
Omega は、後者の実現を目指している。
「上季度各品类的销售情况を分析して」と一言頼むと、Omega はまずデータソースやフィールドを理解し、次にこの分析で見るべき指標を自ら計画します。総売上高、前月比、トレンド推移、カテゴリ構成、地域差などです。その後、データを直接照会し、適切なチャートを選択してページを構成し、完成したダッシュボードを生成します。
「2 枚目の図が直感的でない」と言うと、Omega はその 1 枚だけを柱状図に変更します。「地域別のフィルターを追加したい」と言えば、コントロールを追加し、関連するチャートも連動させます。配色が濃すぎる場合は、「薄くして」と指示すれば色を調整できますし、必要であれば可視化エディタを開いて自分でドラッグ&ドロップすることも可能です。
ページを保存して共有すると、データは自動的に更新され続けます。次回もまた、この分析結果をベースにさらに深掘りした分析が可能です。
Omega のコア開発チームは長らく 3〜4 名のみです。製品、デザイン、テストの役割が明確に分かれているわけではなく、多くの決定は数人がコードを書きながら、「製品はこうあるべきだ」と議論しながら進めています。2026 年 1 月中旬に着手し、最初の 2 ヶ月で主要なフローを完成させました。
しかし振り返れば、2 ヶ月で作られたのは「デモとして動く」Omega に過ぎません。その後の数ヶ月間、私たちが取り組んできたのは別の課題でした。つまり、デモ環境から離れても、本物の製品として機能するようにすることです。
看板が「生きている」とは何か
AI がページを生成する自体は難しくありません。現在のモデルは HTML、CSS、JavaScript の記述にすでに熟練しており、ECharts や D3 を組み合わせれば、2 年前よりも遥かに低いハードルで視覚的に優れたダッシュボードを作成できます。
真の難しさは、そのページと実データの間を、常に信頼できるパイプで繋ぎ続けることにあります。
多くの AI 看板の実装では、クエリ結果をそのままページに書き込んでいます。
const data = [
{ month: '1 月', sales: 1280 },
{ month: '2 月', sales: 1530 }
]このコード自体に間違いはありませんし、デモには最適です。しかし、一度生成されるとデータは固定されてしまいます。期間を変更したり、フィルターを追加したり、データを更新したりするには、毎回 AI に再生成を依頼する必要があります。見た目は生きているように見えても、中身にあるデータはすでに死んでしまっているのです。
Omega がページを生成する際、HTML の他にも構造化された「データ契約」を同時に作成します。内部ではこれを QueryRegistry と呼んでいます。各チャートがどのクエリを必要とし、どのようなパラメータを受け取り、結果をどのようにバインドするか、どのフィルターと連動するのか——これらすべてがここで明確に宣言されます。実際の処理は、DTBridge というランタイムが行います。
これは役割を分離したようなものです。「何を求めるか」は AI が担当し、「どうやって安定して取得するか」はシステムが保証します。
ユーザーが日付を変更すると、DTBridge は契約に基づいて関連するチャートを自動的に更新します。フィルター自体も宣言式で記述されるため、AI は各コントロールに対して addEventListener を手書きしたり、WHERE 条件を組み合わせたり、空値の処理を行ったりする必要はありません。
なぜ私たちは、これらのコードをモデルに丸投げしないのか?それは、個別のコード断片は単純に見えても、組み合わさった瞬間に静かにエラーが発生するリスクが高いためです。
典型的な問題として、SQL は正常にデータを返しているのにチャートが空表示になるケースがありました。データベース側でエラーが出ず、ブラウザのコンソールにも異常はありません。最終的な原因は、ページ側のコールバックが「総上报量」を参照していた一方、SQL の出力列名が total_pv だったという不一致でした。
それぞれ単独で見れば問題ない内容ですが、組み合わせると値を取得できない状態になっていたのです。
その後、フィールドバインドのチェック機能を追加しました。これは、ページから実際に読み取ったフィールドを逆手に取り、SQL の出力別名を検証するものです。これに似た検証は他にもあります。時間フィールドが秒単位なのかミリ秒単位なのか、フィルター値がクエリパラメータと対応しているか、チャート定義内のクエリ ID が実際に存在するかといった点です。これらは派手ではありませんが、ダッシュボードが「たまに成功するもの」になるのか、「多くの場合、納品可能なもの」になるのかを決定づける要素です。
データの流れが確立された後は、セキュリティと生成体験もシステムが管理します。ページ側はデータベースのアカウントやキーを取得できません。クエリと権限チェックはサーバー側に残ります。バックエンドでは、データソースの能力に応じて AST の読み取り専用検証や軽量な読み取り専用チェックを実行し、クエリサービスの権限制御と統一されたタイムアウトを適用します。ページは制限された iframe 内で動作します。ダッシュボードがストリーミング生成される際、安全に到達したスタイルと本文のみを更新し、完了後にスクリプトを一括でロードすることで、ページの繰り返し白屏を防ぎます。ユーザーが目にするのは、ページが滑らかに成長していく様子だけです。背後にある境界線は、モデルの臨場感に頼ることはできません。
AI はどのように指標を見つけ、フィルターを連動させ、データ境界を守るのか
BI の経験がある方なら、ここまで読んで自然と疑問を持つでしょう。「モデルはどうやって『売上』がどの列やどのような集計に対応するかを知るのか?」「企業が既に維持している指標の定義やセマンティックモデルはどのように連携するのか?」「なぜ一つのフィルターで五つのチャートを同時に動かせるのか?」「AI が生成した HTML 文書凭什么で生産データのクエリを許可されるのか?」
これらの問いに、「より強力なモデルを使ったから」という一言で答えることはできません。
Omega は BI の基盤を迂回していません。DataTalk に既に存在するデータソース接続、メタデータ、セマンティックモデル、クエリエンジン、権限体系は、引き続き下層を支えています。Agent が問題の理解、分析の構成、インターフェースの生成を担当しますが、データの意味や実行境界に関わる場面では、システムが検証可能な証拠に立ち返る必要があります。
業務上の質問から実行可能なページに至るまでの実際のフローは以下の通りです:
ユーザー指定データ / 既存チャートの SQL / ビジネス知識
↓
Schema とセマンティックモデルの解析
↓
分析計画とクエリツールの選定
↓
HTML + QueryRegistry の生成
↓
契約検証、SQL 事前検証
↓
DTBridge → 権限チェック → クエリプロキシ → チャートコールバック
まず証拠を探し、その上で「指標の理解」を語るのです。
例えばユーザーが「先月の地域別売上を確認したい」と言った場合、実際に難しいのは SUM を書くことではなく、「売上」が何を指すのかを確定させることです。税込か税抜か、返品分は控除するか、支払時刻基準か注文時刻基準か。企業内では同じ名称の指標でも、定義が全く異なる可能性があります。
Omega が指標を探す際の重要な原則は、ユーザーの現在の作業現場に近い証拠ほど優先度が高いという点です。
ユーザーが既存のチャートを編集している場合、「売上」に関する LLM の一般的な常識よりも信頼性が高いのは、そのチャートで実際に実行された SQL です。システムは、現在のチャートの datasetId、クエリ説明、データベースエンジン、そして SQL を Agent に渡します。SQL が SUM(pay_amount - refund_amount) や COUNT(DISTINCT buyer_id) と記述されていれば、それが現在ページで採用されている業務定義です。地域次元の追加やチャートタイプの変更を行う際にも、この定義を再利用すべきであり、改めて作り直す必要はありません。
新規分析の場合、ユーザーがメッセージ内で明示的に選択したデータセットが、履歴ページやシステム検索結果よりも優先されます。生成前に Agent は必ず Schema ツールを呼び出し、実際の物理フィールド、表示名、型、フィールド説明、パーティション情報、データソースエンジン、接続ルートを取得します。この動作はプロンプトに記述されるだけでなく、外側には Schema Guard が存在し、ユーザーがデータを指定しているにもかかわらずモデルが Schema を読まずにページ生成を始めた場合は、プロセスがブロックされます。
ユーザーがデータソースを明確に指定しない場合、システムは個人空間、チーム空間、およびアクセス可能なデータカタログ内を検索します。この検索は、単にすべてのテーブル名をモデルに読み込ませるのではなく、名称、説明、物理テーブル名、キーワードの網羅性、データの所有権、更新日時といった多角的な情報を照合し、重複を除いた上で候補を提示するものです。これは「数千ものデータセットから範囲を絞り込む」ための仕組みであり、欠落したビジネス定義まで理解しているかのような振る舞いはしません。
もう一つの根拠となるのは業務知識です。ユーザーが指標の定義や分析手法、あるいは関連ドキュメントを参照指示した場合、エージェントはそれらの断片を現在のコンテキストに組み込みます。これにより、「有効な取引額は全額返金分を差し引く」「新規顧客は初回支払い日をもって認定する」といったルールは、単なるチャット上の注記ではなく、フィールドのメタデータや既存のクエリと連携して計画立案に直接反映されるようになります。
これら複数の情報を統合すると、指標のエビデンスチェーンが形成されます。すなわち、「既存のクエリ定義 → ユーザー指定のデータソース → セマンティックモデルおよびフィールドメタデータ → 明確に参照された業務知識 → 検索候補」という流れです。十分な証拠があればシステムは自動生成が可能ですが、上流で指標定義が存在せず、かつユーザーからの説明もない場合、単に列名が似ているという理由だけで正確な口径を自動判定することはできません。より誠実な対応としては、候補を提示するか、あるいは確認を求めることです。LLM はデータ利用のハードルを下げることはできても、企業がこれまで行っていなかったデータガバナンスを勝手に補完するものではありません。
セマンティックモデルが存在する場合でも、物理的な JOIN をモデルに推測させるべきではありません。
多くのシステムが「セマンティック対応」と謳っていても、実際には単にフィールドのコメントをプロンプトに追加しているだけというケースが少なくありません。私たちが採用したアプローチは、より具体的なセマンティックモデルのパスを実装しています。
通常のデータセットでは物理スキーマを経由します。エージェントは実際のテーブルとフィールドを確認し、対応するデータベースの方言に合わせて SQL を生成します。一方、セマンティックモデルを利用する場合は別の処理フローを踏みます。システムはまずモデル内の論理フィールドとその役割を取得し、それらを「次元(DIM)」「指標(FACT)」「時間軸(TIME_DIM)」に分類します。同時に、表示名、説明、データタイプ、およびモデル識別子も保持されます。
例えば、「各チャネルの過去 3 ヶ月間の有料転換率」をユーザーが問い合わせた場合、エージェントは「チャネル」「有料転換率」「統計日付」といったガバナンス済みの論理オブジェクトを使用し、まず MODEL('') を用いた SemanticQL を生成します。その後、セマンティッククエリサービスによって実行用の物理 SQL へ変換されます。基盤となるファクトテーブルの結合方法や指標の数式展開、異なるストレージエンジンへの対応などは、モデルがその場で推測するのではなく、事前に定義されたルールに基づいて処理されます。
変換に失敗した場合でも、システムは問題のある SQL をそのままユーザーに提示することはありません。変換エラー、直前の SemanticQL バージョン、利用可能なフィールドリストをまとめてエージェントへフィードバックします。もし「不明な識別子(Unknown identifier)」が発生すれば、エージェントは既知の実際のフィールドから再選択し、制限された試行回数内で修正を試みます。このリトライは根拠に基づいたエラー修正であり、モデルが同じ答えに対して運任せで繰り返し挑戦させるものではありません。
もちろん、セマンティックモデルは既存のガバナンス価値を拡大するものであり、存在しない口径を生み出すことはできません。上流で統一された指標、論理フィールド、および関係性が定義されて初めて、Omega は安定的に消費可能です。上流に単なる命名規則が適当なワイドテーブルしか存在しない場合、システムはスキーマ検証によって存在しないフィールドをブロックできますが、各社固有の業務意味合いを自動で推測して正しく解釈することを保証することはできません。これが私たちがあえて守る境界線です。
連動するフィルタリング機能の本質は、クエリの依存関係図にあります。
指標が特定された後でも、ページ上で結果を硬直的に書き込むわけではありません。Omega は HTML 出力とは別に、構造化データ契約である QueryRegistry を生成します。以下は簡略化されたクエリ宣言の例です:
「queryId」はページとクエリを結びつける永続的な識別子です。「datasetId」は、リクエストが登録済みのデータセットへ正しくルーティングされるために使用されます。また、「params」にはパラメータの型や日付精度、デフォルト値、ペアリング関係、カスケード(連鎖)ソース、オプションクエリの情報などが保存されています。
さらに「fieldBindings」は、SQL の出力結果においてどの列が次元(ディメンション)、どの列が指標(メトリック)なのかを明確に定義します。これにより、「チャートでは『売上』を表示しているのに、SQL は『total_gmv』を返す」といった、気づきにくい静かな失敗を防ぐことができます。
ページ側で手動で一時的な連動ロジックを組み立てる必要はありません。DTBridge が読み込まれる際、各 SQL テンプレート内のプレースホルダーをスキャンし、「パラメータ名 → queryId[]」という依存関係のテーブルを構築します。例えば「region(地域)」が「販売トレンド」「カテゴリ構成」「顧客ランキング」「地図」の 4 つのクエリに共通して登場している場合、地域フィルターを更新した際、システムは即座に影響を受けるクエリ集合を特定し、該当する 4 つのグラフだけを再描画します。地域とは無関係な在庫カードなどは、一切動きません。
実際のビジネス現場では、時間フィールドのフォーマットが統一されていないことも珍しくありません。同じ日付フィルター値でも、事実テーブル(ファクト)内では「2026-07-22 00:00:00」に変換される必要がある一方、パーティションフィールドでは「20260722」という形式が求められることがあります。QueryRegistry は「valueType」を通じてフォーマット情報を記録し、「aliasOf」によって複数の SQL パラメータを共通のフィルター標準値と共有させます。実際のフォーマット変換は実行時に一元的に処理されるため、モデル側で各グラフごとに日付処理コードを記述する必要はありません。
連続したドラッグ操作や素早い切り替えが発生した場合、複数回の変動はデバウンス(遅延)によって統合され、未完了の古いリクエストは自動的にキャンセルされます。また、異なるチャートカード間のクエリは並列で実行され、それぞれが独立してローディング状態を管理します。カスケードフィルターも同じ依存グラフ上で動作します。例えば「省」を変更すると、「市」の選択肢クエリがその省パラメータに依存しているため、実行時にはまず都市の候補を更新し、その後、関連する都市のチャートを再描画します。
パラメータ注入は、日付や秒・ミリ秒といった型に応じた処理を行い、文字列リテラルに対してはエスケープを適用し、セミコロンやコメントブロックを除去します。必須項目が不足している場合は未解析マーカーを残し、呼び出し元側で実行を阻止します。「すべて」の選択肢については、条件分岐や空集合のセマンティクスによって処理されます。ただし、この層のエスケープ処理を「絶対的なインジェクション防止」として包装するつもりはありません。異なるデータベースエンジンでは、共通のプレコンパイルパラメータ機構が利用できない場合もあるため、真のセキュリティ確保にはサーバーサイドでのクエリ防御ラインが不可欠です。
安全は iframe の属性ではない
「データが漏洩しない」というのは、見栄えは良くても責任の所在が曖昧な言葉になりがちです。確かに、権限を持つユーザーに結果が表示されれば、そのユーザーが見ることは当然です。システムが真に守らねばならないのは、「権限のない人間にはアクセスさせない」ことです。具体的には、すべてのクエリ実行前に、現在の訪問者および共有関係に基づく認証を通過させ、サーバー側で解析された有効なクエリ身份(アイデンティティ)をバインドする必要があります。生成されたページがソースの機密情報を取得できないようにし、クエリも許可された境界を超えられないようにします。
Omega における最初のセキュリティ境界は「身份と認証」にあります。クエリを実行する身份は、サーバー側で現在のログイン状態から解析されるものであり、ページ側が独自に userId を宣言して認めることはできません。通常のアクセスではまずファイルやデータセットの権限をチェックし、共有リンクからのアクセスではまず共有関係を検証した上で、そのクエリで使用する有効な身份を解析します。データセットの Schema(スキーマ)、クエリの実行、ファイルの開封はそれぞれ個別に権限判断を行います。一度 Agent がデータセット ID を取得したからといって、その後のすべてのリクエストが合法であるとみなしてはいけません。
2 つ目の境界は「クエリ」にあります。構文解析が可能な SQL 方言については、バックエンドで AST(抽象構文木)チェックを行い、単一の読み取り専用 SELECT ステートメントのみを許可します。複数ステートメントや危険な関数は拒否し、サブクエリや CTE を含む実際のテーブル参照を抽出した上で、一律に行上限を設定します。AST 解析器を完全に再利用できない ClickHouse などのエンジンに対しては、より軽量な読み取り専用チェックを採用しつつ、ドライバーの readonly モード、クエリのタイムアウト設定、結果行数の上限といった仕組みに依存します。セキュリティポリシーはデータソースの差異を尊重すべきであり、「正規表現」や「汎用解析器」がすべてのデータベースをカバーできるかのように振る舞ってはなりません。
3 つ目の境界こそが「ページ実行環境」です。データベースのアカウント、パスワード、アクセス鍵が生成された HTML に含まれることはありません。ページは制御されたクエリ代理を通じてのみデータにアクセスします。iframe sandbox や CSP(コンテンツセキュリティポリシー)によって生成コードの実行能力を制限し、独立したプレビューではネットワーク接続範囲もさらに厳格化されます。ただし、iframe をデータ漏洩防止の主力システムとして扱うわけではありません。ページは依然としてクエリ契約を保持しており、許可された結果を表示することは避けられません。極めて機密性の高いデータについては、上位レイヤーでの行・列レベルの権限管理、フィールドのマスク処理、そしてより厳格なネットワーク出口ポリシーが必要です。
見落としがちなのが「身份のライフサイクル」です。永続化(保存)する際、システムは一時注入されたランタイムスクリプトや閲覧者の情報を剥離します。実際にページを開くときや共有するときにのみ、唯一の QueryRegistry と DTBridge SDK、そしてその回のアクセスに固有なシステムパラメータが注入されます。データ契約自体は保存可能ですが、ランタイム上の身份を永続的なスナップショットに焼き付けることはできません。
生成完了後、システムは構造と契約層における静的チェックを行います。パラメータが SQL のプレースホルダーに対応しているか、グローバルフィルターで特定のクエリが漏れていないか、オプションクエリが存在するか、チャートの読み取りフィールドが SQL 出力のエイリアスと整合しているかなどを確認します。初回書き込み後に SQL の事前検証を行い、修正可能な問題が見つかった場合は新しいバージョンを生成します。単にビジュアルスタイルを変更する際は、すでに検証済みの QueryRegistry をできるだけ再利用し、色の変更だけで指標の定義が誤って書き換わらないようにします。
つまり、Omega におけるモデルの出力は最終製品ではなく、リンク付け、型チェック、権限確認、ランタイムスケジューリングを経る必要がある「ソースコード」のようなものです。モデルは表現力を高める役割を担い、決定論的なシステムがそれを持続可能なデータプロダクトへと制約します。
AI 生成後、人間の仕事は続く
人は一度で完璧な要件を伝えることはめったにありません。結果を見て初めて「ここが違う」と気づくものです。タイトルが長すぎる、図の選択が間違っている、特定の指標を分割すべき、フィルタリング範囲が不十分などです。真にワークフローに組み込まれた製品とは、ユーザーに対して「ページ全体を受け入れるか、すべてやり直すか」の二者択一を強いるものではなく、人間が引き続き作業を引き継げるように設計されているはずです。
小改动なら AI に指示するだけでいい。「2 枚目の図を折れ線グラフに変えて」と言えば、AI は対象領域を正確に特定し、その部分だけを修正します。レイアウトやスタイルの微調整が必要であれば、ユーザーは GUI でドラッグ&ドロップで対応可能です。大幅な変更が必要な場合は、「Plan Mode」がまず修正予定内容をリストアップし、確認後に実行することで、曖昧な指示によってページ全体が崩れてしまうのを防ぎます。
こうした協働を可能にしているのは、私たちが長年維持してきた「A-MD 协议(プロトコル)」です。A-MD はファイルレベルの外部プロトコルで、1 つの AMDFile には SQL、Python コード、チャート、指標、フィルター、説明文などのブロックと、異なるビュー向けのレイアウトが含まれます。Canvas は並列する別のモデルではなく、dashboardLayout.canvas の下位にある Dashboard ビューの载荷(ロード)です。主に HTML と QueryRegistry を保存します。A-MD がファイル、バージョン、編集可能な分析プロセスを管理し、Canvas が最終ページの動的クエリを担当します。
GUI をなくすつもりはありません。自然言語は意図を伝えるのに適しており、マウスは精密な調整に、コードは複雑なロジック処理に適しています。「AI ネイティブ」だからといって、すべてのインタラクションがチャットボックスを経由しなければならないわけではありません。重要なのは、製品が AI に任せるべきタイミングと、人間に制御権を返すべきタイミングを正しく判断できることです。
見栄えは単なる飾りではない
データ製品を作る人は、往々にして審美性を過小評価しがちです。データの正確性が色よりも重要だと考えがちだからです。しかし現実には、二度と開く人がいない分析レポートがどれほど影響力を持つでしょうか。経営層が見るのは背後にある SQL ではなく、画面そのものです。現場の担当者が結果をチャットグループに共有したり、報告資料に組み込んだりするかは、数秒で決まります。
AI はこの点において「平均的な顔」を持っています。自由な発想で 10 枚のダッシュボードを作らせれば、おそらく 10 枚とも「白地に青い棒グラフ、KPI カードが横一列に並ぶ」という似たようなデザインになります。稀にダークモードに変えても、それは馴染み深い紫のグラデーションです。「高級感」「テクノロジー」「抑制されたデザイン」などプロンプトを長く書いてみたこともありますが、改善は限定的で不安定でした。
そこで Omega には、審美性を支えるインフラストラクチャを整備しました。現在までに 23 のビジュアルスタイルと 18 の可視化エンジンが蓄積されていますが、これらは単なるスキン(外見)の切り替え用テンプレートではありません。サイバーパンク風の監視用大型ディスプレイ、モバイル向けの経営日報、ミニマルな財務報告書など、違いは背景色だけではありません。情報密度、フォントの階層、余白、チャートの選択、インタラクション方式まで含めた総合的な設計が異なります。
このシステムには、社内でもよく使われる「薄配置(スリムコンフィグ)、厚エンジン」という設計思想があります。内蔵スタイルのソース設定は、数色のカラーパレット、1 つのフォント、数個のキーワード、そして簡潔なトーン説明など、約 120 字に圧縮されています。バックエンドはこの情報を展開し、チャートの色板、文字間の関係性、ボーダー、レイアウト、アニメーションの制約などを補完した、約 800 字の設計仕様書へと変換します。
審美性システムが最終的に解決しようとしているのは、派手な技術披露ではありません。2 つの現実的な課題です。同じデータでも、人間はもっと速く理解できるか。同じ結論でも、ユーザーはそれをスクリーンショットして上司に送りたいと思うか。
高コストパフォーマンスなモデルでも、複雑なダッシュボードは作れるか
最初のページで良好な結果が出た後、私たちはすぐに別の誘惑に直面しました。「より最先端のモデルを使えば、問題は解決するのではないか」という考えです。
確かに最先端のモデルには価値があります。複雑なレイアウト、コード生成、そして一度での完成率において、それらは通常優れたパフォーマンスを発揮します。しかし、データ分析製品では「最高の結果」を一度だけ目指せばよいわけではありません。毎日発生する大量のリクエストにかかるコストや、最悪の場合にシステムがどのような出力を生むかについても考慮する必要があります。
私たちは内部で固定されたサンプルテストを行いました。同じダッシュボードの生成と修正タスクにおいて、Claude 4.6 のモデル呼び出し費用は約 14.2 元でしたが、混元 Hy3 は約 0.47 元でした。これは約 30 倍の開きがあります。この数字は特定の固定タスクにおけるコストサンプルに過ぎず、両モデルの品質を一般化して比較するランキングではありませんが、「よりコストパフォーマンスの高いモデルでも、複雑な作業を安定して完了できるのではないか」という問いを真剣に考えるには十分でした。
実際に取り組み始めてみると、最も厄介なのはモデルが明確に「失敗しました」と報告することではなく、完成したように見える結果を提出してくることだと分かりました。
HTML が途中で切れていても、タイトルと最初の 3 つの画像は残っています。SQL はデータを取得していましたが、ページにバインドされているのは別のフィールド名でした。時間フィルターにはミリ秒が渡されていましたが、データベースのフィールドには秒で保存されています。モデルは記憶に基づいて存在しない SDK API を書き出しました。データには明らかに 3 つのテーブルしかないのに、あたかも本物かのように見える第 4 のテーブルを勝手に作り上げています。
これらの問題に共通するのは、明確な警告(レッドフラグ)がないことです。ユーザーがページを開き、フィルターをクリックし、あるいは数週間後にデータの変化に気づいて初めて、「これは機能していない」と発見します。
その後、私たちはモデルの「自己判断」だけに安定性を頼るのをやめ、その外側に Harness という仕組みを追加しました。これを Agent の作業環境と理解してください。モデルがどのようなコンテキストを受け取り、どのツールを呼び出せるか、タスクをどのように分割するか、出力はどのような検証プロセスを経るか、失敗した場合はどうフィードバックして修復するか——これらを統括する仕組みです。
ツール層も特定の Agent フレームワークに縛り付けられていません。ランタイム時には、現在の Canvas やファイルタイプ、ユーザーの選択、計画段階に応じて、信頼度の高いツールを自動的に事前アクティブ化します。残りの機能は、モデルが select_tools を通じて段階的に選択する形をとります。これにより、数十個ものツールのスキーマが同時にコンテキストに読み込まれるのを防ぎます。ビジネス用ツールはフレームワークに依存しない統一された契約に従っており、基盤となる Agent フレームワークを変更する場合でも、主に適応層の調整だけで済みます。
現在、ダッシュボードの生成はモデルからの出力が直接ユーザーに表示されるわけではありません。システムがまず構造の完全性、クエリ声明の有無、チャートが実際のクエリにバインドされているかを確認します。途中で HTML が切れている場合は、不完全な部分を切り捨てて必要な閉じタグを補完し、生成を続行します。フィールドの別名や時間フォーマット、フィルターオプションといった明確な問題はルールで処理し、テーブル名は可能な限り実際のメタデータから選択して実行前に検証します。
ルールで修正できることはモデルに任せず、ルールでは判断できない意味的な問題のみを、エラーコンテキストを持った Agent に有目的の修復として委ねます。
「生成したばかりの長いコードをチェックしてすべて修正してほしい」とモデルに指示するのは一見賢明に見えますが、実際にはコストが高くつき、テストも困難です。また、一处を直しても別の箇所を壊してしまうリスクさえあります。エラーを種類ごとに分類することで、システムは個々のケースに対する確実な解決策を一つずつ蓄積できます。Omega は結果を書き込む前に、現在のユーザーの権限を再検証します。Agent がファイル ID を取得したからといって、自動的にそのファイルを上書きする権限があるとはみなしません。
遅い SQL によって露呈したランタイムの問題
Omega の運用初期に、非常に典型的な事故が発生しました。
あるユーザーが分析を依頼した際、高基盤数フィールドの COUNT(DISTINCT) を計算する SQL がありましたが、クエリ自体にデッドロックやエラーは発生していませんでした。問題は、実行中に中間データが返されない点です。この現象は複数回再現され、同様のクエリでは通常 110〜130 秒を要することが判明しました。
当時、システムには 120 秒のアイドル・タイムアウト(空のウォッチドッグ)が存在していました。これにより、データが 120 秒以上受け取れないと接続がフリーズしたと判断され、強制的に切断されます。結果として、正常に動作していた慢性的なクエリが「ゾンビ」と見なされて殺害されてしまいました。
ユーザー側には明確なエラーメッセージは表示されず、回答が途中まで書かれていたかと思いきや、突然停止してしまいます。さらに深刻なのは、フロントエンド側の接続は切断されたものの、停止信号が最下層の処理エンジンに伝わっていなかった点です。すでに不要になったはずの SQL がバックエンドで実行され続け、30 分後にようやくタイムアウトするまで放置されていました。
今回の事故を機に、Agent の実行環境では単なる散発的な timeout(時間切れ)設定に頼るだけでは不十分だと痛感しました。少なくとも以下の 4 つの問いに答えられる必要があります。「各ステップの最大実行時間はどれくらいか?」「ユーザーが停止指示を出した際、リソースは本当に解放されるのか?」「どのような終了方法であっても、フロントエンド側にはその理由が明確に伝わるか?」「一部の処理で失敗した場合でも、モデルは別の経路を試す余地があるか?」
その後、これらを 4 つの「実行時契約」へと整理しました。それは「有界(時間制限付き)」「取消可能」「観測可能」「自己修復可能」という原則です。
現在、SQL のような末端ツールのデッドラインは、各業務コードに散在するのではなく、ツール側で自ら宣言し、統一されたアダプター層がそれを管理します。ユーザーが停止ボタンを押すと、取消信号はクエリを実行している真正の場所まで確実に伝達されます。また、各ラウンドの実行結果が正常終了、タイムアウト、または取消であっても、必ずかつただ一つの型付き finish{reason, detail} を返すことが義務付けられています。異常終了した場合でも、フロントエンド側では生成済みのコンテンツは保持され、回答全体が抹消されることはありません。
継続的なストリーミング出力を行うサブエージェントについては、末端ツールのタイムアウト方式をそのまま適用することはできません。実行環境は推測に頼らず、delegate_agent_* や backend_agent とマークされたツールに対しては deadline-race の制限を豁免し、運用設定でこれを再強化することも禁止しています。これらのタスクには、ラウンド全体のライフサイクル上限、活性検知(ハートビート)、およびストリーミング・ハートビート管理を採用します。
失敗が必ずしもタスク全体の失敗を意味するわけではありません。ツールがタイムアウトした場合、モデルは明確な理由を取得し、クエリの範囲を縮小するか、別の戦略に変更するかを判断します。また、重複呼び出しに対するガードレールも導入しました。過去には、モデルが同じツールに対して 32 回連続で全く同一の要求を発行する事例がありました。現在はシステムがまず代替経路への切り替えを促し、閾値に達するまで繰り返し実行した上で、直ちにサーキットブレーカー(遮断器)を作動させます。
これらの詳細は製品デモではあまり語られません。しかし、Agent を本番環境で運用できるかどうかは、順調な時の知能の高さではなく、タイムアウト、切り捨て、重複呼び出し、取消といった困難な局面において、いかに「体面ある着地」ができるかにかかっています。
対話終了後こそ、Agent の真価が発揮される
以前は、ダッシュボードの完成度を判断する基準を「生成成功して保存されたか」としていました。しかし、使い続けるうちにこの基準が不適切だと感じるようになりました。
ビジネス現場で本当に必要とされているのは、「今日一度だけデータを見ること」ではなく、「将来も忘れずに継続的に確認し続けること」です。
現在、Omega では任意のチャートに監視ルールを設定できます。「コンバージョン率が 5% を下回り、かつ注文数が 1000 を上回る場合」にアラートを発令するといった条件設定や、複数カードを跨いだ複合条件の設定も可能です。バックエンドではスケジュールに従ってクエリを再実行し、条件を満たせば企業微信(WeCom)やメールで通知を送信します。
ダッシュボードは週次または月次の定期プッシュにも対応しています。指定時刻になるとシステムは古いキャッシュを迂回し、最新データを用いてスクリーンショットを再取得します。一部のクエリが失敗しても、全体のプッシュが完全に消失することはありません。送信可能なコンテンツは可能な限り配信され、失敗の詳細はプッシュ記録に保持されます。データ製品にとって、記録を残した上で段階的に機能を低下させる(デグレード)ことは、何も通知せずに静かに無視するよりも遥かに有用です。
編集履歴は常にバージョン管理され、ユーザーは差分(diff)を確認したり、過去の結果に戻ったりできます。コピーや公開を行う際も、バージョン履歴は成果物に付随します。また、権限設定は単に複製されるのではなく、対象空間で再認証されます。アクセス記録、チームコラボレーション機能、そして企業微信ボット連携により、この分析は個人のワークスペースから脱却し、真の協働環境へと統合されていきます。
Skills や MCP といった仕組みも、これらの機能を Omega の外側へ拡張しています。ユーザーが最終的に気にするのはプロトコルの名称ではなく、「企業微信で同じ質問ができるか」「別の Agent がこの分析を呼び出せるか」「データ異常時に自動通知を受け取れるか」といった実用的な価値です。毎回チャットウィンドウを開き直す必要がないことが重要です。
結びに
まだ Omega が「次世代 BI」の答えだと断言するには時期尚早です。実際のデータはデモ用のデータよりもはるかにノイズが多く、企業の権限管理や業務フローは単なるプロンプト(指示文)では対応しきれないほど複雑です。また、多くの機能体験はまだ急速に変化しています。
AI 時代において、ページを生成すること自体のコストは低下していきます。真に価値があるのは、生成されたページが翌日も使えるか、その分析結果が数週間後も有効かを維持できるかどうか、そして AI エージェントが誤作動を起こした際に依然として信頼に値するかどうかという点です。
現在、ベータ版「マルモス(Marmos)」をリリースしています(marmos.qq.com)。ぜひお問い合わせください。
WeChat で開くにはこちらへ
原文を表示
原创 腾讯程序员 2026-08-03 18:48 广东
image
AI 数据看板最难的,不是生成
image
作者:jiaxingjin
上传一个 Excel,敲下一句话,等上几十秒,一张带着 KPI、趋势图和排行榜的页面就出现在屏幕上。颜色鲜艳,动画流畅,截图发到群里,很容易得到一句“这个厉害”。
但如果你真打算用它,问题往往从第二天开始。
时间范围从“本月”换成“上月”,数字还停在原地;点一下地区筛选,三个图表各算各的;数据表多了一个字段,整张页面直接空白;想把柱状图换成折线图,AI 又从头生成一遍,把你已经确认的布局也顺手改了。
这时候你会发现,昨天看到的不是一个数据产品,只是一张由 AI 画出来的、恰好很像数据产品的图。
2024 年,我们在 DataTalk 里做过 ChatBI:用户在侧边栏里提问,AI 生成 SQL,再把查询结果画成图。方向听上去没错,实际用下来却很别扭。SQL 经常要人收拾,图表质量不稳定,更关键的是,AI 和原来的 BI 系统始终像两套东西——它能回答一个问题,却接不住后面的分析工作。
后来,我们把它推翻了。
不是给聊天框换个模型,也不是把按钮重新排一遍,而是重新想了一次:如果今天从零设计一个数据分析产品,AI 到底应该站在哪里?
聊天框不是 BI 的下一代
传统 BI 的工作方式很明确:找数据、选字段、拖组件、配筛选器、调样式。它很强,也很专业,但前提是用户已经知道自己要分析什么,还得知道怎么把问题翻译成一组操作。
ChatBI 看似跨过了这道门槛。用户不用拖拽了,直接问:“上个月各品类销售怎么样?”AI 把自然语言翻译成 SQL,查完返回一张图。
可分析很少在第一张图结束。
你会继续追问:为什么华南区掉得这么厉害?是订单少了,还是客单价下来了?大客户和中小客户的趋势一样吗?把异常最明显的三个城市单独拿出来看看。最后,你可能还要把结果整理成一个页面,发给团队,并在下周一数据更新时重新看一次。
如果 AI 只能完成“问题到 SQL”这一步,它只是缩短了一次查询,并没有接管一段完整的分析工作。
Omega 想做的是后者。
你说一句“帮我分析上季度各品类的销售情况”,它先理解数据源和字段,再规划这一轮分析应该看哪些指标:总销售额、环比、趋势、品类结构、地区差异。随后自己查询数据、选择图表、组织页面,生成一份完整的 Dashboard。
你觉得第二张图不直观,说“换成柱状图”,它只改第二张图;你想补一个地区筛选器,它把控件加上,并让相关图表一起联动;你觉得配色太重,直接让它调浅一点,也可以打开可视化编辑器自己拖。
页面保存后可以分享,数据会继续刷新,下一次还能沿着这次的结果继续分析。
Omega 的核心开发长期只有 3—4 个人。没有泾渭分明的产品、设计和测试角色,很多决定都是几个人一边写代码,一边争论产品应该长成什么样。2026 年 1 月中旬开工,前两个月把主链路跑通。
但回头看,两个月做出来的只是一个“能演示”的 Omega。后面几个月,我们做的其实是另一件事:让它离开演示环境之后,仍然像个产品。
一块看板,什么时候才算“活着”
AI 生成页面并不难。今天的模型写 HTML、CSS 和 JavaScript 已经相当熟练,配合 ECharts 或 D3,做出一张视觉上不错的看板,门槛比两年前低了很多。
难的是让这张页面和真实数据之间,始终连着一根可靠的管子。
很多 AI 看板的实现,是把查询结果直接写进页面:
const data = [
{ month: '1月', sales: 1280 },
{ month: '2月', sales: 1530 }
]这段代码没有错,甚至很适合做 demo。但它生成之后,数据就凝固了。换时间、加筛选、刷新数据,都得重新找 AI 生成一次。页面看着是活的,里面的数据其实已经死了。
Omega 生成页面时,除了 HTML,还会生成一份结构化的数据契约。内部叫 QueryRegistry:每张图需要哪条查询、接收哪些参数、查询结果怎样绑定到图表、它和哪些筛选器联动,都在这里声明清楚。真正的执行交给 DTBridge 运行时。
这有点像把职责拆开:AI 负责表达“我要什么”,系统负责保证“它怎么稳定拿到”。
用户切换日期时,DTBridge 会根据契约刷新受影响的图表。筛选器本身也是声明式的,AI 不需要为每个控件手写 addEventListener、拼 WHERE 条件和处理空值。
我们为什么坚持不让模型包办这些代码?因为这类代码单看每一段都不复杂,组合起来却非常容易静默出错。
我们碰到过一个很典型的问题:SQL 明明返回了数据,图表却是空的。数据库没报错,浏览器控制台也没有异常。最后排查发现,页面回调读取的是 总上报量,SQL 输出的列名却是 total_pv。
两边分别看都合理,合在一起就是拿不到值。
后来,我们增加了字段绑定检查:从页面真实读取的字段反向校验 SQL 输出别名。类似的校验还有很多——时间字段到底是秒还是毫秒、筛选器的值能不能对应查询参数、图表声明里的查询 ID 是否真实存在。它们都不性感,却决定了一块看板是“偶尔成功”,还是“多数时候可以交付”。
数据真正流动起来之后,安全和生成体验也得由系统接管。页面不会拿到数据库账号和密钥,查询与权限校验留在服务端;后端按数据源能力执行 AST 只读校验或轻量只读检查,并叠加查询服务的权限控制与统一超时。页面运行在受限制的 iframe 中。看板流式生成时,我们只更新已经安全到达的样式和正文,完成后再统一加载脚本,避免页面反复白屏。用户看到的只是页面平滑地长出来,背后的边界不能靠模型临场发挥。
AI 怎么找对指标、联动筛选,又怎样守住数据边界
如果你做过 BI,看到这里大概会有几个本能的追问:模型凭什么知道“销售额”对应哪一列、哪一种聚合?企业已经维护的指标口径和语义模型怎么接进来?一个筛选器为什么能同时驱动五张图?一张由 AI 写出来的 HTML,又凭什么被允许查询生产数据?
这些问题不能靠一句“我们用了更强的模型”回答。
Omega 没有绕过 BI 的底座。DataTalk 已有的数据源接入、元数据、语义模型、查询引擎和权限体系仍然在下面。Agent 负责理解问题、组织分析和生成界面,但涉及数据含义与执行边界时,它必须回到系统能够验证的证据上。
从一句业务问题到一块能运行的页面,实际走的是这样一条链路:
用户指定的数据 / 已有图表 SQL / 业务知识
↓
Schema 与语义模型解析
↓
分析规划与查询工具
↓
HTML + QueryRegistry
↓
契约校验、SQL 预验证
↓
DTBridge → 权限校验 → 查询代理 → 图表回调先找证据,再谈“理解指标”
假设用户说:“看一下上个月各地区的销售额。”真正困难的不是写出 SUM,而是确定“销售额”到底是什么:含税还是不含税,是否扣除退款,按支付时间还是下单时间统计。企业里两个名字相同的指标,口径完全可能不一样。
Omega 找指标时有一条很重要的原则:离用户当前工作现场越近的证据,优先级越高。
如果用户正在修改一张已有图表,最可信的不是 LLM 对“销售额”的常识,而是这张图已经执行过的真实 SQL。系统会把当前图表的 datasetId、查询说明、数据库引擎和 SQL 一起交给 Agent。SQL 里如果写的是 SUM(pay_amount - refund_amount),或者 COUNT(DISTINCT buyer_id),这就是当前页面已经采用的业务定义;后续增加地区维度、改图表类型时,应当复用这个定义,而不是重新发明一遍。
如果是一次新分析,用户在消息里明确选择的数据集优先于历史页面和系统搜索结果。生成前,Agent 必须先调用 Schema 工具,拿到真实的物理字段、展示名、类型、字段说明、分区信息、数据源引擎和连接路由。这个动作不只写在 prompt 里,外层还有 Schema Guard:用户已经指定了数据,模型却没有读 Schema 就开始生成页面,流程会被拦住。
用户没有明确指定数据时,系统才会在个人空间、团队空间和可访问的数据目录中检索。检索不是把所有表名一股脑塞给模型,而是同时看名称、描述、物理表名、关键词覆盖、数据归属和更新时间,合并去重后给出候选。它解决的是“从几千个数据集中缩小范围”,并不假装自己已经理解了缺失的业务口径。
还有一类证据来自业务知识。用户可以引用指标说明、分析方法或业务文档,Agent 会把相关片段带进本轮上下文。于是“有效成交额要扣除全额退款”“新客按首次支付日期认定”不再只是聊天中的一句提示,而会和字段元数据、已有查询一起参与规划。
这几层信息构成的,其实是一条指标证据链:已有查询口径 → 用户指定的数据 → 语义模型和字段元数据 → 被明确引用的业务知识 → 检索候选。证据足够,系统才有条件自动生成;如果上游既没有指标定义,用户也没有说明,仅凭一个相似列名,系统就不具备自动判定准确口径的条件。更诚实的做法是暴露候选或继续确认。LLM 可以降低使用数据的门槛,不能顺手替企业补完从未做过的数据治理。
有语义模型时,不让模型猜物理 JOIN
很多系统所谓“支持语义”,其实只是把字段注释拼进 prompt。我们接入的语义模型路径更具体。
普通数据集走物理 Schema:Agent 看到真实表和字段,按对应数据库方言生成 SQL。语义模型则走另一条链路。系统先取得模型中的逻辑字段和角色,把它们分类为维度 DIM、指标 FACT 和时间维度 TIME_DIM,同时保留展示名、描述、类型以及模型标识。
例如用户问“各渠道近三个月的付费转化率”,Agent 使用的是“渠道”“付费转化率”“统计日期”这些经过治理的逻辑对象,先生成基于 MODEL('<modelCode>') 的 SemanticQL;随后由语义查询服务把它转换成真正执行的物理 SQL。底层事实表怎样关联、指标公式怎样展开、不同存储引擎怎样适配,不再由模型临场猜测。
转换失败时,系统也不会把一段有问题的 SQL 直接扔给用户。转换错误、上一版 SemanticQL 和可用字段列表会一起反馈给 Agent;遇到 Unknown identifier,它只能从已经拿到的真实字段中重新选择,并在限定次数内修复。这里的重试是有依据的纠错,不是让模型对着同一个答案反复碰运气。
当然,语义模型只能放大已有治理的价值,不能创造不存在的口径。上游定义了统一指标、逻辑字段和关系,Omega 才能稳定消费;上游只有一堆命名随意的宽表,系统可以通过 Schema 校验拦住不存在的字段,却不能保证自动猜中每家公司独有的业务含义。这是我们愿意保留的一条边界。
联动筛选,本质上是一张查询依赖图
指标找到之后,页面还不能把结果直接写死。Omega 会在 HTML 之外生成一份结构化数据契约 QueryRegistry。下面是一条经过精简的查询声明:
{
"queryId": "q_sales_trend",
"datasetId": "sales_dataset",
"sqlTemplate": "SELECT ds, SUM(gmv) AS sales FROM orders WHERE ds BETWEEN {{startDate}} AND {{endDate}} GROUP BY ds",
"params": [
{ "name": "startDate", "type": "date", "filterType": "dateRange", "pairWith": "endDate", "valueType": "str_YYYY-MM-DD" },
{ "name": "endDate", "type": "date", "valueType": "str_YYYY-MM-DD" }
],
"fieldBindings": [
{ "alias": "ds", "role": "dimension", "targetType": "chart-axis" },
{ "alias": "sales", "role": "metric", "targetType": "chart-series" }
]
}queryId 是页面和查询之间的稳定引用;datasetId 把请求路由到已经登记的数据集;params 保存参数类型、日期精度、默认值、成对关系、级联来源和选项查询;fieldBindings 则明确 SQL 输出中的哪一列是维度、哪一列是指标,避免图表读取 销售额、SQL 却返回 total_gmv 这类静默失败。
页面不负责临时拼接一套联动逻辑。DTBridge 加载时会扫描每条 SQL 模板中的占位符,构建一张 参数名 → queryId[] 的依赖表。假如 region 同时出现在销售趋势、品类结构、客户排行和地图四条查询中,地区筛选器更新后,系统可以直接计算出受影响的查询集合,只刷新这四张图;和 region 无关的库存卡片不动。
真实业务里的时间字段还经常不是一种格式。同一个日期筛选值,在事实字段里可能要变成 2026-07-22 00:00:00,在分区字段里却要变成 20260722。QueryRegistry 可以通过 valueType 记录格式,再用 aliasOf 让两个 SQL 参数共享同一个筛选器的标准值。格式转换在运行时统一完成,模型不需要在每张图里各写一份日期处理代码。
连续拖动或快速切换筛选器时,多次变化会先被防抖合并;同一查询未完成的旧请求会被取消;不同图卡的查询并发刷新,并分别维护 loading。级联筛选也沿着同一张依赖图工作:省份变化后,城市选项查询引用了省份参数,运行时就会先更新城市候选,再刷新依赖城市的图表。
参数注入会根据类型处理日期、秒和毫秒,对字符串字面量做转义,去掉分号和注释片段;缺失的必填值会留下未解析标记,由调用方阻止执行,“全部”则通过条件块或空集合语义处理。但我们并不把这层转义包装成“绝对防注入”——不同引擎并非都能使用同一种预编译参数机制,真正的安全还需要服务端的查询防线。
安全不是一个 iframe 属性
“数据不泄露”很容易写成一句漂亮但不负责任的话。只要结果已经展示给有权限的用户,这个用户当然能够看到它。系统真正要保证的是:无权限的人拿不到;每次查询都先通过当前访问者及分享关系的授权,再绑定服务端解析出的有效查询身份;生成页面拿不到源凭证,查询也不能越过允许的边界。
Omega 的第一道边界在身份和授权。查询身份由服务端从当前登录态解析,不接受页面自己声明一个 userId。普通访问先校验文件和数据集权限;分享访问先校验分享关系,再解析这次查询应使用的有效身份。数据集 Schema、查询执行和文件打开分别做权限判断,不能因为 Agent 曾经拿到一个数据集 ID,就默认后续所有请求都合法。
第二道边界在查询。对能够完整解析的 SQL 方言,后端使用 AST 检查保证单条、只读 SELECT,拒绝多语句和危险函数,提取包括子查询、CTE 在内的真实表引用,并统一加上行数上限。对 ClickHouse 这类无法完全复用同一解析器的引擎,则采用较轻的只读检查,同时依赖驱动的 readonly 模式、查询超时和结果上限。安全策略要尊重数据源差异,不能假装一条正则或者一个通用解析器能覆盖所有数据库。
第三道边界才是页面运行环境。数据库账号、密码和访问密钥不会进入生成的 HTML,页面只能通过受控查询代理请求数据;iframe sandbox 和 CSP 用来限制生成代码的能力,独立预览还会收紧网络连接范围。但我们不把 iframe 当成数据防泄漏系统:页面仍可能持有查询契约,也一定会拿到获准展示的查询结果。对高度敏感的数据,仍然需要上游行列权限、字段脱敏以及更严格的网络出口策略。
还有一个容易忽略的身份生命周期:持久化时,系统会剥离临时注入的运行时脚本和查看者信息;真正打开或分享时,再注入唯一一份 QueryRegistry、DTBridge SDK 和本次访问的系统参数。数据契约可以保存,运行时身份不能被永久烘焙进快照。
生成完成后,系统还会做结构和契约层的静态检查:参数有没有对应 SQL 占位符、全局筛选器是否漏掉某条查询、选项查询是否存在、图表读取字段能否与 SQL 输出别名对齐。初次写入后再做 SQL 预验证,发现可修复的问题则生成新版本;只改视觉风格时,尽量复用已经验证过的 QueryRegistry,不让一次换色顺手改掉指标口径。
所以,Omega 里的模型输出不是最终产物,更像一份需要经过链接、类型检查、权限检查和运行时调度的源代码。模型负责提高表达能力,确定性的系统负责把它约束成一个可以持续运行的数据产品。
AI 生成完,人还得接着工作
人很少能在第一遍就把需求说完整。看见结果之后,才会知道哪里不对:标题太长,图选错了,某个指标应该拆开,筛选范围还不够。一个真正进入工作流的产品,必须允许人继续接手,而不是逼用户在“接受整页”和“全部重来”之间二选一。
小改动可以直接告诉 AI,“把第二张图换成折线图”,它精准定位目标区域,只改那一块;需要微调布局和样式时,用户可以在 GUI 里拖拽;变化比较大时,Plan Mode 会先列出准备修改的内容,确认后再执行,避免一句模糊的要求把整张页面改得面目全非。
底层之所以能支持这种协作,和我们一直在维护的 A-MD 协议有关。A-MD 是外层文件协议:一个 AMDFile 同时包含 SQL、Python、图表、指标、筛选器和说明文字等 Block,以及不同视图的 Layout。Canvas 不是另一套并列模型,而是 dashboardLayout.canvas 下的一种 Dashboard 视图载荷,主要保存 HTML 与 QueryRegistry。A-MD 管文件、版本和可编辑的分析过程,Canvas 负责最终页面的动态查询。
我们不想把 GUI 消灭掉。自然语言适合表达意图,鼠标适合精确调整,代码适合处理复杂逻辑。AI 原生不等于所有交互都必须经过聊天框,而是产品知道什么时候该让 AI 接手,什么时候该把控制权交还给人。
好看不是锦上添花
做数据产品的人容易低估审美,因为数据准确显然比颜色重要。但真实世界里,一份没人愿意打开第二次的分析,很难产生多少影响。管理者看到的是页面,不是背后的 SQL;业务同学愿不愿意把结果发到群里、放进汇报,往往在几秒钟内就决定了。
AI 在这件事上有一种顽固的平均脸。让它自由发挥做十张看板,大概率会得到十张“白底、蓝色柱状图、四个 KPI 卡片排一行”的页面。偶尔换成深色,就是熟悉的紫色渐变。我们试过在 prompt 里多写几句“高级、科技、克制”,效果有改善,但很不稳定。
于是我们给 Omega 做了一套审美基础设施。现在积累了 23 种视觉风格和 18 种可视化引擎,但它们不是用来换皮肤的模板。赛博朋克监控大屏、移动端经营日报、极简财务报告,差的不只是背景色,还有信息密度、字体层级、留白、图表选择和交互方式。
这套系统有一个我们内部常说的设计:薄配置,厚引擎。一份内置风格的源配置只保留几个颜色、一个字体、几个关键词和简短的调性描述,大约 120 字;后端把它展开成一份约 800 字的设计规格,补齐图表色板、文字关系、边框、布局和动效约束。
审美系统最后要解决的不是炫技,而是两个很现实的问题:同样的数据,能不能让人更快看懂;同样的结论,用户愿不愿意截图发给老板。
高性价比模型,也能把复杂看板做好
做出第一个效果不错的页面之后,我们很快碰到另一个诱惑:只要换更前沿的模型,问题是不是就解决了?
前沿模型当然有价值。它在复杂布局、代码生成和一次成型率上通常更好。但数据分析产品不可能只看最好的一次,还得考虑每天大量请求的成本,以及最差情况下系统会做出什么。
我们做过一个内部固定样例:同一份生成并修改一次看板的任务,Claude 4.6 的模型调用费用约 14.2 元,混元 Hy3 约 0.47 元,相差约 30 倍。这个数字只是一组固定任务下的成本样本,不是两种模型质量的通用排名,但它足以让我们认真思考:有没有可能让性价比更高的模型,也稳定完成复杂工作?
真正开始做之后,我们发现有一些模型最麻烦的地方,并不是它会明确告诉你“我失败了”,而是它会交出一份看起来已经完成的结果。
HTML 被截断了,它仍然有标题和前三张图;SQL 查到了数据,页面绑定的是另一个字段名;时间筛选器传的是毫秒,数据库字段存的是秒;模型凭记忆写出一个并不存在的 SDK API;数据里明明只有三张表,它顺手编出第四张,而且名字还很像真的。
这些问题共同的特点是:没有明显的红灯。用户只有打开页面、点击筛选器,甚至等到下周数据变化之后,才发现它不工作。
我们后来不再把稳定性完全押在模型的“自觉”上,而是在模型外面加了一套 Harness。可以把它理解成 Agent 的工作环境:模型拿到什么上下文,可以调用哪些工具,任务怎样拆分,输出经过哪些校验,失败之后怎样反馈和修复。
工具层也没有和某个 Agent 框架绑死。运行时会根据当前 Canvas、文件类型、用户选择和计划阶段,自动预激活高置信度工具;剩下的能力再由模型通过 select_tools 渐进选择,避免几十份工具 Schema 同时进入上下文。业务工具遵循统一的框架无关契约,底层更换 Agent 框架时,主要调整适配层。
现在,一次看板生成不会直接从模型输出跳到用户面前。系统先检查结构是否完整、查询声明是否存在、图表有没有绑定到真实查询;遇到截断,会裁掉不完整片段、补齐必要的闭合结构,再继续生成。字段别名、时间格式、筛选器选项这类确定性问题优先用规则处理,表名则尽量从真实元数据中选择并在执行前验证。
规则能修的,不让模型重写;规则无法判断的语义问题,再交给 Agent 带着错误上下文做一次有目标的修复。
让模型面对自己刚生成的一大段代码,说“请检查并修复所有问题”,看起来聪明,实际上既贵又不可测试,还可能修好一处、改坏另一处。把错误拆成类型之后,系统才能逐个积累确定的解法。写入结果前,Omega 还会重新验证当前用户的权限,不能因为 Agent 拿到了一个文件 ID,就默认它有权覆盖。
一条慢 SQL 暴露出的运行时问题
Omega 上线早期发生过一次很典型的事故。
一位用户发起分析,其中一条 SQL 在计算高基数字段的 COUNT(DISTINCT)。查询本身没有死锁,也没有报错,只是执行期间不会返回中间数据。后来多次复现,同类查询通常需要 110—130 秒。
当时系统里有一个 120 秒的空闲看门狗:这么久收不到数据,就认为连接卡死并把它终止。结果,一个正在正常工作的慢查询,被当成僵尸杀掉了。
用户端没有得到明确报错,回答只是写到一半突然停住。更糟的是,前端连接虽然断了,停止信号却没有传到最底层,那条已经没人需要的 SQL 仍在后端运行,直到 30 分钟后才超时。
这次事故让我们意识到,Agent 运行时不能只靠一堆散落的 timeout。它至少要回答四个问题:每一步最多执行多久?用户停止后,资源能不能真的释放?无论怎样结束,前端能不能知道原因?一次局部失败,模型有没有机会换条路继续?
后来我们把它们收成四个运行时契约:有界、可取消、可观测、可自纠。
现在,SQL 这类叶子工具的 deadline 由工具自己声明,再由统一适配层执行,而不是散落在各段业务代码里;用户点击停止后,取消信号会一直传到真正执行查询的地方。每轮运行无论正常结束、超时还是被取消,都必须返回且只能返回一个类型化的 finish{reason, detail}。即使异常终止,前端也会保留已经生成的内容,而不是把整段回答抹掉。
持续流式输出的子 Agent 则不能照搬叶子工具的超时方式。运行时不会靠猜测判断,而是明确对 delegate_agent_* 和标记为 backend_agent 的工具豁免 deadline-race,运维配置也不能把它重新武装;这类任务改用整轮生命周期上限、活性检测和流式心跳管理。
失败也不一定意味着整个任务失败。工具超时后,模型会拿到明确原因,再决定缩小查询或换一种策略。我们也给重复调用加了护栏——线上曾经出现过模型对同一个工具连续发起 32 次完全相同的请求。现在系统会先提醒它换路,继续重复到阈值后直接熔断。
这些细节很少出现在产品演示里。但一个 Agent 能不能上线,往往不取决于它顺利时有多聪明,而取决于它超时、截断、重复、取消时,有没有一个体面的收场。
对话结束之后,Agent 才开始变得有价值
有段时间,我们判断一张看板是否完成,标准是“生成成功并保存”。后来越用越觉得不对。
业务真正需要的,通常不是今天看一次数据,而是以后别忘了继续看。
现在,Omega 可以在任意图卡上设置监控规则。比如“转化率低于 5%,并且订单量高于 1000”才告警,也可以跨多张卡片组合条件。后台按计划重新执行查询,满足条件后通过企业微信或邮件通知。
看板也可以按周或按月定时推送。到时间后,系统会绕过旧缓存,用最新数据重新截图。某一部分查询失败,不会让整次推送直接消失:可以展示的内容仍会尽量发送,失败详情则保留在推送记录中。对数据产品来说,有记录地降级,通常比悄悄什么都不给更有用。
每次编辑都会留下版本,用户可以看 diff、回到之前的结果。复制或发布时,版本历史会跟随产物;权限不会被原样照搬到另一个空间,而是在目标空间重新授权。访问记录、团队协作和企业微信机器人,则让这份分析逐渐离开个人工作台,进入真实的协作环境。
Skills 和 MCP 也在把这些能力接到 Omega 之外。用户最终不关心协议名,他关心的是:能不能在企业微信里问同一个问题,能不能让另一个 Agent 调用这份分析,能不能在数据异常时主动收到消息,而不是每次都重新打开一个聊天框。
写在最后
我们现在仍然不敢说 Omega 已经是“下一代 BI”的答案。真实数据比演示数据脏得多,企业里的权限和流程也比一句 prompt 复杂得多,很多体验仍在快速变化。
在 AI 时代,会生成页面会越来越便宜。真正稀缺的,是生成之后,那张页面第二天还能不能用;那次分析下一周还能不能继续;那个 Agent 出错的时候,仍然值不值得信任。
目前我们已经发布了内测版本「马尔摩斯/marmos」(marmos.qq.com),欢迎联系我们。
跳转微信打开
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み