0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CAPアプリをAIエージェント化して、BTPからローカルSAPのアドオン発注をチャットで承認するまで【概要編】

0
Posted at

CAPで作った簡易的な発注アプリを @cap-js/agents でAIエージェント化し、データの保存先をローカルのABAP環境のZテーブルにして、SAP BTP上で動かしました。Fiori Elementsの画面から発注を登録し、チャットで発注を検索して承認するところまで試しています。

この記事は概要編です。全体の構成、設計で決めたこと、はまった点の一覧、動かした結果と制約をまとめます。個々の対処の詳細は、別の記事に分けて書く予定です。

個人の検証環境で試した記録です(2026年9月末時点)。認証を付けていないなど、本番にそのまま持ち込める構成ではありません。制約は「制約と注意」にまとめています。

作ったもの

検証用に作った、簡易的な発注アプリです。

ここでいう「発注」は、SAP標準の購買発注(ME21NやEKKO/EKPO)ではありません。検証用にアドオンで作った簡易機能で、Zテーブル2つ(ヘッダと明細)に保存します。仕入先は検証用のデータをCAP側に持ち、品目は自由入力です。SAPのマスタや、入庫・請求といった後続の処理とは連動していません。

構成要素は3つです。

  • 画面:Fiori Elements(List Report / Object Page)で発注を登録・承認・削除する
  • エージェント:チャットで発注を検索・集計し、承認待ちの発注を承認・却下する
  • データ:発注ヘッダ・明細は、ローカルのABAP環境のZテーブルに保存する。BTP上のCAPにはコピーを持たず、自作したRAPのOData V4をその場で読み書きする

業務ルールは、入力チェック(数量・単価・発注日・品目の重複)と、金額による承認の判定です。合計金額が10万円を超える発注は「承認待ち」になり、それ以下は自動で「承認済み」になります。

動かしたシナリオ

担当者が画面で発注を登録し、承認者がチャットで内容を確かめて承認する、という流れです。認証を付けていないので、担当者と承認者の区別は想定上のものです。

# 誰が どこで 何をするか データの動き
1 担当者 画面 「作成」を押し、発注日・仕入先・明細(品目・数量・単位・単価)を入力する 下書きとしてCAP側に保存される。SAPにはまだ書かれない
2 担当者 画面 「保存」を押す CAPが入力チェックと承認の判定をして、SAPのZテーブルに登録する。合計が10万円を超えていれば「承認待ち」になる
3 承認者 チャット 「何件?」「内訳は?」「承認待ちの詳細は?」と聞く エージェントがSAPのZテーブルを検索して答える
4 承認者 チャット 「承認して」と頼む エージェントが承認のToolを呼び、Zテーブルの承認ステータスと承認者が更新される
5 承認者 SAP側 Zテーブルの該当行を見る 「承認済み」になり、承認者が入っている

4の承認には、実行前に人の確認を挟む指定を付けています。ただし、今回は機能せず、確認なしでそのまま承認されました(「制約と注意」を参照)。

承認・却下は画面からもできます。承認待ちの発注を選んで「承認」か「却下」を押すと、承認者の入力画面が開くので、名前を入れて実行します。

画面で見ると、次のようになります。

シナリオ1の登録画面です。発注番号・発注日・通貨は自動で入ります。

登録画面

シナリオ2のあとの一覧です。合計金額と承認ステータスが並びます。ちょうど10万円の発注は自動で承認済みになり、10万円を超えた3件は承認待ちになりました。そのうち2件は、画面から承認・却下したあとの状態です。

一覧画面

シナリオ3のチャットです。件数、内訳、承認待ちの詳細を順に聞いています。

チャットで質問

なぜ試したか

CAPを学んだ機会に、2026年8月に公開された @cap-js/agents を使ってみたくなったのがきっかけです。確かめたかったのは次の2点です。

  • 保存先をSAP側(ABAP)に置いたまま、CAPのアプリをエージェント化できるか
  • SAP AI Coreを使わずにエージェントを動かせるか

標準のODataをエージェントから使うことについてはSAP API Policyの確認が必要だと考え、今回は自作(Z)のサービスだけを対象にしました。ポリシーの解釈はSAP社に確認していません。

環境

項目 内容
CAP Node.js 24、@sap/cds 10.0.3
エージェント @cap-js/agents 0.9.7(1.0未満)、A2Aで公開
LLM OpenAI gpt-4o-mini(@langchain/openai 1.6.0)
画面 Fiori Elements、OData V4、UI5 1.152(CDNから読み込み)
SAP側 ABAP Cloud Developer Trial 2025(Docker)。RAPはmanaged・Draftなし
BTP トライアル、Cloud Foundry。approuter、Connectivity、Destination、Cloud Connector。XSUAAは未使用
開発 VS Code+Claude Code拡張機能、Windows 11

全体構成

全体構成図

図の番号に沿って説明します。

  1. 編集中:Fiori Elementsで入力中の下書き(Draft)は、CAP側のSQLiteだけに保存します。SAPには書きません。
  2. 保存時:CAPからCloud Connectorを通ってRAPのOData V4へ、ヘッダと明細を1回のDeep Insertで登録します。確定した発注を持つのはZテーブルだけです。
  3. チャット:エージェントがOpenAIのモデルで推論し、呼び出すToolを決めます。このとき、Toolの結果(発注データ)がBTPの外に送られます。
  4. Tool実行:検索と、承認・却下です。エージェントはCAPのサービスを通るので、SAPへの経路は画面と同じです。

BTPからローカルへの接続は、DestinationとConnectivity、Cloud Connectorを使いました。Cloud Connectorはローカル側で動かし、そこからBTPへトンネルを張ります。Claude Codeは開発時だけ使っていて、実行時の通信経路には入りません。

設計で決めたこと

ここでは考え方だけを書きます。コードは詳細編に回します。

下書きはCAP、確定データはSAP

下書きはCAP、確定データはSAP

当初はDraftを使わず、保存時にそのままSAPへ登録するつもりでした。しかし、UI5 1.152とこの構成では、Draft(かスティッキーセッション)が無いと「作成」ボタンが出ず、明細の行追加もできませんでした。

そこで、役割を2つに分けました。

  • 編集中:入力途中の下書きは、CAP側のSQLiteだけに置きます。SAPには何も書きません。
  • 保存時:CAPが入力チェックと承認の判定をしてから、ヘッダと明細をまとめて1回でSAPに登録します。

CAPのサービスは、cds import で取り込んだRAPのエンティティをそのまま公開し、そこにDraftを有効にしています。SAP側のRAPはDraftなしのままです。

「Draftが無いと作成できない」のは、この構成で確認した挙動です。Fiori Elementsの一般的な仕様なのか、外部モデルから引き継いだ注釈の影響なのかは、切り分けられていません。RAP側にDraftを持たせる構成は試していません。

RAPは薄く、業務ルールはCAPに置く

CAPとRAPの役割分担

RAPはmanaged・Draftなしで、ABAPの実装クラスもありません。登録・更新・削除を公開し、作成者と日時を記録するだけです。

入力チェック、承認の判定、キー(UUID)と発注番号の採番は、CAPのサービス層に置きました。もともとCAPにあったルールをそのまま残した形で、画面とエージェントのどちらから操作しても同じルールが効きます。

承認専用のアクションもRAPには作っていません。承認・却下はCAPで受け、RAPへは発注ヘッダの更新として送っています。

エージェントはCAPのサービスをそのまま使う

画面とエージェントの承認経路

@cap-js/agents では、サービスに @agent の指定を付けると、エンティティの検索と、サービス直下のアクションがエージェントのToolになります。エージェント専用の仕組みを別に作る必要はありませんでした。

気を付けた点は3つです。

  • 画面用のアクションはToolにならない:特定の発注に紐付くアクション(バウンドアクション)は対象外でした。そのため、発注番号で呼ぶアクションを別に用意し、中では画面と同じ承認処理を呼んでいます。
  • 承認・却下には人の確認を挟む:実行前に人の確認を待つ指定(@agent.hitl)を付けました。ただし、今回は機能しませんでした(「制約と注意」を参照)。
  • ODataの公開を明示する:エージェントの指定だけだとODataの公開が外れ、画面が404になりました。

LLMについては、プラグインが標準で使えるのはSAP AI Core、Anthropic、モックの3種類でした。OpenAIを使うため、モデルを用意する処理(buildModel)を差し替えています。差し替えは10行ほどで済み、SAP AI Coreなしで動かせました。

はまったところ(一覧)

問題はどれも、層と層の境目で起きました。エラーを見たら、まずどの層の話かを切り分けると調べやすくなります。

層 症状 対処
SAP(OData V4の設定) 登録が500 Virus scan profile /IWBEP/V4/ODATA_UPLOAD is not active /IWBEP/V_V4_SETT で VIRUS_SCANER_IGNORED = X
SAP(DDIC) 承認ステータスが大文字で保存される ドメインを小文字可のもの(CHAR20 → TEXT20)に変更
CAP(外部モデル) Draftが作られない、下書きの読み書きが501 引き継がれた @cds.external と @cds.persistence.skip を打ち消す
CAP(注釈) 明細の追加が405、「作成」でIDの入力を求められる 引き継がれた InsertRestrictions を外し、IDを @Core.Computed にする
CAP → SAP 一覧の取得が400 Draftの一覧でCAPが作る not in の条件を、= と or に書き換える
CAP → SAP 登録が400 Error while parsing an XML stream 下書きから文字列で来るDecimalを、数値にして送る
BTP approuterが500 XSUAAを使っていないので authenticationMethod: none にする
BTP(エージェント) content safety check is temporarily unavailable、チャット画面が404 LLMをOpenAIに差し替え、server.index: true にする

