こんにちは!バクラク債務管理のソフトウェアエンジニアの @shoyan42 です。2025年4月に新卒として入社しました。
この記事は LayerX AI Agent ブログリレー の 24 日目 の記事です。
今回は、Snowflake の AI Agent を作成できる Snowflake Cortex Agents(以後、Cortex Agents)というサービスを用いて、自社サービスに対してのお客様からのお問い合わせの一次調査を行う AI Agent の構築を試してみた際の知見の共有を行います。この記事では、Cortex Agents の具体的な構築手順は紹介しません。公式のチュートリアルを参考にしてください。
1. 課題
私が所属している債務管理開発チームでは、バクラクシリーズの中で最初にリリースされたサービスということもあり、他プロダクトと比べてお客様からへのお問い合わせが多いです。
そのため、お問い合わせ回答のための調査に工数がかかり、本来行いたいお客様に WOW を届けられるような機能開発に集中することができていないという課題がありました。
そこで、お問い合わせ回答のための調査や仕様回答を手助けするような AI Agent を構築し、工数削減をしようと考えました。
2. Snowflake Cortex AI について
Snowflake Cortex AI は、Snowflake が提供するフルマネージドな AI サービス群です。
ユーザーは、インフラストラクチャの管理コストがかからず、セキュリティ面でも安全にデータが格納されている Snowflake で直接 AI を活用した分析やアプリケーション開発を迅速に行うことができます。
弊社では、1 日ごとに本番環境の DB の snapshot を作成していたり、Datadog のアーカイブされたログを Snowflake 上に保存しています。
そのため、Snowflake を用いたお問合せ調査を行うことが多く、Snowflake 上で構築が完結する Snowflake Cortex AI が お問合せ調査 AI Agent を構築する技術として適していると考えました。
本章では、お問い合わせ調査 AI Agent の中核を担う 3 つの主要なサービス、Cortex Agents、Cortex Analyst、Cortex Search についてご紹介します。
2.1 Cortex Analyst について
Snowflake Cortex Analyst(以後、Cortex Analyst)は、ユーザーの入力から目的の構造化データを取得するための SQL を構築します。
そのため、ユーザーは SQL の知識がなくとも構造化データを参照することができます。
Cortex Analyst では、テーブルやカラムの定義、ビジネス上の意味 をあらかじめ Semantic Model として yaml 形式で定義する必要があります。
テーブル同士の relation
も relationships
して定義することができます。
私の体感値だと、 relationships
を適切に設定しておかないと、Cortex Analyst はテーブル同士の結合を行なってくれません。
yaml ファイルはビジネスドメインやトピックに応じて分割し、使用するカラムもなるべく増やしすぎないほうが良いとのことででいた。
最初はシンプルに構築して、徐々にテーブルやカラムを追加していくのが良さそうです。
以下は Cortex Analyst のチュートリアルの Semantic Model の例です。
具体的な構築方法は、以下のチュートリアルを参考にしてください。
2.2 Cortex Search について
Snowflake Cortex Search(以後、Cortex Search)は、PDF、テキストファイル といった非構造化データの中から、必要な情報を瞬時に探し出すことに特化した、検索サービスです。
Cortex Search では、文章や単語がもつ 意味の近さ をベクトルとして表すベクトル検索と、従来のキーワード検索を組み合わせることで、精度の高い検索結果を回答します。
実際にバクラク債務管理のサポートページの Markdown が入っている DB で、freee API 連携 と検索すると、freee 会計の API 連携に関するサポートページが推薦されました。
具体的な構築方法は、以下のチュートリアルを参考にしてください。
2.3 Cortex Agents について
Cortex Agents は、構造化データに強い Cortex Analyst と非構造化データに強い Cortex Search の両方をテータ分析のツールとして操作し、自律的な意思決定を行います。
ユーザーからの自然言語による複雑な質問やタスクを受け取ると、その意図を理解し、達成までの最適な計画を立てます。
そのプロセスは以下のようになります。
- 計画 : ユーザーからの質問を解釈し、具体的なサブタスクに分割する。そして次のステップでどのツールをを使用するかを計画する。
- ツールの使用: 構造化データに強い Cortex Analyst と非構造化データに強い Cortex Search 等を実行をし、データの取得を行う。
- 振り返り: 2 で得られたデータを評価し、次のステップ (ユーザーに説明を追加情報を求める、ステップ 1 に戻る、最終的な回答を生成する) を決定する。
このように、Cortex Agents は単一のツールではなく、複数のツールを操作することで、人間のように多角的で複雑な調査を自律的に実行できる強力な AI エージェントです。
以下は、Cortex Agents を用いて、end-to-end な Agentic Application を構築する際の Framework を表した図です。
具体的な構築方法は、以下のチュートリアルを参考にしてください。
3. 実験
今回の目的としては、お客様からのお問い合わせの情報から、根本原因と解決方法、もしくはエンジニアが引き継いで調査するための一次調査結果を出すことです。
データセットには、構造化データとして 本番データの snapshot と Datadog のアーカイブされたログ を扱い、非構造化データとして バクラクのサポートページを markdown にして Snowflake の DB に挿入したものを用意しました。
AI Agent への入力と作成された SQL、AI Agent の回答を掲載していますが、一部編集しています。
3.1 構造化データのみで回答出来るお問合せ
バクラクで作成した仕訳を会計ソフトに API で連携する機能がありますが、
お客様によると、No.XX の請求書の API 仕訳連携に失敗するとのことでした。
このお問い合わせを解決するためには、以下の 2 ステップが必要となります。
- 請求書情報が入ったテーブルで、テナント ID と請求書の連番で検索をかけて、全テナントでユニークな請求書 ID を取得する。
- 仕訳連携の実行履歴が保存されているテーブルの 連携対象の請求書 ID が配列で保存するカラム に 1 で取得した請求書 ID をキーとして検索して、履歴を取得する。
請求書情報テーブルの ID と実行履歴テーブルの連携対象の請求書 ID とでは、relationships
を作成することができなかったため、別の Semantic Model として定義されており、2 つの Cortex Analyst のツールを呼ぶ必要があります。
実際にテナント ID と実行時間、お問合せ内容をもとに AI Agent に調査を依頼すると、期待通りに 2 つの Cortex Analyst のツールを呼び出して回答することができました。
回答には 2 分かかりました。
ユーザーの入力 (折りたたまれています)
tenant_id: XXX
起票時間: 20XX 年 Y 月 ZZ 日 XX:YY
お問合せ内容: 〇〇会計ソフトと費用計上仕訳を連携しようとするとエラーが出ました。
このお問い合わせの原因と対処法を調査してください。
作成されたSQL (折りたたまれています)
tenant_id と 請求書番号から 請求書 ID を取得するクエリ
WITH __incoming_invoices AS ( SELECT id, tenant_id, archived_at, created_at, deleted_at, updated_at, tenant_incoming_invoice_num, ... FROM incoming_invoices ) SELECT id, tenant_id, archived_at, created_at, deleted_at, updated_at, tenant_incoming_invoice_num, ... MIN(created_at) OVER () AS start_date, MAX(created_at) OVER () AS end_date FROM __incoming_invoices WHERE tenant_id = XXX AND tenant_incoming_invoice_num = YYY ORDER BY created_at DESC NULLS LAST -- Generated by Cortex Analyst ;
tenant_id と 請求書 ID から該当の実行履歴を取得するクエリ
WITH __XXX_api_jobs AS ( SELECT ..., error_log, incoming_invoice_ids, ..., failed_at, modified_at FROM XXX_api_jobs ) SELECT ..., error_log, incoming_invoice_ids, ..., failed_at, modified_at MIN(modified_at) OVER () AS min_modified_at, MAX(modified_at) OVER () AS max_modified_at, COUNT(DISTINCT tenant_id) OVER () AS distinct_tenant_count, COUNT(DISTINCT type) OVER () AS distinct_type_count, MIN(failed_at) OVER () AS min_failed_at, MAX(failed_at) OVER () AS max_failed_at, COUNT(*) OVER () AS total_records FROM __XXX_api_jobs WHERE tenant_id = XXX AND type = YYY AND modified_at >= '20XX-YY-ZZ 00:00:00' AND modified_at '20XX-YY-ZZ 23:59:59' ORDER BY modified_at DESC NULLS LAST -- Generated by Cortex Analyst ;
AI Agentの回答 (折りたたまれています)
エラーの詳細
テナント ID XXX の 20XX 年 Y 月 ZZ 日の API 連携ログを確認したところ、以下のエラーが複数回発生していることが判明しました:
エラーメッセージ: 「会計ソフトの連携情報の取得に失敗しました」
発生時刻:
- 20XX 年 Y 月 ZZ 日 XX:YY
原因
このエラーは、〇〇会計ソフトとの接続認証に問題があることを示しています。【1】〇〇会計ソフトでは、セキュリティ保持のため 1 ヶ月〜2 ヶ月で接続が自動で切れる設定となっており、これが最も可能性の高い原因です。
解決方法
以下の手順で問題を解決してください:
〇〇会計ソフトの事業所との再連携を行ってください。接続が自動で切れているため、新しい認証情報での接続設定が必要です。
今後の予防策
〇〇会計ソフトの接続は定期的に切れるため、1〜2 ヶ月に一度は再連携が必要になります。エラーが発生した際は、まず接続状況をご確認ください。
もし上記の手順で解決しない場合は、具体的なエラーメッセージとともに再度お問い合わせください。
3.2 非構造化データのみで回答出来るお問合せ
バクラクでは定期的に発生する支払をスケジューリングすることができ、オプションで 支払予定日が土日祝の場合に、そのままにするか、避けるかを選択することができます。
お問い合わせでは、オプションで、そのままにする を設定しているにもかかわらず、土日を避けて定期支払が反映されるというお問合せでした。
原因としては、別の設定項目の支払期日ルールが優先されており、土日祝をはずす 設定がされていたことでした。
以下が AI Agent の回答です。正しく原因と対処法を案内できており、参照したサポートページのリンクも表示することができました。
回答には 2 分かかりました。
ユーザーの入力 (折りたたまれています)
定期支払設定の支払期日に、支払予定日が土日祝の場合は「そのままにする」と設定しても、土日を避けて反映されてしまうというお問い合わせがありました。
この原因と対処法について教えてください
AI Agentの回答 (折りたたまれています)
定期支払設定の支払期日で「そのままにする」設定にも関わらず土日を避けて反映されてしまう問題について、以下の原因と対処法をご説明いたします。
原因
この問題は、バクラク請求書の定期支払設定において、2023 年 4 月のアップデート以降に祝日対応が強化されたことに関連している可能性があります。【4】
システムでは以下の 3 つの選択肢が用意されています:
- そのままにする
- 直前の平日に設定する
- 直後の平日に設定する
しかし、実際の銀行営業日との整合性を保つため、システム側で自動調整が行われている可能性があります。
対処法
1: 設定の再確認
[設定] > [定期支払設定] から該当の設定を確認
「支払予定日が土日祝の場合」の設定が「そのままにする」になっているかを確認
2: 個別の請求書での修正
定期支払設定で登録された請求書の支払期日が意図した日付と異なる場合は、以下の手順で個別に修正できます:【1】
[請求書] > [未処理] から該当の請求書を選択請求書詳細画面で支払期日を手動で編集
土日祝日の日付に変更して保存
3: 支払期日ルール設定の確認
[設定] > [会計処理設定] > [支払期日ルール設定] も併せて確認し、こちらでも「そのままにする」設定になっているかを確認してください。【2】参照したサポートページ
この問題が継続する場合は、システムの仕様上の制約である可能性もありますので、バクラクサポートまでお問い合わせいただくことをお勧めします。
3.3 構造化データと非構造化データを組み合わせないと回答できないお問合せ
3.1 のお問い合わせの発展として、履歴を取得し、そこに書かれているエラーメッセージをもとに、サポートページにある会計ソフトごとのトラブルシューティングを参照して欲しいお問い合わせで検証します。
1 回目に実行した際は、実行履歴の特定まではできましたが、エラーメッセージをもとにトラブルシューティングを参照するということができず、実行履歴を元に原因を推察して回答することしかできませんでした。
そこで、Cortex Search の Description に、サポートページには会計ソフトごとのトラブルシューティングが存在することを追記しました。すると、2 回目では、実際に該当のトラブルシューティングを参照し、根本原因と解決策まで提示することができました。
回答には3分かかりました。
ユーザーの入力 (折りたたまれています)
tenant_id: XXX
起票日: 20XX 年 Y 月 ZZ 日
請求番号 No.XXX を費用計上 API 連携した際にエラーが発生したとのことでした。
この原因を調査してください。
作成されたSQL (折りたたまれています)
tenant_id と 請求書番号から 請求書 ID を取得するクエリ
WITH __incoming_invoices AS ( SELECT id, tenant_id, archived_at, created_at, deleted_at, updated_at, tenant_incoming_invoice_num, ... FROM incoming_invoices ) SELECT id, tenant_id, archived_at, created_at, deleted_at, updated_at, tenant_incoming_invoice_num, ... MIN(created_at) OVER () AS start_date, MAX(created_at) OVER () AS end_date FROM __incoming_invoices WHERE tenant_id = XXX AND tenant_incoming_invoice_num = YYY ORDER BY created_at DESC NULLS LAST -- Generated by Cortex Analyst ;
tenant_id と 請求書 ID から該当の実行履歴を取得するクエリ
WITH __XXX_api_jobs AS ( SELECT ..., error_log, incoming_invoice_ids, ..., failed_at, modified_at FROM XXX_api_jobs ) SELECT ..., error_log, incoming_invoice_ids, ..., failed_at, modified_at MIN(modified_at) OVER () AS min_modified_at, MAX(modified_at) OVER () AS max_modified_at, COUNT(DISTINCT tenant_id) OVER () AS distinct_tenant_count, COUNT(DISTINCT type) OVER () AS distinct_type_count, MIN(failed_at) OVER () AS min_failed_at, MAX(failed_at) OVER () AS max_failed_at, COUNT(*) OVER () AS total_records FROM __XXX_api_jobs WHERE tenant_id = XXX AND type = YYY AND modified_at >= '20XX-YY-ZZ 00:00:00' AND modified_at '20XX-YY-ZZ 23:59:59' ORDER BY modified_at DESC NULLS LAST -- Generated by Cortex Analyst ;
AI Agentの回答 (折りたたまれています)
調査結果:請求書No.445のAPI連携エラー原因
エラーの特定
テナント ID XXX の請求書 No.XXX において、20XX 年 Y 月 ZZ 日から Y 月 ZZ 日にかけて、〇〇会計ソフトとの費用計上 API 連携で継続的にエラーが発生していることを確認しました。
具体的なエラー内容
・請求書 No.XXX: 〇〇会計ソフトでエラーが発生しました:
このエラーは、0 円の明細行が含まれていることが原因です【8】。〇〇会計ソフトの API 仕様では、金額が 0 円の明細行があると「
エラー発生パターンの分析
- 発生期間: 20XX 年 Y 月 ZZ 日 ~ 20XX 年 Y 月 ZZ 日
- 発生回数: N 回以上の連携試行で同一エラーが継続発生
- 対象請求書: ID XXX (作成日: 20XX 年 Y 月 ZZ 日)
- エラーの一貫性: 全ての試行で同じエラーメッセージが発生、データ起因の問題であることを示唆
解決方法
サポートドキュメントに基づく推奨対処法【8】:
該当明細の以下項目を削除:
- 勘定科目
- 金額
- 税区分
- 部門、補助科目、プロジェクト
削除後、再度 API 連携を実行
検証結果
他の請求書では異なるエラーパターン(マスタ同期エラー等)が発生しており、これは請求書 No.XXX 固有の問題
システム全体の障害ではなく、特定の請求書データ構造に起因する問題
報告された日時(20XX 年 Y 月 ZZ 日)とエラー発生タイムラインが一致
今後の予防策
- 仕訳作成時に 0 円明細行の作成を避ける
- API 連携前に明細内容の事前チェックを実施
- 類似エラーの早期発見のため、API 連携履歴の定期確認を推奨
この調査により、請求書 No.XXX の API 連携エラーは 0 円明細行が原因であることが確定しました。上記の解決方法を実施することで問題は解決されるはずです。
3.4 課題
Cortex Analyst を活用するには、2.1 で説明したように、事前に Semantic Model を定義する必要があります。
このモデルはデータベースのスキーマをそのまま利用できるわけではなく、AI に不要な情報を与えず精度を担保するために、人間が利用するテーブルやカラムを慎重に取捨選択し、手動で構築しなければなりません。
また、お問い合わせの種類に応じて個別の Semantic Model や AI Agent を準備する必要があるため、すべてのお問い合わせに網羅的に対応するモデルを構築するのは現実的に困難です。
さらに、一度構築したモデルも万能ではなく、運用を通じて継続的な改善が不可欠です。 例えば、新たな問い合わせに対応するためのカラムの追加や、システムプロンプトの改善といった作業が都度発生します。結果として、お問い合わせ対応の工数を削減するという目的のために、モデルの構築と改善という新たな運用コストが発生している状況です。
3.4.2 継続的にメンテナンスするための仕組みが必要
非構造化データとしてサポートページを使用していますが、現状ではサポートページの Markdown を Snowflake にアップロードしています。
しかし、新機能のサポートページが追加されたり、機能改善によって既存のサポートページを修正した場合に、現状だと変更を追従することができません。
そこで、月 1 回のペースでサポートページの更新状況を、Snowflake に同期する仕組みが必要だと考えました。もしくは、サポートページを参照するための MCP を作成し、外部ツールとして Agent に実行させるのも良いと思います。
4. 展望
4.1 Routing Workflow を用いた AI Agent の構築
単体の Cortex Agents で、多様な種類のお問い合わせを調査するための汎用的な AI Agent を構築することも可能ですが、実行できるツールが多いと AI がどのツールを使えば良いか判断ができず、最終的な回答の精度が劣化するのではないかと考えています。
そこで、Anthropic が提唱しているワークフローのデザインパターンである、Routeing Pattern を用いた Multi Agent の構築を行いたいと考えています。
具体的には、特定の種類のお問い合わせに特化した Specialist Agent と、お問い合わせの種類に応じてどの Specialist Agent を呼び出すかを判断する Router Agent を構築します。
これにより、Specialist Agent はタスクと関連性の高いツールのみを使うことができるため回答の精度が高句なることが期待されます。
4.2 ユーザーのレスポンスによる非構造化データの追加
基本的な機能の仕様に関してはサポートページに記載されているのですが、機能の詳細な仕様までは書かれていないことがあります。
また、開発チームの流動性により機能の開発者がチームにおらず、コードを参照して初めてわかる知見などがあります。
同様の事象が発見した際には、Agent が同様の件だと判断して過去の知見を回答して欲しいです。
そこで、判明した知見をユーザーから Agent へフィードバックし、非構造化データとして追加し、Cortex Search で検索可能にしておくことでそれが実現できると考えています。
5. まとめ
本記事では、Snowflake の Cortex Agents を活用し、お客様からのお問い合わせの一次調査を自動化する AI Agent の構築に挑戦しました。
実験を通して、構造化データ(本番 DB のスナップショットやログ)と非構造化データ(サポートページ)を横断的に扱うことで、複雑な問い合わせに対しても、原因の特定から解決策の提示まで行えるポテンシャルを確認できました。特に、ツールの説明(Description)を適切に設定することで、AI Agent がより的確な判断を下せるようになるという知見が得られました。
一方で、実用化に向けた課題も明らかになりました。AI の精度を担保するための Semantic Model の構築と継続的な改善には新たな運用コストが発生すること、そしてサポートページのような外部データの鮮度をいかに保つかという仕組み作りが必要不可欠である点です。
今後はこれらの課題を解決しつつ、以下のような展望を描いています。
- Routing Workflow を用いた Multi-Agent 化による回答精度の向上
- ユーザーフィードバックによる継続的な知識の蓄積と自己成長
これらの取り組みを通じて、お問い合わせ対応を抜本的に効率化し、少しでも多く お客様に WOW な体験を届けられるような機能開発 ができる環境を目指していきたいと考えています。
この記事が、Snowflake Cortex AI を用いた AI Agent 構築の一助となれば幸いです!
LayerX AI Agent ブログリレー はまだまだ続きます。
LayerX の Tech アカウントの @LayerX_tech をフォローして次のブログをお待ちください!
カジュアル面談もお待ちしております!
layerx.notion.site