問い合わせ分類をif文で作る — 条件を足しても14/18のままだった理由
「料金に問題はありませんが、ログインできません」という架空の問い合わせ文を、料金の分類から順に調べるif文で判定すると、「料金・請求」と誤判定されます。前置きを除去する文脈分割を導入したところ、手元の調整文9件では正解数が6/9から9/9へと向上しました。しかし、あらかじめ内容を確認している固定比較文18件に同じ処理を適用すると、正解数は14/18から14/18のままで推移し、全件で予測ラベルが一致しました。本稿では、キーワード探索順と文脈分割の挙動を実測値から確認します。
キーワードによる分類と優先順位の仕組み
サポート窓口に寄せられる問い合わせ文を自動で振り分けるため、この実習では以下の3つのクラスを分類先として定義します。使用する例文はすべて教材用に用意された架空の文章です。
- クラス0(料金・請求):利用料金の確認、請求書の受領、返金や解約の相談など
- クラス1(ログイン・認証):パスワードの失念、アカウントへのサインイン、本人確認など
- クラス2(操作・不具合):画面の表示乱れ、ファイルの保存失敗、アプリの強制終了など
正解ラベルの判定基準は、「問い合わせた人がいま最も解決したい主目的」です。たとえば、「料金に問題はありませんが、ログインできません」という問い合わせ文には、「料金」と「ログイン」という2つの単語が含まれています。利用者が困っていて解決を求めている内容はログインの失敗であるため、この文の正解クラスは「1(ログイン・認証)」となります。
最初に検証するルールv1は、定義したキーワード辞書を用いて判定を行います。Python 3.11.2の標準ライブラリのみを使用し、外部パッケージには依存しません。
KEYWORDS = [
['料金','請求','支払','領収','課金','返金','解約','価格','金額','引き落と'],
['ログイン','パスワード','認証','アカウント','サインイン','本人確認','ID','コード'],
['画面','保存','ファイル','アプリ','検索','エラー','アップロード','印刷','表示','動画','ボタン'],
]
def rule_v1(text):
for label, words in enumerate(KEYWORDS):
if any(word in text for word in words): return label
return -1
上の判定コードでは、KEYWORDS リストをクラス0(料金・請求)、クラス1(ログイン・認証)、クラス2(操作・不具合)の順に走査します。文中にキーワードが含まれた時点でそのクラス番号を返し、どの単語も含まれなければ -1(未分類)を返します。単語リストにない表記であっても、登録語を部分文字列として含んでいれば一致します。
ルールv1は料金の分類を先に調べます。「料金に問題はありませんが、ログインできません」でも料金が一致し、正解の認証には進みません。文中で先に出た単語を選ぶ処理ではありません。
接続表現による文脈分割とフォーカスの更新
ルールv1で発生した「前置きの話題に引きずられる誤判定」を避けるため、ルールv2では判定対象とする文字列の範囲(focus)を更新する処理を導入します。
問い合わせ文では、問題のない事象を前置きとして述べた後、逆接を挟んで困りごとを伝える文がみられます。そこで、文中の接続表現を検知し、その末尾部分を切り出して判定します。
def rule_v2(text):
focus=text
for marker in ['ありませんが、','できますが、','正常ですが、','後に']:
if marker in focus: focus=focus.split(marker)[-1]
return rule_v1(focus)
上のルールv2では、マーカーリストとして ['ありませんが、', 'できますが、', '正常ですが、', '後に'] の4つの文字列を定義し、この順番で1つずつ確認します。初期状態の focus に入力全文を代入し、focus 内にマーカーが含まれていれば focus.split(marker)[-1] で更新します。文中に同じマーカーが複数回現れる場合は、最後のマーカーより後ろの文字列が新たな focus となります。得られた focus に対して、ルールv1と同じキーワード走査を実行します。
採用した4つのマーカーは、教材の調整文9件の文面に存在する文字列を抽出したものです。構文解析や文法規則に基づいているわけではなく、特定の文字列パターンに合致した部分を機械的に切り出します。
たとえば調整文「料金に問題はありませんが、ログインできません」にルールv2を適用すると、マーカー「ありませんが、」によって focus が「ログインできません」に切り替わります。残りのマーカーは含まれないため focus が保持され、この文字列に対してキーワード走査が行われます。クラス0のキーワードは検出されず、クラス1の「ログイン」が一致するため、正解ラベルと同じ 1 が出力されます。
| 確認する文章 | 変更前 v1 | 条件を加えた v2 |
|---|---|---|
| 調整文 | 6/9 | 9/9 |
| 固定比較文 | 14/18 | 14/18 |
上の成績一覧が示すように、調整文9件では、前置きを取り除いたことで正解数がルールv1の6件(6/9)からルールv2の9件(9/9)へと向上しました。
固定比較データでの検証とルールの限界
調整文9件での結果を確認した後、あらかじめ各クラス6件ずつ用意されていた固定比較文18件に対してルールv1とルールv2を実行しました。
その結果、正解数はルールv1で14件、ルールv2でも14件となり、正解数は変化しませんでした(14/18)。18件の全データにおいて、ルールv1が出力した予測ラベル番号とルールv2が出力した予測ラベル番号は一致しています。未分類となった1件を含め、誤答は4件でした。
この18件のうち「ログインは問題ありませんが、請求の金額がおかしいです」という文では、「ありませんが、」によって focus が「請求の金額がおかしいです」へ切り出されています。ただし、切り出し前も切り出し後もクラス0のキーワード(「請求」「金額」)が検出されるため、読む範囲は変化したものの、予測結果はどちらも正解の 0 で一致しました。
| 固定比較文で残った誤り | 正解 | v1・v2の判定 |
|---|---|---|
| 今月の利用料について内訳を教えてください | 料金・請求 | 未分類(−1) |
| 料金ではなく、サインインについて相談したいです | ログイン・認証 | 料金・請求 |
| ログインは成功しましたが、一覧画面が空白です | 操作・不具合 | ログイン・認証 |
| 請求は正常なので、ファイルを開けない問題を解決したい | 操作・不具合 | 料金・請求 |
上の誤答一覧に示す4件は、登録語を含まない未分類が1件と、文脈分割が行われずに元の分類優先順位が残った事例が3件です。未分類となった「今月の利用料について内訳を教えてください」(正解:0 料金・請求、予測:-1)では、クラス0の辞書に「利用料」という単語が含まれておらず、登録語(「料金」「請求」「支払」など)を部分文字列としても含まないため、どのキーワードにも一致せず -1 となりました。
残る3件の誤答は、v2で定義した4条件に合致しなかった事例です。「料金ではなく、サインインについて相談したいです」(正解1、予測0)、「ログインは成功しましたが、一覧画面が空白です」(正解2、予測1)、「請求は正常なので、ファイルを開けない問題を解決したい」(正解2、予測0)の3文には、「ではなく、」「成功しましたが、」「正常なので、」という接続表現が含まれていますが、いずれもv2のマーカーには登録されていません。そのため focus は分割されず、全文に対してキーワード走査が実行された結果、料金→認証→操作という元の分類優先順位がそのまま適用されて誤判定となりました。
この固定比較文18件は以前から内容を確認している固定例であり、今回初めて確認した独立データではありません。この誤答を見て条件を足すと、同じ18件をルールの調整に使うことになります。追加した条件の有効性は、別の文を用意して確かめる必要があります。
自分の文でルールを確かめる
実習ファイルを展開し、text-labsフォルダで実行します。追加のPythonパッケージは不要です。
python run-rules.py --out rules-results-local.json出力の予測番号を公開した結果と比べられます。時刻などは再実行で変わるため、JSON全体の一致は求めません。
次に試す文は、正解基準と一緒に先に用意します。すでに見た18件に条件を足しただけで、未知の文への改善を示したことにはなりません。
参照資料と実行記録
- Python公式:文字列の分割
splitに区切り文字を指定した場合の動作。
- 実行したルールと全件の判定
調整9件・固定比較18件の予測と、判定に使った文章。
- 実行スクリプト
Python標準ライブラリだけで同じ架空データを判定。
確認日:2026年10月22日。