0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

判断特化AI「Jev」に「金運があがるカエル」の営業電話60本を仕分けさせてみた話

0
Last updated at Posted at 2026-09-20

TL;DR

  • 文章を生成しない判断特化AI Jev に、架空の営業電話ログ60本(正解ラベル付き)を判定させた
  • 「通話の結果はどれか(5択)」は、意地悪な通話24本を含めて 60/60 正解。5質問まとめて 中央値250ms、60本で 約0.6円
  • Noul(真偽の確率)は 0.7以上と答えたものが全部 true、Brier score 0.012。確率がちゃんと当てになる
  • ただし 「営業は6回以上しゃべったか」は 37/60 でコイン投げ並み。数えるのは本当にできない
  • 結論: 「読めばわかる判断」はJev、「数えればわかること」はコード。この線引きがすべて

やろうとしたこと

Qiitaの公式イベントで判断特化AI「Jev」を知り、TypeSafe AI の waitlist に登録したら 3時間ほどでAPIキーが届きました

が、正直に言うと、キーが来た時点で 何に使えるのかよくわかっていませんでした。迷路を解かせる? シフト表の最適化? ゲームのデバッグ? いろいろ考えたんですが、ドキュメントを読んで全部ボツに。Jevはこういうものなんですね。

  • 渡すのは state(判断したい対象のテキスト)と、型つきの質問
  • 返ってくるのは3種類だけ。Choice(選択肢から1つ)、Score(段階評価)、Noul(真偽の確率 0〜1)
  • 文章は一切生成しない。計算もしない。公式が「電卓ではない」と明言している

考えて解くAIではなく、読んで即答する「直感」の部品。探索や計算はコードの仕事。

となると向いているのは「同じ形の判断を大量にさばく」仕事で、思いついたのが 営業電話ログの仕分け でした。入口で断られたのか、担当不在だったのか、金額で断られたのか、アポは取れたのか。人が1本ずつ聞き直すのはつらいやつです。


構成

本物の通話ログは使えないので、完全フィクション で作りました。

  • 架電元: 株式会社開運堂本舗
  • 商材: 金運があがる置物 「黄金の福ガエル」(純金メッキ・高さ30cm・1体29万8千円)
  • ゴール: 社長との対面アポ

通話ログは Claude に仕様書を渡して 6カテゴリ×10本=60本 生成させ、正解ラベルも同時に付けさせています。各カテゴリ4本は hard(字面だけ追うと間違えそうな通話) です。

カテゴリ
門前払い 「今忙しい」「間に合ってます」「営業はすべてお断り」
担当不在 外出・会議・居留守
金額で断り 「その値段じゃ無理だなあ」
金額以外で断り 怪しい、招き猫がもうある、税理士に反対された
一度断ってからアポ 「5分だけだぞ。買わないからな」
すんなりアポ 「最近ツイてなくてさ、見てみたい」

hard の例はこんな感じです。

  • 「社長は今ちょっと忙しくて…」→ 実は 会社方針で営業電話は一切お断り(不在ではない)
  • 受話器の向こうから「いないって言っといて」と聞こえる 居留守
  • ずっと「ぼったくりじゃないの」と文句を言いながら「あさって3時に来てよ」と 約束する 社長

1通話に対して、次の5質問を 1リクエストにまとめて 投げます。公式の設計指針が「1質問=1つの狭い判断に分解せよ」なので、それに従いました。

from typesafe_sdk import Choice, Noul, Score

QUESTIONS = {
    "outcome": Choice(
        instructions="この営業電話は最終的にどういう結果で終わったか。",
        criteria={
            "gatekeeper_reject": "商品説明をほぼ聞いてもらえないまま、電話口で門前払いされた(...)",
            "absent": "社長や担当者が不在であることを理由に通話が終わった",
            "price_reject": "商品説明を聞いた上で、金額の高さを主な理由に断られた",
            "other_reject": "商品説明を聞いた上で、金額以外の理由で断られた",
            "appointment": "対面で訪問する約束(アポ)が取れた。途中で断られかけていても...",
        },
    ),
    "appointment": Noul(instructions="営業担当は、相手先を訪問する具体的な約束を取り付けたか。"),
    "initial_refusal": Noul(instructions="相手は通話中に一度でも、断る・電話を切ろうとする意思を示したか。..."),
    "reached_decision_maker": Noul(instructions="営業担当は、決裁者本人と直接会話できたか。"),
    "recall_priority": Score(
        instructions="この相手にもう一度営業電話をかける優先度はどのくらいか。",
        criteria=["再架電してはいけない...", "望み薄...", "再架電の価値がある...", "アポ獲得済み..."],
    ),
}

result = await client.system_one(transcript, QUESTIONS, model="jev-latest")

環境は uv add typesafe-sdk python-dotenv だけ。指示文も選択肢の説明も 全部日本語 で書いています。


やってみた結果

正答率

質問 全体 easy hard
通話の結果(5択) Choice 60/60 36/36 24/24
アポが取れたか Noul 60/60 36/36 24/24
一度でも断られたか Noul 58/60 36/36 22/24
決裁者と話せたか Noul 60/60 36/36 24/24
再架電の優先度(0〜3) Score 55/60 33/36 22/24

正直、もっと間違えると思っていました。 居留守も、文句を言いながらのアポも、「忙しい」が口実の門前払いも、全部正しく仕分けています。

たとえばこの不機嫌な運送会社の社長。

社長: カエルだあ? くだらねえ。そんなもんで事故が減りゃ苦労しねえよ。
(中略)
社長: ちっ……。水曜の朝8時半。トラック出し終わったあとなら事務所にいる。5分だけだぞ。買わないからな。
"outcome":         { "choice": "appointment", "confidence": 1.0 },
"appointment":     { "noul": 0.98 },
"initial_refusal": { "noul": 0.83 },
"recall_priority": { "score": 2.99, "confidence": 0.99 }

「買わないからな」と言われてもアポはアポ。パースも正規表現もなしで、この形のJSONがそのまま返ってきます。

60本ぶんの通話ログ・正解ラベル・Jevの回答は、1通話ずつ並べて見られるようにしてあります。「誤答のある通話だけ」で絞ると、どこで間違えたかがすぐわかります。

速度とコスト

項目 実測
レイテンシ(5質問/リクエスト) 中央値 250ms、p95 511ms
60本の処理時間(4並列) 4.2秒
入力トークン 平均1,517/通話、合計91,001
料金($0.042/Mtok、出力無料) 60本で約 $0.004(0.6円)。1万通話でも100円くらい

確率は当てになるのか

Noul の3問ぶん(180判断)をまとめて、「Jevが言った確率」と「実際に true だった割合」を並べました。

Noul の値 件数 実際に true だった割合
0.0–0.1 75 0.00
0.1–0.3 6 0.17
0.3–0.5 2 0.00
0.5–0.7 3 0.67
0.7–0.9 8 1.00
0.9–1.0 86 1.00

Brier score は 0.012。161/180 が両端に寄っていて、間違えた2件はどちらも 0.26 と 0.68 という「自信なさげ」な値 でした。つまり「0.2〜0.8 のものだけ人が見る」というルールにすれば、180判断のうち8件をチェックするだけ で誤判定を全部拾えます。confidence でルーティングせよ、という公式の推しパターンはちゃんと機能しそうです。


間違えたところ

「今週は忙しい」を断りと読んだ

院長: うーん、今週は忙しいんですよ。学会の準備もあって、ほんとに時間が取れなくて。
院長: だから来週にしてください。来週の木曜は午後休診なんで、その日なら。

これは日程調整であって断りではないんですが、「一度でも断られたか」に 0.68 と回答。公式の弱点リストにある 「字義通りに読む。含みは読まない」 がそのまま出ました。

正解ラベルのほうが怪しかった

もう1件の誤答は「ぼったくりじゃないの」と文句を言い続けてアポに応じた居酒屋の社長。正解ラベルは「断られた=true」、Jevは 0.26。読み返すと 社長は一度も断っていない んですよね。ラベルを付けた側(Claude)のミスで、Jevのほうが正しい気がします。後出しでラベルは直していないので、数字は 58/60 のままにしてあります。

Score は悲観的に寄る

再架電優先度の誤答5件のうち 4件は「1: 望み薄」を「0: 再架電NG」と判定 したものでした。「霊感商法みたいで怪しい」「信仰と合わないので」とはっきり断られた通話を、「二度とかけるな」と同格に読んでいます。これはルーブリックの書き方で直せる余地がありそうです。なお confidence 0.7 以上に絞ると 49/50 正解でした。


わざと苦手なことを聞いてみた

結果が良すぎて不安になったので、公式が苦手と明言している カウント時刻の比較 を同じログで聞いてみました。どちらも正解はコードや目視で機械的に決まります。

質問 結果 全部同じ答えにした場合
営業担当の発話は6回以上あるか 37/60(Brier 0.275) 36/60
約束した訪問時刻は14時より遅いか 20/20(Brier 0.001) 13/20

カウントは完全に崩壊 しました。Brier 0.275 は「常に0.5と答える」(0.25) より悪く、6〜7回の通話をほぼ全部「6回未満」と答えています。transcript.count("営業:") の1行で済む話なので、これは聞くほうが悪い。

一方で時刻比較は、「3時に本店に来てよ」「朝8時半」「午後2時」と表記がバラバラなのに 全問正解。弱点リストに載っていても、この程度なら普通にいけました。苦手の出方には濃淡があるので、自分のデータで試すのが一番 ですね。


学んだこと

  • Jevに渡すのは「読めばわかる判断」だけにする。 数える・計算する・並べ替えるはコードでやる。今回なら、Jevが出した recall_priorityappointment を使って架電リストをソートするのはPythonの仕事
  • 質問は分解するほど強い。 「この通話を分析して」ではなく、5つの狭い質問にしたから検証もできたし、どこで間違えたかも特定できた
  • 確率つきで返ってくるのが想像以上に便利。 LLMに「自信度も出して」と頼むのとは違って、0.7以上は実際に全部当たっていた。人に回す線を数字で引ける
  • 日本語の指示文でそのまま動く。 英語に直す必要は今回感じなかった
  • ⚠️ ただし今回のデータは LLMが書いた「きれいな」通話ログ です。実際の音声認識テキストは言い間違いや誤変換だらけのはずで、公式も「ノイズが多いと精度が落ちる」と書いています。100%という数字は割り引いて読んでください

おわりに

「何に使えるのかわからない」から始まりましたが、触ってみての感想はこれです。

LLMの代わりではなく、if 文の条件式に「文章を読む力」を足す部品。

250ms・1通話0.01円で確率つきの判断が返ってくるなら、「全件LLMに読ませるのは高いし遅いから諦めていた仕分け」が現実的になります。逆に、迷路や最適化みたいな「考える・計算する」仕事を渡すと、発話カウントの実験のようにあっさり崩れます。

検証コードと通話ログ60本(福ガエルの営業トーク入り)は GitHub に置いてあります。APIキーがあれば uv run だけで再現できます。

「うちのログではこうだった」「こういう質問は崩れた」などあれば、ぜひコメントで教えてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?