勘定科目のAI判定に「判定根拠」を言わせる設計にした話
月末に3時間くらい記帳作業に溶かしていた時期がある。明細を眺めて「これは通信費か雑費か」と迷って手が止まる、あの時間。C言語で組み込みのステートマシンを書いている身としては、この曖昧さがずっと気持ち悪かった。
この記事は、勘定科目をAIに判定させる仕組みを自分で作った過程と、そこで一番苦労した「判定根拠を出させる設計」について書く。同じように記帳の勘定科目判定で毎回止まっている人向けの内容だ。
結論を先に書く
勘定科目のAI判定は、科目名だけを返させると精度が上がらない。判定根拠(どの単語・どのパターンから判断したか)を同時に出力させることで、誤判定の原因が見え、プロンプトの修正サイクルが回るようになる。
自分の場合、根拠なしの判定で運用したときの体感精度は7割程度だった。根拠つきに変えてからは、誤判定が起きても「なぜ間違えたか」がその場でわかるので、修正のたびに精度が積み上がっていく。数字で言うと、根拠なし運用の1ヶ月目は月20件前後の手動修正が発生していたが、根拠つきに切り替えて3ヶ月目には月5件程度まで落ちた。
なぜ「科目名だけ」だと詰まるのか
C言語で例えると、これは戻り値だけ見てデバッグしようとしているのと同じ状態だ。関数が -1 を返してきても、それだけではどこで失敗したのかわからない。errno を見て初めて原因にたどり着ける。
勘定科目のAI判定も構造は同じだった。最初に自分が作った版は、明細のテキスト(摘要欄)をそのままAIに渡して「この取引の勘定科目を1つ答えて」というプロンプトだけで動かしていた。結果は返ってくるが、なぜその科目になったのかがブラックボックスになる。
たとえば「Amazon Web Services利用料」という摘要が「通信費」に判定されたり「支払手数料」に判定されたりと、実行のたびに揺れることがあった。原因を探ろうにも、AIの内部で何を根拠にしたかが見えないので、プロンプトのどこをどう直せばいいかの当たりがつけられない。
この状態のまま3週間くらい運用していたが、修正のたびに「なんとなくこの言い回しを足す」という当てずっぽうの調整になっていた。これは自分がC言語のバグ修正で一番嫌うやり方だ。再現条件もログもないまま「直った気がする」で終わらせる修正は、大体あとで再発する。
この問題、自分も困ってた。解決のためにJournalPilotというツールを作った。マネーフォワード会計の明細取込から勘定科目のAI判定、仕訳登録までを一気通貫でやる自己ホストキットで、判定根拠を毎回出力させる設計にしている。
解決手順:判定根拠を出させる設計
ここから具体的にどう組んだかを書く。
ステップ1:出力フォーマットを「科目名」ではなく「構造化データ」にする
まず、AIへの指示を「勘定科目を1つ答えて」から「以下のJSON形式で答えて」に変える。
{
"account": "通信費",
"confidence": 0.82,
"reason": "摘要に「AWS」「利用料」を含み、過去の類似取引37件中31件が通信費に分類されているため",
"matched_keywords": ["AWS", "利用料"]
}
この形式にしただけで、判定結果を人間が検証できるようになる。C言語でいうところの、関数の戻り値だけでなくログバッファに処理過程を書き出させるのと同じ発想だ。
ステップ2:根拠に「過去データとの類似度」を含めさせる
判定根拠を「なんとなくそれっぽいから」で終わらせないために、過去の仕訳履歴(自分の場合は直近12ヶ月分、約1,800件)をベクトル化して、類似する過去取引を根拠として引用させるようにした。
これにより、「なぜこの科目になったか」の説明が「摘要文言のパターン」だけでなく「過去に自分がどう判断してきたか」まで含むようになる。表記ゆれのある明細(例:「AMAZON WEB SERVICES」「AWS Japan」「Amazon Web Serv」など)でも、過去の判定履歴と紐づけることで拾いきれるようになった。
ステップ3:confidence(確信度)が低い判定は自動登録せず保留にする
根拠と一緒にconfidenceスコアを出力させ、0.7未満は自動登録せず「要確認」キューに回す設計にした。これは組み込みのウォッチドッグタイマーの発想に近い。全部を自動で通してしまうと、閾値を割ったときの検知ができない。
自分の運用では、月あたりの取引数が140件前後で、そのうち確信度0.7未満に落ちるのは月8〜12件くらい。この分だけ手動確認が残るが、逆に言えばそれ以外の130件前後は根拠つきで安心して自動登録できる。
ステップ4:誤判定が起きたら根拠のログから原因を追う
実際に誤判定が起きたときの対処フローも決めておく。根拠ログを見て、「matched_keywordsに何が入っていたか」「類似度上位の過去取引が何だったか」を確認する。ここでC言語のデバッグと同じ感覚が使える。ログを見て仮説を立て、プロンプトの該当箇所だけをピンポイントで直す。
コード例:判定根拠を伴うプロンプト設計の骨格
Pythonで実装しているが、考え方自体はC言語の状態遷移設計と同じなので、疑似コードで構造だけ示す。
def classify_transaction(description, past_transactions):
prompt = f"""
以下の取引を勘定科目に分類してください。
出力は必ず次のJSON形式にしてください。
取引摘要: {description}
類似する過去取引(上位5件): {get_similar_transactions(description, past_transactions, top_k=5)}
出力形式:
{{
"account": "科目名",
"confidence": 0.0〜1.0,
"reason": "判定理由(過去取引のどれと類似したか、摘要のどの単語に基づいたか)",
"matched_keywords": ["単語1", "単語2"]
}}
"""
result = call_claude(prompt)
return parse_and_validate(result)
def route_by_confidence(result):
if result["confidence"] >= 0.7:
register_journal_entry(result)
else:
push_to_review_queue(result)
ポイントは parse_and_validate で、AIが返してきたJSONの形式が崩れていた場合(キーが欠けている、confidenceが範囲外など)は例外にして人間確認に落とす。C言語で境界値チェックを省略しないのと同じ感覚で、ここは妥協せずに書いた。
落とし穴と対処
一番苦労したのは、根拠(reason)の文章がAIによって毎回微妙に表現が変わることだった。同じ判定でも「摘要にAWSを含むため」と書かれる回と「Amazon Web Servicesの利用料と推定されるため」と書かれる回があり、これをそのままログに溜めても後から検索・集計しづらい。
対処として、reasonは自由文のまま残しつつ、matched_keywordsを別フィールドとして必ず配列で出させる形にした。集計や再学習に使うのはmatched_keywordsの方で、reasonは人間が読むための補助情報という役割分担にした。この分離をする前は、誤判定パターンの傾向を掴むのに毎回reasonの文章を目視で読む必要があり、月末の確認作業が1時間近くかかっていた。分離してからはmatched_keywordsの頻度集計だけで傾向が見えるようになり、その作業は15分程度で終わるようになった。
もう一つ、confidenceの数値をAI自身に申告させる方式は、最初は数値が全体的に高めに出る傾向があった(実際にはよく外している判定でも0.9と答えてくる)。これに対しては、過去の判定結果と実際の正解(人間が修正したかどうか)を突き合わせて、confidenceの実測との乖離をログに残し、プロンプト側で「confidenceは保守的に見積もってください」と明示的に指示する形で調整した。完全には解消していないが、乖離幅は縮まった実感がある。
関連ツール
この設計を組み込んだ状態で自分が実際に運用しているのが、JournalPilotというマネーフォワード会計向けの自己ホストキットだ。明細取込から勘定科目のAI判定(判定根拠つき)、仕訳登録までを一通り自動化していて、売掛金の発生と回収の突合チェックも読み取り専用スクリプトで用意している。自分の記帳作業が月数時間かかっていたのが、確認作業だけで済むようになった。
まとめ
勘定科目のAI判定は、科目名だけを出させると誤判定の原因が追えなくなる。判定根拠と確信度をセットで出力させ、閾値未満は自動登録しない設計にすることで、誤判定への対処がデバッグ可能な作業に変わる。C言語のログ設計と同じ発想が、こういうところでも普通に使えるという実感がある。
著者:ぽん(@pon_freelance)
C言語実務23年、組み込み/制御系。
副業で技術記事販売と自作ツール販売をやっている。
書いているもの:
- JournalPilot - マネーフォワード会計の記帳を自動化する自己ホストキット(明細取込→勘定科目AI判定→仕訳登録)
(その他:(なし))
同じ手で作った道具を、インストールなしで試せます — 自分が実際に使っている自作ツールを、ブラウザからそのまま動かせる形で並べています。登録は不要です。
→ RunStack(無料で試す)