Skip to main content

sokuto_runtime/hierarchical/
mapping.rs

1//! # 粗密階層マッピングモジュール
2//!
3//! 大分類 (Coarse) と細分類 (Fine) のオントロジー定義、双方向インデックス、
4//! および MECE (相互排他的かつ網羅的) 性のバリデーションを提供する。
5
6use indexmap::IndexMap;
7use serde::{Deserialize, Serialize};
8
9use crate::engine::coarse::{DEFAULT_NEGATIVE_KEYS, is_negative_candidate};
10use crate::error::{Result, RuntimeError};
11
12/// 大分類と細分類の対応関係を保持する階層マッピング構造体。
13///
14/// 大分類 (4〜8 クラスタ) と細分類 (各 8〜12 候補) の双方向高速参照を実現する。
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct HierarchicalMapping {
17    /// マッピング全体の識別名。
18    pub name: String,
19    /// 大分類の識別子と説明文のマッピング。
20    pub coarse_categories: IndexMap<String, String>,
21    /// 全細分類の識別子と説明文のマッピング。
22    pub fine_criteria: IndexMap<String, String>,
23    /// 大分類識別子から配下の細分類識別子リストへのマッピング。
24    pub coarse_to_fine: IndexMap<String, Vec<String>>,
25    /// 細分類識別子から所属する大分類識別子への逆引きマッピング。
26    pub fine_to_coarse: IndexMap<String, String>,
27    /// 事前キャッシュされた全大分類キーの順序保持リスト。
28    #[serde(default)]
29    pub all_coarse_keys: Vec<String>,
30    /// 事前キャッシュされた全細分類キーの順序保持リスト。
31    #[serde(default)]
32    pub all_fine_keys: Vec<String>,
33    /// 受け皿 (None / その他) 候補として判定するキー一覧。
34    pub negative_keys: Vec<String>,
35}
36
37impl HierarchicalMapping {
38    /// 新しいビルダーを生成する。
39    pub fn builder(name: impl Into<String>) -> HierarchicalMappingBuilder {
40        HierarchicalMappingBuilder::new(name)
41    }
42
43    /// マッピングの MECE 性および整合性を検証する。
44    ///
45    /// # 検証項目
46    /// - 大分類が 1 つ以上定義されていること。
47    /// - 各大分類に少なくとも 1 つ以上の細分類が割り当てられていること。
48    /// - すべての細分類が `fine_criteria` に定義されていること。
49    /// - すべての細分類がいずれか 1 つの大分類にのみ重複なく所属していること。
50    /// - `coarse_to_fine` と `fine_to_coarse` が厳密に双方向一致していること。
51    pub fn validate(&self) -> Result<()> {
52        if self.coarse_categories.is_empty() {
53            return Err(RuntimeError::InvalidQuestion(
54                "大分類 (coarse_categories) が 1 つも定義されていません。".to_string(),
55            ));
56        }
57
58        if self.fine_criteria.is_empty() {
59            return Err(RuntimeError::InvalidQuestion(
60                "細分類 (fine_criteria) が空です。".to_string(),
61            ));
62        }
63
64        // 1. 各大分類の重複および MECE 性検証
65        let mut seen_fine_keys = indexmap::IndexSet::new();
66        for fine_list in self.coarse_to_fine.values() {
67            for fine_key in fine_list {
68                if !seen_fine_keys.insert(fine_key.clone()) {
69                    return Err(RuntimeError::InvalidQuestion(format!(
70                        "細分類 `{}` が複数の大分類に重複して割り当てられています (MECE 違反)。",
71                        fine_key
72                    )));
73                }
74            }
75        }
76
77        // 2. 大分類配下の細分類検証
78        for (coarse_key, coarse_desc) in &self.coarse_categories {
79            if coarse_key.trim().is_empty() {
80                return Err(RuntimeError::InvalidQuestion(
81                    "空の大分類キーが存在します。".to_string(),
82                ));
83            }
84            if coarse_desc.trim().is_empty() {
85                return Err(RuntimeError::InvalidQuestion(format!(
86                    "大分類 `{}` の説明文が空です。",
87                    coarse_key
88                )));
89            }
90
91            let fine_list = self.coarse_to_fine.get(coarse_key).ok_or_else(|| {
92                RuntimeError::InvalidQuestion(format!(
93                    "大分類 `{}` に対する細分類リストが定義されていません。",
94                    coarse_key
95                ))
96            })?;
97
98            if fine_list.is_empty() {
99                return Err(RuntimeError::InvalidQuestion(format!(
100                    "大分類 `{}` 配下の細分類リストが空です。",
101                    coarse_key
102                )));
103            }
104
105            for fine_key in fine_list {
106                if !self.fine_criteria.contains_key(fine_key) {
107                    return Err(RuntimeError::InvalidQuestion(format!(
108                        "大分類 `{}` 配下の細分類 `{}` が fine_criteria に存在しません。",
109                        coarse_key, fine_key
110                    )));
111                }
112
113                let mapped_coarse = self.fine_to_coarse.get(fine_key).ok_or_else(|| {
114                    RuntimeError::InvalidQuestion(format!(
115                        "細分類 `{}` から大分類への逆引きエントリが存在しません。",
116                        fine_key
117                    ))
118                })?;
119
120                if mapped_coarse != coarse_key {
121                    return Err(RuntimeError::InvalidQuestion(format!(
122                        "細分類 `{}` の逆引き大分類 (`{}`) が所属大分類 (`{}`) と一致しません。",
123                        fine_key, mapped_coarse, coarse_key
124                    )));
125                }
126            }
127        }
128
129        // 定義された全細分類がカバーされているかを検証
130        if seen_fine_keys.len() != self.fine_criteria.len() {
131            let missing_keys: Vec<_> = self
132                .fine_criteria
133                .keys()
134                .filter(|k| !seen_fine_keys.contains(*k))
135                .cloned()
136                .collect();
137            return Err(RuntimeError::InvalidQuestion(format!(
138                "いずれの大分類にも属していない孤立細分類が存在します: {:?}",
139                missing_keys
140            )));
141        }
142
143        Ok(())
144    }
145
146    /// 大分類のキーおよび説明文マップを取得する。
147    pub fn coarse_criteria(&self) -> &IndexMap<String, String> {
148        &self.coarse_categories
149    }
150
151    /// 指定された大分類キー配下の細分類識別子スライスを取得する。
152    pub fn fine_keys_for_coarse(&self, coarse_key: &str) -> Option<&[String]> {
153        self.coarse_to_fine.get(coarse_key).map(|v| v.as_slice())
154    }
155
156    /// 指定された細分類が属する大分類キーを取得する。
157    pub fn coarse_for_fine(&self, fine_key: &str) -> Option<&str> {
158        self.fine_to_coarse.get(fine_key).map(|s| s.as_str())
159    }
160
161    /// 登録されている細分類の総数を取得する。
162    pub fn total_fine_count(&self) -> usize {
163        self.fine_criteria.len()
164    }
165
166    /// 登録されている大分類の総数を取得する。
167    pub fn total_coarse_count(&self) -> usize {
168        self.coarse_categories.len()
169    }
170
171    /// 事前キャッシュされた全細分類キーの参照スライスを取得する。
172    pub fn all_fine_keys(&self) -> &[String] {
173        &self.all_fine_keys
174    }
175
176    /// 事前キャッシュされた全大分類キーの参照スライスを取得する。
177    pub fn all_coarse_keys(&self) -> &[String] {
178        &self.all_coarse_keys
179    }
180
181    /// 指定された候補が受け皿 (None / その他) 候補であるかを判定する。
182    pub fn is_negative_candidate(&self, key: &str, description: &str) -> bool {
183        is_negative_candidate(key, description, &self.negative_keys)
184    }
185
186    /// Banking77 向け 7 大分類プリセットを構築する。
187    ///
188    /// 77 候補の金融インテントを 7 個の大分類クラスタへ網羅的に分類する。
189    pub fn banking77() -> Self {
190        let mut builder = Self::builder("Banking77-7Clusters");
191
192        // 1. カード発行・利用管理 (card_ops: 12 件)
193        builder.add_category(
194            "card_ops",
195            "カード発行・配送・更新・有効化および利用設定",
196            vec![
197                (
198                    "activate_my_card",
199                    "新しいカードを有効化・利用開始登録したい。",
200                ),
201                (
202                    "card_about_to_expire",
203                    "カードの有効期限が近づいているため更新したい。",
204                ),
205                (
206                    "card_acceptance",
207                    "カードが加盟店や決済端末で利用可能か確認したい。",
208                ),
209                (
210                    "card_arrival",
211                    "申し込んだカードがいつ届くか、配送状況を確認したい。",
212                ),
213                (
214                    "card_delivery_estimate",
215                    "カードの発送時期や到着予定日を知りたい。",
216                ),
217                (
218                    "card_linking",
219                    "カードをアプリや外部アカウントに連携・登録したい。",
220                ),
221                (
222                    "card_not_working",
223                    "カード決済や読み取りが正常に動作しない。",
224                ),
225                (
226                    "card_swallowed",
227                    "ATM や端末にカードが吸い込まれて戻らない。",
228                ),
229                (
230                    "getting_spare_card",
231                    "予備カードや追加カードの発行を申請したい。",
232                ),
233                (
234                    "getting_virtual_card",
235                    "即時利用可能なバーチャルカードを発行したい。",
236                ),
237                (
238                    "order_physical_card",
239                    "プラスチック製の物理カードを新規発行したい。",
240                ),
241                (
242                    "pin_blocked",
243                    "PIN コードの入力間違い等で暗証番号がロックされた。",
244                ),
245            ],
246        );
247
248        // 2. 口座間送金・振込 (transfers: 11 件)
249        builder.add_category(
250            "transfers",
251            "口座間送金・銀行振込・送金エラーおよび送金取消",
252            vec![
253                (
254                    "balance_not_updated_after_bank_transfer",
255                    "銀行振込を行ったが残高に反映されない。",
256                ),
257                (
258                    "beneficiary_not_allowed",
259                    "振込先口座が登録できない、または送金不可と表示される。",
260                ),
261                (
262                    "cancel_transfer",
263                    "実行済みまたは予約中の送金・振込を取り消したい。",
264                ),
265                (
266                    "failed_transfer",
267                    "送金手続きそのものが失敗・エラーになった。",
268                ),
269                (
270                    "pending_transfer",
271                    "送金処理が保留中 (Pending) のまま完了しない。",
272                ),
273                (
274                    "transfer_fee_charged",
275                    "送金時に想定外の手数料が差し引かれた。",
276                ),
277                (
278                    "transfer_into_account",
279                    "外部口座から本口座への振込方法や着金を知りたい。",
280                ),
281                (
282                    "transfer_not_received_by_recipient",
283                    "送金完了通知が出たが受取人に届いていない。",
284                ),
285                (
286                    "transfer_timing",
287                    "送金が相手に届くまでの所要時間やスケジュールを知りたい。",
288                ),
289                (
290                    "unable_to_verify_identity_for_transfer",
291                    "送金時の本人確認ステップを通過できない。",
292                ),
293                (
294                    "wrong_amount_sent",
295                    "振込金額を誤って多く(または少なく)送金してしまった。",
296                ),
297            ],
298        );
299
300        // 3. 店舗・オンライン決済・手数料 (payments: 11 件)
301        builder.add_category(
302            "payments",
303            "店舗・オンライン決済・カード決済手数料および二重請求",
304            vec![
305                (
306                    "card_payment_fee_charged",
307                    "カード決済時に余分な手数料が請求された。",
308                ),
309                (
310                    "card_payment_not_recognised",
311                    "カードの利用明細に身に覚えのない請求がある。",
312                ),
313                (
314                    "card_payment_wrong_exchange_rate",
315                    "海外決済時の為替レートが想定と異なる。",
316                ),
317                (
318                    "declined_card_payment",
319                    "店舗またはオンラインでのカード決済が拒否された。",
320                ),
321                (
322                    "direct_debit_payment_problem",
323                    "口座引き落とし・自動引き落としの支払いに失敗した。",
324                ),
325                (
326                    "disposable_card_limits",
327                    "使い捨てバーチャルカードの利用限度額や制限を知りたい。",
328                ),
329                (
330                    "extra_charge_on_statement",
331                    "請求明細に身に覚えのない追加料金が加算されている。",
332                ),
333                (
334                    "pending_card_payment",
335                    "カード決済の引き落としが保留中になっている。",
336                ),
337                (
338                    "refund_not_showing_up",
339                    "返品・キャンセルの返金が口座に反映されない。",
340                ),
341                (
342                    "transaction_charged_twice",
343                    "1 回の買い物で同じ金額が二重に決済・請求された。",
344                ),
345                (
346                    "unable_to_refund",
347                    "アプリ上から返金処理・キャンセル処理を実行できない。",
348                ),
349            ],
350        );
351
352        // 4. ATM 出金・現金取扱 (cash_atm: 9 件)
353        builder.add_category(
354            "cash_atm",
355            "ATM 出金・現金引き出し・ATM 手数料トラブル",
356            vec![
357                (
358                    "atm_support",
359                    "利用可能な提携 ATM や利用方法について知りたい。",
360                ),
361                (
362                    "cash_withdrawal_charge",
363                    "ATM 現金引き出し時に想定外の手数料が引かれた。",
364                ),
365                (
366                    "cash_withdrawal_not_recognised",
367                    "利用明細に身に覚えのない ATM 現金引き出しがある。",
368                ),
369                (
370                    "declined_cash_withdrawal",
371                    "ATM での現金引き出しが拒否・失敗した。",
372                ),
373                (
374                    "supported_cards_currencies_at_atm",
375                    "ATM で利用可能なカード種別や対応通貨を知りたい。",
376                ),
377                (
378                    "wrong_amount_of_cash_received",
379                    "ATM から指定と異なる金額の現金が出てきた。",
380                ),
381                (
382                    "wrong_exchange_rate_for_cash_withdrawal",
383                    "海外 ATM で出金した際の為替レートが不当に高い。",
384                ),
385                (
386                    "contactless_not_working",
387                    "非接触 (タッチ) 決済がリーダーで反応しない。",
388                ),
389                (
390                    "declined_transfer",
391                    "システム規制や不正検知により送金が強制拒否された。",
392                ),
393            ],
394        );
395
396        // 5. 残高チャージ・入金 (topup: 11 件)
397        builder.add_category(
398            "topup",
399            "残高チャージ・入金反映・チャージ失敗および限度額",
400            vec![
401                (
402                    "automatic_top_up",
403                    "残高連動のオートチャージ (自動入金) を設定・解除したい。",
404                ),
405                (
406                    "balance_not_updated_after_cheque_or_cash_deposit",
407                    "小切手や現金で入金したが口座残高に反映されない。",
408                ),
409                (
410                    "pending_top_up",
411                    "チャージ処理が処理中・保留のまま残高に加算されない。",
412                ),
413                (
414                    "reverted_card_payment",
415                    "カード決済が店舗側により取り消し・差し戻された。",
416                ),
417                (
418                    "top_up_by_bank_transfer_charge",
419                    "銀行振込による残高チャージで手数料が発生した。",
420                ),
421                (
422                    "top_up_by_card_charge",
423                    "クレジットカード等からのチャージで手数料が発生した。",
424                ),
425                (
426                    "top_up_by_cash_or_cheque",
427                    "現金や小切手による口座残高のチャージ方法を知りたい。",
428                ),
429                (
430                    "top_up_failed",
431                    "クレジットカード等からの残高チャージ操作が失敗した。",
432                ),
433                (
434                    "top_up_limits",
435                    "残高チャージの上限額や回数制限を確認したい。",
436                ),
437                (
438                    "top_up_reverted",
439                    "チャージした金額が取り消され元に戻ってしまった。",
440                ),
441                (
442                    "verify_top_up",
443                    "残高チャージ時に追加の認証や確認を求められた。",
444                ),
445            ],
446        );
447
448        // 6. 口座設定・認証・セキュリティ (account_sec: 13 件)
449        builder.add_category(
450            "account_sec",
451            "口座設定・認証情報・紛失盗難対応・セキュリティ",
452            vec![
453                ("change_pin", "カードの暗証番号 (PIN) を変更したい。"),
454                (
455                    "compromised_card",
456                    "カード番号が漏洩・不正利用された疑いがある。",
457                ),
458                (
459                    "edit_personal_details",
460                    "氏名、住所、電話番号などの登録個人情報を更新したい。",
461                ),
462                (
463                    "lost_or_stolen_card",
464                    "カードを紛失した、または盗難に遭ったため即時停止したい。",
465                ),
466                (
467                    "lost_or_stolen_phone",
468                    "アプリが登録されている携帯電話を紛失・盗難された。",
469                ),
470                (
471                    "passcode_forgotten",
472                    "アプリのログインパスコードやパスワードを忘れた。",
473                ),
474                (
475                    "terminate_account",
476                    "サービスを退会し、口座・アカウントを解約したい。",
477                ),
478                (
479                    "verify_my_identity",
480                    "口座開設や取引制限解除のための本人確認を行いたい。",
481                ),
482                (
483                    "verify_source_of_funds",
484                    "資金源や入金根拠に関する書類確認を求められた。",
485                ),
486                (
487                    "why_verify_identity",
488                    "なぜ本人確認書類の提出が必要なのか理由を知りたい。",
489                ),
490                (
491                    "age_limit",
492                    "サービスの利用年齢制限や未成年者の利用条件を知りたい。",
493                ),
494                (
495                    "country_support",
496                    "本サービスが利用可能な対応国・地域を確認したい。",
497                ),
498                (
499                    "visa_or_mastercard",
500                    "発行されるカードの国際ブランド (Visa/Mastercard) を知りたい。",
501                ),
502            ],
503        );
504
505        // 7. 外貨両替・付帯サービス (exchange_fee: 10 件)
506        builder.add_category(
507            "exchange_fee",
508            "外貨両替・為替手数料・アプリ内両替および付帯サービス",
509            vec![
510                (
511                    "exchange_charge",
512                    "通貨両替時に適用される手数料体系について知りたい。",
513                ),
514                (
515                    "exchange_rate",
516                    "現在の適用為替レートやリアルタイム換算レートを確認したい。",
517                ),
518                (
519                    "exchange_via_app",
520                    "アプリ内で複数の法定通貨・外貨を両替・保有したい。",
521                ),
522                (
523                    "fiat_currency_support",
524                    "サポートされている法定通貨の種類や制限を知りたい。",
525                ),
526                (
527                    "pending_cash_withdrawal",
528                    "ATM 現金引き出しの処理が完了待ちになっている。",
529                ),
530                (
531                    "request_refund",
532                    "店舗やサービス提供元に対して返金請求を行いたい。",
533                ),
534                (
535                    "receiving_money",
536                    "国内外からのお金の受け取り手順や受取口座情報を確認したい。",
537                ),
538                (
539                    "transfer_fee_refund",
540                    "誤って差し引かれた送金手数料の返還を求めたい。",
541                ),
542                (
543                    "wrong_amount_refunded",
544                    "店舗から返金された金額が購入金額と異なっている。",
545                ),
546                (
547                    "other_or_unsupported",
548                    "上記のいずれにも該当しないその他の問い合わせ。",
549                ),
550            ],
551        );
552
553        builder
554            .build()
555            .expect("Banking77 プリセットのバリデーションに合格する。")
556    }
557}
558
559/// `HierarchicalMapping` を構築するためのビルダー構造体。
560#[derive(Debug, Clone)]
561pub struct HierarchicalMappingBuilder {
562    name: String,
563    coarse_categories: IndexMap<String, String>,
564    fine_criteria: IndexMap<String, String>,
565    coarse_to_fine: IndexMap<String, Vec<String>>,
566    fine_to_coarse: IndexMap<String, String>,
567    negative_keys: Vec<String>,
568}
569
570impl HierarchicalMappingBuilder {
571    /// 新しいビルダーを初期化する。
572    pub fn new(name: impl Into<String>) -> Self {
573        Self {
574            name: name.into(),
575            coarse_categories: IndexMap::new(),
576            fine_criteria: IndexMap::new(),
577            coarse_to_fine: IndexMap::new(),
578            fine_to_coarse: IndexMap::new(),
579            negative_keys: DEFAULT_NEGATIVE_KEYS
580                .iter()
581                .map(|&s| s.to_string())
582                .collect(),
583        }
584    }
585
586    /// ネガティブキーのリストを設定する。
587    pub fn negative_keys(mut self, keys: Vec<String>) -> Self {
588        self.negative_keys = keys;
589        self
590    }
591
592    /// ネガティブキーを 1 件追加する。
593    pub fn add_negative_key(mut self, key: impl Into<String>) -> Self {
594        self.negative_keys.push(key.into());
595        self
596    }
597
598    /// 大分類と、その配下の細分類群を一括追加する。
599    ///
600    /// # 引数
601    /// - `coarse_key`: 大分類キー。
602    /// - `coarse_desc`: 大分類の説明文。
603    /// - `fine_items`: (細分類キー, 細分類説明文) のリスト。
604    pub fn add_category(
605        &mut self,
606        coarse_key: impl Into<String>,
607        coarse_desc: impl Into<String>,
608        fine_items: Vec<(impl Into<String>, impl Into<String>)>,
609    ) -> &mut Self {
610        let coarse_k = coarse_key.into();
611        let coarse_d = coarse_desc.into();
612
613        let mut fine_keys = Vec::with_capacity(fine_items.len());
614        for (f_key, f_desc) in fine_items {
615            let fk = f_key.into();
616            let fd = f_desc.into();
617            self.fine_criteria.insert(fk.clone(), fd);
618            self.fine_to_coarse.insert(fk.clone(), coarse_k.clone());
619            fine_keys.push(fk);
620        }
621
622        self.coarse_categories.insert(coarse_k.clone(), coarse_d);
623        self.coarse_to_fine.insert(coarse_k, fine_keys);
624        self
625    }
626
627    /// マッピングを検証し、構築する。
628    pub fn build(self) -> Result<HierarchicalMapping> {
629        let all_coarse_keys: Vec<String> = self.coarse_categories.keys().cloned().collect();
630        let all_fine_keys: Vec<String> = self.fine_criteria.keys().cloned().collect();
631
632        let mapping = HierarchicalMapping {
633            name: self.name,
634            coarse_categories: self.coarse_categories,
635            fine_criteria: self.fine_criteria,
636            coarse_to_fine: self.coarse_to_fine,
637            fine_to_coarse: self.fine_to_coarse,
638            all_coarse_keys,
639            all_fine_keys,
640            negative_keys: self.negative_keys,
641        };
642        mapping.validate()?;
643        Ok(mapping)
644    }
645}
646
647#[cfg(test)]
648mod tests {
649    use super::*;
650
651    #[test]
652    fn test_banking77_preset_validity() {
653        let mapping = HierarchicalMapping::banking77();
654        assert_eq!(mapping.total_coarse_count(), 7);
655        assert_eq!(mapping.total_fine_count(), 77);
656        assert!(mapping.validate().is_ok());
657
658        // 大分類引き
659        let card_fines = mapping.fine_keys_for_coarse("card_ops").unwrap();
660        assert_eq!(card_fines.len(), 12);
661        assert!(card_fines.contains(&"activate_my_card".to_string()));
662
663        // 逆引き
664        assert_eq!(
665            mapping.coarse_for_fine("activate_my_card"),
666            Some("card_ops")
667        );
668        assert_eq!(
669            mapping.coarse_for_fine("transfer_timing"),
670            Some("transfers")
671        );
672    }
673
674    #[test]
675    fn test_validation_mece_violation() {
676        // 重複細分類キーがある場合
677        let mut builder = HierarchicalMapping::builder("InvalidMapping");
678        builder.add_category(
679            "cat1",
680            "カテゴリ 1",
681            vec![("item1", "アイテム 1"), ("dup_item", "重複アイテム")],
682        );
683        builder.add_category(
684            "cat2",
685            "カテゴリ 2",
686            vec![("item2", "アイテム 2"), ("dup_item", "重複アイテム")],
687        );
688        let res = builder.build();
689        assert!(res.is_err());
690        let err_msg = res.unwrap_err().to_string();
691        assert!(err_msg.contains("MECE 違反"));
692    }
693
694    #[test]
695    fn test_validation_empty_category() {
696        let mut builder = HierarchicalMapping::builder("EmptyCategory");
697        builder.add_category("cat1", "カテゴリ 1", vec![("item1", "アイテム 1")]);
698        builder.add_category("cat2", "カテゴリ 2", Vec::<(&str, &str)>::new());
699        let res = builder.build();
700        assert!(res.is_err());
701        let err_msg = res.unwrap_err().to_string();
702        assert!(err_msg.contains("空です"));
703    }
704
705    #[test]
706    fn test_negative_candidate_detection() {
707        let mapping = HierarchicalMapping::banking77();
708        assert!(mapping.is_negative_candidate(
709            "other_or_unsupported",
710            "上記のいずれにも該当しないその他の問い合わせ。"
711        ));
712        assert!(!mapping.is_negative_candidate("activate_my_card", "カード有効化。"));
713    }
714}