はじめに
WindowsアプリにローカルAIを組み込んでいると、推論精度や処理時間に加えて、途中でやめたときの動作も確認する必要があります。
キャンセルボタンを押して画面の待機が終わっても、それだけでは、処理結果がどこまで反映されたのか、推論側の計算も止まったのかは分かりません。
Local Manual Maker(LMM)の開発では、文章整形とVisionのキャンセルを調べる中で、この区別が必要になりました。特に、文章整形の途中でキャンセルすると、完了済みの結果は反映される挙動でした。「キャンセルしたから変更はゼロ」とは説明できません。
この記事では、2026年10月7日の開発検証版v16(2.0.0.15)の記録をもとに、何を確認し、何を未確認として残したかを整理します。当時の構成に対する検証記録であり、現行版の全条件で同じ結果になることや、すべてのAIアプリのキャンセル動作を保証するものではありません。
1. まずキャンセル後に確認したいことを分ける
今回の構成は、Windowsアプリからローカルの推論サーバーへ処理を依頼する方式でした。文章整形とVisionが同じサーバーを使用します。
そのため、少なくとも次の項目は別々に見る必要がありました。
| 確認する場所 | 確かめたいこと |
|---|---|
| 画面と呼び出し側 | 待機が終わり、キャンセルとして表示されるか |
| 処理の進行 | まだ始まっていない後続のバッチへ進まないか |
| 編集データ | 完了済みの結果と、未完了の結果をどう扱うか |
| 推論サーバー | サーバー内部の計算が実際に停止したか |
| 次の操作 | キャンセル後の再実行が成功するか |
.NETのキャンセルは協調的な仕組みで、要求を受けた側が停止要求に応答する必要があります。トークンを取り消したことだけで、処理のすべてが強制的に止まるわけではありません。基本的な考え方は、Microsoft Learnのマネージドスレッドでの取り消しに整理されています。
アプリ側の待機解除、結果の反映、推論サーバー内部の停止を、一つの「キャンセル成功」にまとめないようにしました。
2. キャンセルが利用不可として表示されていた
当時は、推論サーバーの起動中にキャンセルすると、「モデル/ランタイム利用不可」と表示される問題がありました。
調査で分かったのは、キャンセルという情報が途中の層で失われていたことです。
- 起動を管理する処理が、キャンセル時にも
falseを返していた。 - その呼び出し元は、キャンセルの例外ではなく
nullを返していた。 - 上位のサービスにはキャンセル状態が伝わらず、利用不可として扱われていた。
画面にキャンセル用の表示分岐を足すだけでは、そこまでキャンセル状態が届きません。修正では、起動管理側からキャンセルを例外として伝えるようにし、画面が「AI:キャンセル」になることを確認しました。
このときは、UIの文言だけでなく、キャンセル、起動失敗、利用不可がどの経路で上位へ渡るかを追う必要がありました。
3. 起動中と推論中では反映結果が違った
修正を含む実パッケージで、次の三つを分けて確認しました。
| キャンセルした場面 | 当時の確認結果 | 次の実行 |
|---|---|---|
| 文章整形のためのサーバー起動中 | 適用0件。「AI:キャンセル」と表示 | 完走 |
| 文章整形の推論中 | 完了済み5件がキャンセル時点で適用。その後30秒間は変化なし | 残り11件が完走 |
| Visionの推論中 | 提案は表示されず、適用なし | 再実行成功 |
文章整形の推論中の試験では、「処理中(5/16)」を観測してからキャンセルしました。記録上、後続のバッチ2〜4の実行ログはなく、完了済みの5件だけがキャンセル時点で反映されています。
ここで分けるべきなのは、次の二つです。
- キャンセル時点までに完了した結果を反映すること
- キャンセル後に、未完了だった処理の結果が遅れて反映されること
今回確認した5件は前者でした。完了済みの結果をすべて巻き戻す仕様ではありません。
また、「30秒間は変化しなかった」は、その観測時間内の確認です。あらゆるタイミングや長時間の動作を網羅した結果とは扱っていません。
4. サーバーが残っていることだけでも判断できない
文章整形の推論中にキャンセルした後も、推論サーバーのプロセスは生きていました。その状態で再実行し、残りの11件は完走しています。
ただし、プロセスが存在することと、その内部で直前の推論が続いていることは同じではありません。逆に、次の要求が成功したからといって、キャンセルボタンを押した時点で直前の計算が停止した証明にもなりません。
この検証で確認できたのは、呼び出し側の待機が終わったこと、後続バッチが実行されなかったこと、観測中に追加の反映がなかったこと、次の実行が成功したことです。
サーバー内部の推論停止そのものは未確認として残しました。
内部停止まで確認したい場合は、その要求を識別できるサーバーログや実装など、別の根拠が必要になります。画面表示やプロセスの有無だけで代用すると、確認した範囲を広く説明しすぎてしまいます。
5. 同じような試験をするときに残す項目
今回の経験から、キャンセル試験の記録には次を含めると、結果を読み返しやすいと考えています。
- キャンセルした段階:起動中、推論中、複数件処理の途中など
- 押下時の表示と、完了していた件数
- キャンセル後の状態表示
- 反映された件数と、その反映タイミング
- 後続処理のログの有無
- 追加反映がないことを観測した時間
- 再実行の成否
- サーバー内部について確認できたことと、未確認のこと
「完了済みの結果を保持する」「処理開始前へ戻す」のどちらにするかは、アプリの設計によって変わります。試験では、期待する挙動を先に決め、実際の結果と照合する必要があります。
まとめ
キャンセルの確認では、ボタンが押せて待機が終わることだけを見ていると、反映済みデータや推論側の状態を見落とします。
今回の開発検証では、文章整形の途中キャンセルで完了済み5件が反映されること、起動中やVisionのキャンセルとは結果が異なることを確認しました。一方、サーバー内部の推論停止は未確認です。
何を止める操作なのか、どこまで結果を残すのか、どの層まで観測したのか。この三点を記録しておくと、「キャンセルできた」という報告を、具体的な動作として説明できます。
開発しているアプリ
Local Manual Maker(LMM)は、Windows操作を記録し、画面と説明を確認・編集してWord/Excelへ出力するアプリです。AIによる文章整形やVision補正は、操作記録の後に任意で実行します。