ウイルススキャンを無視する設定は、スキャナの無いローカルの検証環境だから行ったものです。本番では、スキャンのプロファイルを正しく設定すべきです。

動かした結果

2026年9月末の時点で、次のことを確認しました。

  • ローカル:テスト24件が成功。テストは実際にSAP側へ登録し、最後に削除します。画面から明細2行の発注を登録し、Zテーブルに入ったこと、承認・却下でSAP側が更新されることを確認しました。
  • BTP:approuter経由で、登録 → 読み取り → 削除が動きました。エージェントは、仕入先の一覧と発注の件数を、SAP側の実データどおりに答えました。
  • チャットでの承認:シナリオの3〜5を通しで実行し、Zテーブルの該当行が承認済みになり、承認者が更新されたことを確認しました。

シナリオ4で、チャットから承認を頼んだところです。

チャットで承認

シナリオ5で、承認後にSAP側のデータを見たところです。test005 の行が「Approved」になり、承認者には「DEVELOPER」が入っています。画面から承認・却下した2件の承認者は伏せています。

承認後のSAP側のデータ

制約と注意

検証用として割り切った点です。同じ構成を試す場合は、特に上の3つに注意してください。

  • 認証がありません。 XSUAAを使っておらず、URLを知っていれば誰でも操作できます。試すときだけ起動し、終わったら止めています。
  • 承認者は自己申告です。 画面から承認するときは、開いた入力画面に承認者の名前を入れます。ログインしたユーザーではありません。チャットから承認したときは、承認者を入力する機会がなく、「DEVELOPER」が入りました。SAPへは固定ユーザーでつないでいるため、SAP側の更新者も常に同じです。
  • 人による確認(HITL)が機能していません。 @agent.hitl は指定していますが、チャットで承認を頼むと、確認なしでそのまま実行されました。上の画面コピーのとおりです。原因の調査と対応は今後の課題です。
  • 発注データが外部のLLMに送られます。 OpenAIのAPIを直接呼んでいます。
  • 2つのシステムをまたぐ不整合が起こり得ます。 SAPへの登録が成功したあとにCAP側で失敗すると、発注と下書きの両方が残ります。
  • 登録後の編集はできません。 直すときは、削除して登録し直します。同時に承認した場合の排他も考慮していません。
  • 下書きはメモリ上のSQLiteにあります。 CAPを再起動すると、保存していない下書きは消えます。

Claude Codeとの分担

実装の大部分はClaude Code(VS Codeの拡張機能)に任せました。

担当 内容
Claude Code テーブル定義の案、RAP一式(ADTのREST APIで作成・有効化・公開)、CAPのコード、テスト、ヘッドレスChromeでの画面確認、BTPへのビルドとデプロイ、ドキュメント
自分 方針の判断(Draftの方式、データの置き場所)、SE11でのテーブル作成、ウイルススキャンの設定、cf login とAPIキーの入力、デプロイの承認

cf deploy はhookで止め、自分が承認したときだけ実行される仕組みにしています。成果の確認は、別のセッションのClaudeにドキュメントと突き合わせさせ、出てきた指摘に自分が判断を返す形で行いました。

保存先をSAP側に移す作業は、最初の指示から完了の報告まで約1時間40分でした。ウイルススキャンの設定作業を含みます。

今後試したいこと

  • XSUAAによるログインと権限、ログインユーザーをSAPへ引き継ぐ接続
  • 人による確認(HITL)が機能しない原因の調査と対応
  • チャットからの承認で、承認者を指定できるようにする
  • プラグイン付属のチャット画面以外のUI。付属の画面ではファイルのアップロードができませんでした
  • 他のエージェントからA2Aで呼び出す
  • RAP側にDraftを持たせる構成、登録後の編集
  • 2つのシステムをまたぐ不整合への対策

まとめ

  • 下書きはCAP、確定データはSAPに分けることで、Fiori ElementsのDraftと外部のODataを両立できました。
  • @cap-js/agents は、buildModel を差し替えればSAP AI CoreなしでもBTP上で動きました。
  • はまった点の多くは、cds import したモデルの注釈と、CAPからSAPへ送るクエリの差でした。
  • 認証と承認者の扱いは未対応で、人による確認も機能していません。「AIが承認する」構成では、ここが本番化の前提になります。

詳細編では、DraftとODataの対処、エージェントの設定、Claude Codeでの進め方を順に書く予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?