経費精算の監査では、Wordに記載された社内規程と、Excelにまとめられた精算明細を照合し、規定違反や証憑不足、期限超過などを確認する必要があります。明細が増えるほど、こうした確認を手作業で行う負担も大きくなります。
従来は、監査ルールをコードに組み込む方法が一般的でした。しかし、規程が改定されるたびにコードの修正やテストが必要になります。一方、明細と規程をそのままAIに渡すだけでは、正確な数値計算や監査結果の構造化が難しくなります。
本記事では、C#と Spire.Agent.Office を使い、Wordの経費精算規程とExcelの経費明細を照合する監査フローを構築します。AIには規程の内容理解や適合性の判断を、文書エンジンにはファイル操作や数値処理を担当させ、監査プロセスを段階的に自動化します。
監査ソリューションの設計
なぜ「指示を一つ投げるだけ」では済まないのか
Spire.Agent.Office の使い方は直感的です——自然言語の要件を AI 層に渡し、AI 層が文書エンジンを駆動して処理を実行します。しかし経費監査という場面で「この精算申請に不正がないか確認して」と丸ごと投げると、三つの落とし穴にはまります。
- コンテキストの圧迫:60 ページの規程と 300 行の明細を一度にモデルへ渡すと、判定品質は情報量の増加にともなって低下し、周辺的な条項が見落とされやすくなります。
- 数値判定の信頼性:1 泊 18,000 円の宿泊費、基準 12,000 円、超過 6,000 円——これをモデルに暗算させると、数十件から数百件の記録ではほぼ確実に誤計算が発生します。金額は監査結論の着地点であり、モデルの確率的な出力に委ねることはできません。
- 結果の検証不能性:成果物がモデルの出力した文章だけである場合、どの記録を照合し、どの行を漏らしたのかを確認する手段がありません。
三つの設計ルール
以上の問題を踏まえ、本記事のコードは常に次の三つのルールに従います。
ルール 1:まず決定論的に、その後に AI。 特定・読み取り・計算のうち確定できる部分は、モデルに渡さない。
ルール 2:AI は意味判定だけを担い、数値計算は文書エンジンに戻す。 「この記録が基準を超えているか」をモデルが判断し、差額は式が計算する。
ルール 3:一つの指示には一つの目的だけを書く。 段階に分ければ、各段階の成果物を個別に検査でき、失敗時にはどの工程かを特定できる。
段階的なアプローチ
フロー全体は独立した AI 処理ステップに分解され、各ステップの成果物はディスク上に実在する、開いて確認できるファイルになります。
| ステップ | 入力 | 出力 | AI の役割 |
|---|---|---|---|
| ステップ 1 ルールの固定化 | 出張経費精算規程.docx | 経費監査ルール表.xlsx | 規程の条文を読み、構造化されたルールに整理する |
| ステップ 2 逐条照合 | 経費明細.xlsx + ルール表 + 規程原文 | 経費精算コンプライアンス監査結果.xlsx | 1 件ずつ突き合わせ、異常区分を判定し根拠を引用する |
| ステップ 3 報告書生成 | 監査結果.xlsx | 経費精算監査報告書.docx | 集計・要約し、報告できる形にまとめる |
| ステップ 4 通知の一括生成 | 監査結果.xlsx | 照会通知(1 件 1 ファイル) | —(メールマージ。AI 呼び出しなし) |
各ステップは個別に呼び出される設計であり、一つの指示チェーンで最後まで通すわけではありません。こうすることで各段階の成果物を単独で開いて検査できます。ステップ 2 の結果に問題があれば、ステップ 1 を再実行する必要はありません。
サンプルデータの前提
コードをそのまま実行できるよう、本記事では入力ディレクトリの構成を次のように定めます。
D:\expense-audit\
├─ in\
│ ├─ 出張経費精算規程.docx # 規程ドキュメント
│ └─ 経費明細_2025年度.xlsx # 経理システムから出力した明細
├─ out\ # 成果物の出力先
└─ sessions\ # AI セッションの作業ディレクトリ(中間過程を保存し、監査可能にする)
『出張経費精算規程.docx』の主要な条項の例:
経費明細表の列構成:
なお、出張先(宿泊基準の適用区分)のように独立した列を持たない判定材料は、「内容」欄の記述から AI が読み取ります。列を増やしたくなければ、判定材料を自由記述欄に残しておくだけでも成立します。
環境準備
.NET 10 のコンソールプロジェクトを作成します。
dotnet new console -n ExpenseAudit -f net10.0
cd ExpenseAudit
NuGet から Spire.Agent.Office をインストールします。関連する文書エンジンの依存関係は自動的に導入されます。
dotnet add package Spire.Agent.Office
公式サイトからアセンブリをダウンロードし、ローカルの DLL として参照することもできます。その場合は Microsoft.Extensions.*、Polly、SkiaSharp などの依存関係に加えて、Spire.Doc / Spire.XLS / Spire.PDF / Spire.Presentation の .NETStandard 版を追加する必要があります。
この製品の全機能を利用するには、有効な SpireToken Key が必要です。Spire.Agent.Office 公式サイトから取得できます。AI 処理のパラメータは一つの AIOptions インスタンスにまとめて設定し、サービス層で一度だけ生成して再利用することを推奨します。
using Spire.Agent.Office.AI;
// AI 処理の共通設定
AIOptions CreateOptions(string spireToken)
{
return new AIOptions
{
SpireToken = spireToken,
TimeoutMs = 300000, // 1 回の呼び出しは完全なエージェントループ。タイムアウトは余裕を持たせる
WorkDir = @"D:\expense-audit\sessions" // セッションディレクトリ:中間過程を保存し、事後監査を可能にする
};
}
説明:
-
SpireToken:AI サービスのキー。未設定または失効している場合は例外をスローして中断します -
TimeoutMs:1 回の指示実行は「計画 → スクリプト生成 → 実行 → 成果物の検証」という完全なループであり、秒から分単位の処理になります。十分なタイムアウトが必要です -
WorkDir:セッションディレクトリ。指定すると、各ステップの入力文書・中間スクリプト・出力成果物がすべて保存され、監査プロセスに追跡可能性が生まれます。これは監査の場面でとりわけ重要です
ステップ 1:規程の条文をルール一覧に固定化する
1.1 設計方針
このステップで解決するのは「規程が変わる」という問題です。基準をコードに書き込むのではなく、毎回規程ドキュメントから読み直させます。規程が改定されればルール表もそれに追随し、コードは一行も変更する必要がありません。
要点は、推論の過程ではなく出力の構造を拘束することです。指示の中で、出力する列を明示し、条項の出典を残すよう要求し、同時に「文書に書かれていない規定を推測して補うこと」を明確に禁止します。三つ目の制約は監査の現場における一線です。AI は規程を整理する手助けはできても、規程に代わって規定を新設することはできません。
1.2 コード
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Doc;
/// <summary>
/// ステップ 1:経費精算規程を構造化された監査ルール一覧に固定化する
/// </summary>
static AIResult ExtractAuditRules(
string policyPath, // 出張経費精算規程.docx
string rulesOutputPath, // 経費監査ルール表.xlsx
AIOptions options)
{
// 規程ドキュメントを読み込む
using (Document policy = new Document())
{
policy.LoadFromFile(policyPath);
// AI 文書プロセッサを取得する
AIDocumentProcessor processor = policy.AI(options);
// 「どうやるか」ではなく「何が欲しいか」を自然言語で記述する
string instruction =
"あなたは企業の内部監査担当者です。この文書を読み、その中に書かれた数量化・判定可能な" +
"経費基準と精算要件をすべて抽出し、監査ルール表として整理してください。列は次のとおりです:" +
"ルール番号、経費区分、判定条件、上限基準、規程条項。要件:" +
"1) 文書に明記された基準のみを抽出し、文書にない規定を推測・補足しないこと;" +
"2) 上限基準は原文の数値と単位を必ず保持すること;" +
"3) 規程条項には条番号または見出しを記入し、人による確認を容易にすること;" +
"4) 数量化できない要求(例:「節約を旨とすること」)はルール表に含めないこと。";
// outputPath の拡張子が出力形式を決める:ここでは .xlsx を指定
AIResult result = processor.ExecuteInstruction(policy, instruction, rulesOutputPath);
if (result == null || !result.Success)
{
string reason = result?.ErrorMessage ?? "不明なエラー";
throw new InvalidOperationException("ルール抽出に失敗しました:" + reason);
}
return result;
}
}
説明:
-
出力の拡張子が形式を決める:
rulesOutputPathに.xlsxを渡せば、AI 層は結果を Excel として書き出します。Word やプレーンテキストにはなりません。つまり同じ規程ドキュメントでも、拡張子を変えるだけで PDF や Markdown の版を直接得られます。 - 指示が列構造を拘束する:5 つの列を明示するのは、ステップ 2 に安定した入力を持たせるためです。構造が自由に流れてしまうと、次の照合は足場を失います。
- 条項の出典を残す:監査の結論は最終的に「どの条項に基づくか」に答えられなければなりません。出典の列は、後続の人による確認の入口になります。
- 推測の禁止:監査の場面における必須要件です。AI が規程を整理するのはよくても、規程に代わって規定を立てることは許されません。
実行後に得られる 経費監査ルール表.xlsx は次のようになります。
運用のヒント: ルール表はバージョン管理に載せることをお勧めします。規程を改定したら再生成し、前版と差分を取れば、その差分が今回の監査における判定基準の変更点になります。それ自体が内部監査に必要な証跡になります。
ステップ 2:明細を 1 件ずつ照合する
2.1 クロスフォーマット入力の扱い
このステップはフロー全体の中核であり、クロスフォーマット能力が最もよく現れる箇所です。主文書は Excel(経費明細)であり、判定の根拠には前のステップで生成した Excel(ルール表)と、原文の Word(規程。条項の原文を確認する必要がある場合に使用)の両方が含まれます。
ExecuteInstruction メソッドの最後の引数は追加ドキュメントを受け取ります。主文書以外のファイルを処理する必要があるとき——たとえば添付の複数の xlsx を分析する場合や、同時に規程ドキュメントを参照する場合——は、それらをこの引数に渡します。
// 主文書(経費明細)+追加ドキュメント(ルール表・規程原文)
result = processor.ExecuteInstruction(
workbook, // 主文書オブジェクト
instruction, // 自然言語の指示
violationPath, // 出力パス
new string[] { rulesPath, policyPath }); // 追加ドキュメント
1 回の呼び出しで、AI は主文書とすべての追加ドキュメントを同時に読み、クロスフォーマットの意味的な突き合わせを行います。
2.2 コード
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Xls;
/// <summary>
/// ステップ 2:経費明細とルール表を 1 件ずつ照合し、監査結果を出力する
/// </summary>
static AIResult AuditExpenses(
string detailPath, // 経費明細_2025年度.xlsx(主文書)
string rulesPath, // 経費監査ルール表.xlsx(追加ドキュメント)
string policyPath, // 規程原文.docx(追加ドキュメント。条項の確認用)
string violationPath, // 経費精算コンプライアンス監査結果.xlsx(出力)
AIOptions options)
{
using (Workbook workbook = new Workbook())
{
workbook.LoadFromFile(detailPath);
AIDocumentProcessor processor = workbook.AI(options);
string instruction =
"あなたは企業の内部監査担当者です。『経費監査ルール表』に厳密に従い、このブックの精算明細を" +
"1 件ずつ照合し、規程に適合しない記録をすべて見つけて、元の明細の右側に次の列を追加してください:" +
"適合判定、異常区分、規程根拠、控除額(円)。要件:" +
"1) 適合判定は「適合」または「異常」のいずれかのみ;" +
"2) 異常区分は次の 5 種類のいずれかに限ること:宿泊費超過、証憑不足、期限超過精算、重複精算、精算対象外;" +
"3) 規程根拠にはルール番号と条項を記入すること(例:「第3条【宿泊基準】(ルール R-001)」);" +
"4) 控除額は規程の基準を超えた部分、または精算できない金額のみを記入し、異常がない場合は 0 とすること;" +
"5) 判定に必要な情報が不足している場合は推測せず、適合判定を「要確認」とし、" +
" 規程根拠に不足している項目を明記すること;" +
"6) 元の申請番号は必ず保持し、明細行へ遡れるようにすること。";
// 追加ドキュメント引数でルール表と規程原文をまとめて AI に参照させる
AIResult result = processor.ExecuteInstruction(
workbook,
instruction,
violationPath,
new string[] { rulesPath, policyPath });
if (result == null || !result.Success)
{
string reason = result?.ErrorMessage ?? "不明なエラー";
throw new InvalidOperationException("明細の照合に失敗しました:" + reason);
}
return result;
}
}
説明:
-
主文書+追加ドキュメントの組み合わせ:明細を主文書として読み込み(
LoadFromFile)、ルール表と規程原文を第 4 引数で渡します。AI はこの三者の間に対応関係を構築します。これはコードの走査だけでは実現が難しい処理です。「この記録は主要都市に該当するか、それともその他の地域か」「この内容は経費区分と整合するか」はいずれも意味の判断だからです。 - ホワイトリスト方式の異常区分:異常区分を 5 つの固定値に限定するのは、後続の集計を可能にするためです。モデルの自由な記述に任せると、同じ種類の問題が「宿泊費超過」「基準超過」「上限オーバー」と複数の表記で現れ、報告書が集計できなくなります。
- 明示的なフォールバック方針:判定根拠が不足する場合は「要確認」を出力させ、無理に結論を出させません。一見すると自動化率を下げますが、実際には不確実性を明示的に露出させ、「成功したように見えて実は誤判定」というリスクを避けることになります。
- 申請番号の保持:監査の結論は元の記録へ遡れなければなりません。これは可用性の最低線です。
2.3 超過額の計算をモデルに任せられない理由
ここに、見落とされがちな細部があります。報告書の「控除額」の列をそのままモデルに出力させると、誤りの確率は明細の行数にともなって急速に上昇します。理由は、モデルが行っているのはトークン単位の確率的生成であって、数値演算ではないからです。
正しい方法は、文書エンジンに計算させることです。指示の中で「控除額は Excel の数式で算出すること」を明確に要求してもよく、実行後にコードで重要な数値を独立に再計算してもかまいません。この部分は後述の「結果の検証」で扱います。
これはモデルの能力を疑う話ではなく、エンジニアリング上の分業です。意味はモデルへ、算術はエンジンへ。数式で表現できる判定は、モデルの出力に現れるべきではありません。
ステップ 3:監査報告書を生成する
監査結果は担当者向けの明細であり、経営層が求めているのは結論です。ステップ 3 では、明細をそのまま会議に出せる報告書に変換します。
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Xls;
/// <summary>
/// ステップ 3:監査結果に基づいて監査報告書を生成する
/// </summary>
static AIResult BuildAuditReport(
string violationPath, // 経費精算コンプライアンス監査結果.xlsx
string reportPath, // 経費精算監査報告書.docx
string period, // 監査期間。例:"2025年度"
AIOptions options)
{
using (Workbook workbook = new Workbook())
{
workbook.LoadFromFile(violationPath);
AIDocumentProcessor processor = workbook.AI(options);
string instruction =
$"あなたは企業の内部監査担当者です。このブックの監査結果に基づいて『経費精算監査報告書』を作成してください。" +
$"監査期間は{period}です。報告書には次の章を含めます:" +
"一、監査概要(抽出件数、検出した異常件数、異常率);" +
"二、異常明細(1 件ごとに申請番号・申請者・金額・異常区分・規程根拠・推奨対応を表で示す);" +
"三、問題の総括(異常区分別に件数と金額を集計し、表で示す);" +
"四、改善勧告(異常区分ごとに実行可能な対応策を示す)。" +
"要件:報告書は経営層向けであり、客観的で中立的な表現とすること;" +
"すべての数値は監査結果そのものから取得し、推計しないこと;" +
"金額は小数第 2 位まで保持すること。";
AIResult result = processor.ExecuteInstruction(workbook, instruction, reportPath);
if (result == null || !result.Success)
{
string reason = result?.ErrorMessage ?? "不明なエラー";
throw new InvalidOperationException("報告書の生成に失敗しました:" + reason);
}
return result;
}
}
説明:
- 章立てを指示に書き込む:報告書は自由記述の文章ではなく、固定構造を持つ成果物です。章を明示すれば、毎期の報告書で口径がそろい、横並びの比較ができます。
- 入力は明細ではなく監査結果:報告書は監査結果だけを読みます。データソースが単一で、すでに人による確認を経ているため、報告書の信頼性は前の 2 ステップの上に築かれます。
- 「すべての数値は監査結果そのものから取得する」:この制約は極めて重要です。モデルの仕事を「要約と構成」に限定し、「計算と推測」から切り離します。
-
出力の拡張子は
.docx:経営層が PDF 版を必要とするなら、reportPathを.pdfに変えて再実行するだけです。他のコードを変更する必要はありません。
ステップ 4:確認依頼通知を一括生成する
監査の結論は、しばしば個人ごとのフィードバックを必要とします。指摘を受けた申請者一人ひとりに、具体的な問題を記した通知を届ける必要があります。この種の「1 つのテンプレート、N 通の内容」という要件は、モデルに 1 通ずつ書かせるよりもメールマージで処理する方が適しています。テンプレートのレイアウトは完全に制御でき、内容の差し込みは決定論的で、追加の AI 呼び出しも消費しません。
using System.Data;
using Spire.Doc;
using Spire.Doc.Documents;
/// <summary>
/// ステップ 4:監査結果に基づいて確認依頼通知を一括生成する(メールマージ)
/// </summary>
static void GenerateClarificationNotices(
string templatePath, // 照会通知テンプレート.docx(差し込みフィールド入り)
DataTable noticeTable, // 1 行 = 1 件の照会対象
string outputDir) // 出力ディレクトリ
{
if (!Directory.Exists(outputDir))
Directory.CreateDirectory(outputDir);
// テンプレート内の差し込みフィールド名。テンプレート側と完全に一致させる必要がある
string[] fieldNames = new string[]
{
"申請者", "部門", "申請番号", "経費区分", "金額", "異常区分", "規程根拠"
};
foreach (DataRow row in noticeTable.Rows)
{
// 行のデータを先にローカル変数へ取り出す。可読性が上がり、出力ファイル名の組み立ても楽になる
string person = row["申請者"].ToString();
string dept = row["部門"].ToString();
string orderNo = row["申請番号"].ToString();
string expenseType = row["経費区分"].ToString();
string amount = row["金額"].ToString();
string violationType = row["異常区分"].ToString();
string basis = row["規程根拠"].ToString();
// 1 人につき 1 通。前の 1 通の差し込み結果が使い回されないよう、毎回テンプレートを読み直す
Document notice = new Document();
notice.LoadFromFile(templatePath);
// フィールドの値と名前は位置で 1 対 1 に対応させる
string[] fieldValues = new string[]
{
person, dept, orderNo, expenseType, amount, violationType, basis
};
// 1 件分のメールマージを実行する
notice.MailMerge.Execute(fieldNames, fieldValues);
string outputPath = Path.Combine(outputDir, $"照会通知_{person}_{orderNo}.docx");
notice.SaveToFile(outputPath, FileFormat.Docx);
notice.Dispose();
}
}
説明:
-
MailMerge.Execute(fieldNames, fieldValues):位置で対応づける単一レコードの差し込み方式で、「1 人 1 通」の通知文書に適しています。 -
毎回テンプレートを読み直す:テンプレートオブジェクトの差し込み結果はインスタンス内に残ります。ループ内で同じ
Documentインスタンスを使い回すと、後続の 1 通が前の 1 通の内容を引き継いでしまいます。そのため、ループ本体の中で毎回新しく生成し、読み直す必要があります。 - フィールド名はテンプレートと一致させる:テンプレート側の差し込みフィールドが実在し、名前が完全に一致していなければ、そのフィールドは差し込まれません。これはメールマージで最も多い失敗要因です。
- テンプレートが領域型(表の領域内で複数行を繰り返す)の場合は、
MailMerge.ExecuteWidthRegion(dataTable)を使い、DataTable.TableNameにテンプレート側の領域名を設定します。
サービス化:再利用可能な監査サービス
4 つのステップを 1 つのサービスクラスに集約し、外部には「監査を 1 回実行する」という 1 つの入口だけを公開します。
using Spire.Agent.Office.AI;
/// <summary>
/// 経費精算コンプライアンス監査サービス:ルール固定化 → 逐条照合 → 報告書生成 → 通知発行
/// </summary>
public class ExpenseAuditService
{
private readonly AIOptions _options;
public ExpenseAuditService(string spireToken)
{
_options = new AIOptions
{
SpireToken = spireToken,
TimeoutMs = 300000,
WorkDir = @"D:\expense-audit\sessions"
};
}
/// <summary>
/// 監査を 1 回実行し、3 つの成果物のパスを返す
/// </summary>
public (string Rules, string Violations, string Report) RunAudit(
string policyPath, string detailPath, string outputDir, string period)
{
if (!Directory.Exists(outputDir))
Directory.CreateDirectory(outputDir);
string rulesPath = Path.Combine(outputDir, "経費監査ルール表.xlsx");
string violationPath = Path.Combine(outputDir, "経費精算コンプライアンス監査結果.xlsx");
string reportPath = Path.Combine(outputDir, $"経費精算監査報告書_{period}.docx");
// ステップ 1:規程 → ルール表
ExtractAuditRules(policyPath, rulesPath, _options);
// ステップ 2:明細 + ルール表 + 規程 → 監査結果
AuditExpenses(detailPath, rulesPath, policyPath, violationPath, _options);
// ステップ 3:監査結果 → 監査報告書
BuildAuditReport(violationPath, reportPath, period, _options);
return (rulesPath, violationPath, reportPath);
}
// 3 ステップの具体的な実装は、上記の ExtractAuditRules / AuditExpenses / BuildAuditReport を
// そのまま本クラス内の private メソッドとして取り込み(static を外す)、続けて
// GenerateClarificationNotices を呼び出せば、通知の発行まで含めた一連の流れになる。
}
呼び出し方:
ExpenseAuditService service = new ExpenseAuditService("SpireToken key");
var (rules, violations, report) = service.RunAudit(
policyPath: @"D:\expense-audit\in\出張経費精算規程.docx",
detailPath: @"D:\expense-audit\in\経費明細_2025年度.xlsx",
outputDir: @"D:\expense-audit\out",
period: "2025年度");
Console.WriteLine($"ルール表:{rules}");
Console.WriteLine($"監査結果:{violations}");
Console.WriteLine($"監査報告書:{report}");
結果の検証:AI に自己申告させない
AIResult.Success が示すのは「このステップが通り、成果物ファイルが存在する」ことだけで、「照合結果が正しい」ことではありません。監査の場面では、フローの最後に独立した検証ロジックを置き、文書エンジンで成果物を読み戻して重要な数値を集計することを勧めます。
using Spire.Xls;
/// <summary>
/// 検証:監査結果を読み戻し、行数と異常区分の分布を集計する
/// </summary>
static void VerifyViolations(string violationPath)
{
using (Workbook workbook = new Workbook())
{
workbook.LoadFromFile(violationPath);
Worksheet sheet = workbook.Worksheets[0];
// 1 行目は見出し。データは 2 行目から始まる
int dataRows = sheet.LastRow - 1;
// 異常区分の列インデックス(1 起算。監査結果の 11 列目)
const int typeColumn = 11;
var distribution = new Dictionary<string, int>();
for (int row = 2; row <= sheet.LastRow; row++)
{
// 数値セルは Text では空文字が返るため、Value を優先して読む
string type = sheet.Range[row, typeColumn].Value;
if (string.IsNullOrEmpty(type))
type = sheet.Range[row, typeColumn].Text;
if (string.IsNullOrWhiteSpace(type) || type == "適合")
continue;
distribution[type] = distribution.TryGetValue(type, out int count)
? count + 1
: 1;
}
Console.WriteLine($"異常記録の総数:{dataRows}");
foreach (var item in distribution)
{
Console.WriteLine($" {item.Key}:{item.Value} 件");
}
// 「要確認」が存在する場合は、人が確認したうえで報告書を発行する
if (distribution.ContainsKey("要確認"))
{
Console.WriteLine("要確認の項目があります。人による確認の後に報告書を発行してください。");
}
}
}
説明:
-
Workbook.LoadFromFileが読み戻すのはディスク上の実在する成果物であり、AI の自己申告とは互いに独立しています。これがクロスチェックになります。 - 集計結果は明細の総行数や過去期間のデータと照合できます。異常率が極端に高い、あるいは低い場合は、指示の見直しが必要な兆候です。
- 「要確認」の項目は必ず人が処理します。自動化フローが不確実な判定をそのまま監査結論に変えてはなりません。これは内部監査の適合性に関する基本要件です。
追跡可能性について:
WorkDirを設定しているため、各ステップの入力文書、AI が生成した実行スクリプト、出力成果物はすべてセッションディレクトリに残ります。監査の結論に疑義が生じたとき、「その時どのファイルを読み、何を実行し、何を出力したか」を工程ごとに遡れます。これは AI を監査プロセスに導入する前提条件です。
主要クラスとメソッドの解説
中心となる型
| 型 | 名前空間 | 役割 | 本記事で使用したメンバー |
|---|---|---|---|
AIOptions |
Spire.Agent.Office.AI |
AI 処理の設定 |
SpireToken:AI サービスキー;TimeoutMs:1 回の実行のタイムアウト;WorkDir:セッションディレクトリ |
AIDocumentProcessor |
Spire.Agent.Office.AI |
AI 文書プロセッサ。文書オブジェクト.AI(options) で取得 |
ExecuteInstruction(文書, 指示, 出力パス, 追加文書):自然言語の指示を実行し AIResult を返す |
AIResult |
Spire.Agent.Office.AI |
指示の実行結果 |
Success:成否;ErrorMessage:失敗理由;Duration:所要時間;TokenUsage:トークン統計 |
TokenUsage |
Spire.Agent.Office.AI |
トークン消費の統計 |
InputTokens / OutputTokens / TotalTokens
|
Document |
Spire.Doc |
Word 文書オブジェクト |
LoadFromFile() / SaveToFile() / MailMerge.Execute()
|
Workbook |
Spire.Xls |
Excel ブックオブジェクト |
LoadFromFile() / Worksheets[] / SaveToFile()
|
Worksheet |
Spire.Xls |
ワークシートオブジェクト |
LastRow / Range[行, 列] / CellRange.Value / CellRange.Text
|
⚠️
CellRange.TextとCellRange.Valueを混同しないこと:Textはテキスト型のセルでしか内容を返しません。
数値型のセル(金額、合計など)を読むと空文字が返り、しかもエラーになりません——静かに 0 件を読んでしまうのが、
ここで最も踏みやすい落とし穴です。確実な書き方はValueを優先し、Textをフォールバックにすることです。string text = cell.Value; if (string.IsNullOrEmpty(text)) text = cell.Text;本記事の
VerifyViolationsが読むのは「異常区分」というテキスト列なのでTextでも正しく動きますが、
金額や合計の列を読む場合は必ず上記の書き方に置き換えてください。
ExecuteInstruction の 4 つの引数
| 位置 | 引数 | 説明 |
|---|---|---|
| 1 | 文書オブジェクト | 主文書。今回の処理の中心となるファイル(Document / Workbook / PdfDocument / Presentation) |
| 2 | 指示 | 処理目標を記述した自然言語。日本語・英語のどちらでも可 |
| 3 | 出力パス | 成果物の保存先。拡張子が出力形式を決める |
| 4 | 追加文書 | 併せて参照する他の文書のリスト。クロスフォーマット・複数文書の場面で使用 |
利用上のヒント
-
AIOptionsは一度だけ生成する:サービス層のコンストラクタで初期化して再利用し、呼び出しごとにキーとタイムアウトを設定し直さないようにします。 - 1 つの目的に 1 つの指示:指示が記述する業務目標が単一であるほど判定は安定し、失敗時の切り分けも容易になります。
- 構造の制約は指示に書く:出力する列、列挙値、必須項目はすべて指示の中で明示します。そうして初めて下流のコードが安定した入力契約を得られます。
- 判定と計算を分離する:意味の判断は AI に、数値の計算は文書エンジンに。どちらも領域を越えさせません。
本シナリオの対応範囲と制約
| 能力 | 対応 | 説明 |
|---|---|---|
| Word / Excel / PDF / PPT の相互読み書き | 対応 | 主文書と追加ドキュメントはクロスフォーマットで組み合わせ可能 |
| 出力形式を拡張子で指定 | 対応 | 同じ内容を .xlsx / .docx / .pdf / .md などで出力できる |
| メールマージによる文書の一括生成 | 対応 |
MailMerge.Execute(単一レコード)と MailMerge.ExecuteWidthRegion(領域型) |
| スキャンした文書・画像・手書き内容の読み取り | 非対応(機能企画中) | 本記事の入力はテキスト型の文書である必要があります。スキャン文書を含む場合は別途 OCR などの処理が必要 |
| ネットワーク経由で法令の最新版を取得 | 非対応 | 規程原文は呼び出し側が用意する必要があります。AI が能動的に外部情報を取得することはありません |
| 複数指示の並列バッチ処理 | 非対応 | 1 回の呼び出しで処理できるのは 1 つの指示です。一括処理は呼び出し側でループを組む必要があります |
導入時の注意点
- SpireToken は有効であること:未設定・誤り・期限切れのキーは例外を引き起こしてプログラムを中断します。統合前にキーの有効性を確認してください。
- 必ず非同期化する:1 回の実行は完全なエージェントループであり、秒から分単位の処理です。同期 HTTP リクエストに置いてはいけません。「タスクを投入してタスク番号を返す+バックグラウンドのキューで実行する+進捗を照会する」という形に改めてください。
- 指示の粒度は小さく:本記事で 3 段階に分けたのは、1 回あたりのコンテキスト規模を抑えるためであり、失敗時にチェーン全体をやり直すのではなく該当工程を特定するためでもあります。
- 判定結果は必ず人による確認を挟む:AI が出力するのは「違反の疑い」であり「違反の結論」ではありません。金額・責任者・処分に関わる最終判断は人が行うべきです。
-
データの安全性:監査データには給与や証憑番号などの機微な情報が含まれます。
AIOptionsのBaseUrlとModelを自社のモデルサービスに向ければ、データを統制の効く範囲内で処理できます。 -
コストの把握:呼び出しごとにトークンを消費します。
AIResult.TokenUsageは完全な計測の入口になるため、部門別・月別に集計してコスト管理に組み込むことを勧めます。 -
同名の型にはエイリアスを付ける:4 つのステップを 1 つのファイルにまとめると、
using Spire.Doc;とusing Spire.Xls;がそれぞれのFileFormatを同時に導入します。そのままFileFormat.Docxと書くとCS0104(参照があいまい)になります。1 行のエイリアスで解決できます。
using DocFileFormat = Spire.Doc.FileFormat;とし、Word 側の保存をDocFileFormat.Docxに変えます。
HorizontalAlignment、VerticalAlignmentなども同様です。CS0104に遭遇したらエイリアスで解決してください。
まとめ
本記事のサンプルを通じて、C# と Spire.Agent.Office で経費精算のコンプライアンス監査フローを構築する方法を紹介しました。ステップ 1 で規程ドキュメントをルール一覧に固定化し、ステップ 2 で経費明細とルール表を 1 件ずつ照合して監査結果を出力し、ステップ 3 で監査報告書を生成し、ステップ 4 でメールマージにより確認依頼通知を一括発行します。この流れは、「規程の意味を読み取る」という作業と「文書を正確に読み書きする」という作業を、それぞれ性質の異なる実行者に委ね、役割を分離しています。
手作業による照合と比べたときの価値は、速さだけではありません。むしろ重要なのは、判定基準の一貫性とプロセスの追跡可能性です。ルール表は規程のバージョンに追随して更新され、各ステップの成果物はすべてディスクに残り、重要な数値は独立に再計算できます。基準をコードにハードコーディングする従来の方法と比べれば、「ルール」をコードから文書へと戻し、規程を改定したときはステップ 1 を再実行するだけで済むようになります。
この構成はさらに拡張できます。4 つのステップを経費精算システムの提出工程に組み込み、不適合な申請を提出時点で止める。複数期間の監査結果を集約してトレンドのダッシュボードを作り、部門ごとのコンプライアンス度合いの変化を観察する。あるいは WorkDir に残るセッション記録を利用して、監査の証跡アーカイブを構築する——いずれも自然な発展形です。
経費監査、契約書の突き合わせ、レポートの検証など、「ルールが文書に書かれ、データが表に横たわっている」要件を扱っているなら、この自然言語指示ベースのクロスフォーマットなアプローチは、実装と保守のコストを明確に下げます。
公式リソース:






