「あのお客様、去年似たような問い合わせをしていた気がするけど、どの担当者とのやり取りだったか思い出せない」——受託でお客様対応の仕組みをお預かりしていると、こういう相談を年に何度もいただきます。メールとチャットと電話メモが別々の場所にあり、担当者の記憶だけが頼りになっている状態です。
先に、この記事の要点をまとめます。
- **顧客対応履歴の検索が難しいのは、問い合わせの言い回しが毎回違うから。**キーワードが一語一句一致しないと探せない検索では「先月と同じ内容」の過去ログを取りこぼす
- ベクトル検索(意味で探す仕組み)を顧客対話ログに適用すると、表現が違っても近い内容の過去のやり取りを拾える。ただし、社内マニュアル検索とはデータの性質が違うため、同じ設計をそのまま流用すると精度が落ちる
- 着手は「特定の窓口・直近1年分」のような絞った範囲から始め、使いながら対象を広げるのが現実的
ただし、1つ注意点があります。顧客対話ログは社内マニュアルと違って「個人情報を含む」「担当者ごとに書き方の癖がある」「未解決のまま放置された相談も混ざる」という固有の難しさがあります。ここを甘く見て設計すると、検索結果に個人情報が漏れて出てきたり、精度の低い検索になったりします。後半で具体的な対処を説明します。
本記事は2026年8月時点の情報をもとに、Next.js × ベクトルDBで顧客対応履歴を検索可能にする設計を、エンジニアと発注側の両方の目線で整理します。中小企業で複数の担当者が同じお客様に対応する体制を組んでいる情シス担当者の方へ、特に読んでいただきたい内容です。
なぜ顧客対応履歴の検索は難しいのか?
同じ内容の問い合わせでも、聞き方が毎回違うため、キーワードの一致だけを頼りにした検索では過去ログを見つけられないことが多いからです。
たとえば「請求書の宛名を間違えた」という相談は、「請求書 宛名 間違い」と検索して探すことを想定しがちですが、実際のお客様や担当者の記録には次のような表現で残っています。
- 「発行済みの請求書、会社名の表記を直してほしいとのご要望」
- 「インボイスの宛先訂正の件で折り返し予定」
- 「請求先名義変更の対応、経理部に確認中」
言葉としては一致する単語がほとんどありません。人間なら「同じ話だ」とすぐ分かりますが、キーワード検索のシステムはそう判断できません。この結果、「同じような相談が過去にもあったはずなのに、検索しても出てこない」という状態が起き、毎回ゼロから調べ直すことになります。
この問題が実害になるのは、担当者が異動・退職したときです。属人的な記憶に依存していた顧客対応の経緯が、検索できない形でログに埋もれたまま引き継がれなくなります。新しく担当になった人は、同じ質問をお客様に何度もすることになり、「前にも同じことを話したのに」という不満につながります。
ベクトル検索は何を解決するのか?
単語の一致ではなく「意味の近さ」で検索するため、表現が違っても同じ内容のやり取りを見つけられます。
ベクトル検索は、文章を「意味を数値化した座標(ベクトル)」に変換して保存し、検索クエリも同じ方法で座標に変換した上で、近い座標を持つ文章を探す仕組みです。「請求書の宛名を間違えた」と「インボイスの宛先訂正」は、単語こそ違いますが意味的には近い場所に配置されるため、片方で検索してももう片方が見つかります。

この仕組み自体は、社内マニュアルをAIで検索可能にするで扱った社内文書検索と技術的な土台は共通しています。ただし、対象データが「マニュアル」から「顧客対話ログ」に変わることで、設計上の注意点がいくつか変わります。
マニュアル検索と顧客対応履歴検索は何が違うのか?
マニュアルは「正解が1つに定まった静的な文書」なのに対し、顧客対応履歴は「同じお客様の状況が時系列で変化していく動的な記録」だという点が最大の違いです。
マニュアル検索では、最新版の文書さえ正しく索引化されていれば、検索結果の信頼性は保たれます。一方、顧客対応履歴の検索では、次のような違いを意識した設計が必要です。
- 時系列の重み付けが要る:3年前の相談と先月の相談が同じ重みで検索結果に並ぶと、担当者は混乱します。基本は新しいやり取りを優先し、古いものは参考情報として下位に表示する設計にします
- 担当者・部署のメタデータが不可欠:「誰が」「いつ」「どの案件で」対応したかが分からない検索結果は、業務では使い物になりません。マニュアル検索以上に、メタデータの設計比重が上がります
- 未解決・保留中の案件が混在する:マニュアルは公開時点で完結していますが、顧客対応ログには「まだ回答待ち」の相談も混ざります。検索結果にステータス(対応済み/保留中)を表示しないと、解決済みの話だと誤認するリスクがあります
- 個人情報を含む前提で設計する:氏名・連絡先・場合によっては契約内容の詳細が本文に含まれます。マニュアル検索より一段厳しいアクセス制御が前提になります
個人情報を含むデータで検索を作るときの注意点は?
検索結果に表示する範囲を「役職・部署に応じて絞る」ことと、外部APIに送るデータから個人情報を除去する下処理の両方が必要です。
顧客対話ログをベクトル検索の対象にする際、最初に決めるべきはアクセス制御の設計です。全社員が全顧客の対応履歴を検索できる状態は、多くの企業でセキュリティポリシー上望ましくありません。担当している顧客・部署の範囲に検索結果を絞る仕組みが必要です。これは社内システムの権限設計で説明したRBAC(役割に基づくアクセス制御)の考え方をそのまま応用できます。
もう1つの論点が、外部のAI APIにデータを送る場合の下処理です。ベクトル化や要約生成でAnthropicやOpenAIのAPIを呼ぶ設計にする場合、次のような配慮が実務上求められます。
- 契約書・利用規約でAPI提供元がデータを学習に使わない条項になっているかを確認する(多くの法人向けAPIプランはデフォルトで学習利用オプトアウトですが、契約内容は必ず確認します)
- 氏名・電話番号など、検索精度に不要な個人情報はベクトル化前にマスキングする(メタデータとして別テーブルに保持し、表示時だけ突合する設計が安全です)
- 監査ログを残す:誰がいつどの顧客の対応履歴を検索したかの記録を残しておくと、後から問題が起きた際の追跡ができます
この3点は「検索精度を上げるための工夫」ではなく「作る前提として最初から組み込むべき設計」です。後から追加しようとすると、既存データの再処理が必要になり手戻りが大きくなります。
どこから着手するのが現実的か?
特定の1窓口・直近1年分のデータからスモールスタートし、検索してみた手応えを確認してから対象範囲を広げるのが現実的です。
全部署・全期間のデータを一度にベクトル化しようとすると、次の理由でうまくいかないことが多いです。
- データの形式がバラバラ:メールはテキスト、チャットはSaaSのエクスポート形式、電話メモは担当者ごとの手書きメモとフォーマットが違い、統合の下処理だけで想定以上の工数がかかる
- 精度検証がしづらい:一度に大量のデータを入れると、検索結果の質が良いのか悪いのか判断する基準が作りづらい
- 費用感が見えにくい:ベクトル化・保存・検索のいずれも処理量に応じて費用が発生するため、小さく始めて費用感を掴んでから広げる方が予算計画を立てやすい
実務では、次のような順序で進めるプロジェクトが多い印象です。
- 1つの窓口(たとえばカスタマーサポート窓口)に絞り、直近1年分のメール・チャットログだけを対象にする
- 検索UIを最小構成で作り、実際の担当者に1〜2週間使ってもらう
- 「見つからなかった」フィードバックを集めて、チャンク(文章の分割単位)の粒度やメタデータの設計を調整する
- 手応えが確認できたら、対象期間や他の窓口(営業部門の商談ログなど)に広げる
私たちが受託で関わった案件の範囲でも、最初から全社統合検索を目指すよりも、この段階的な進め方の方が結果的に完成までの期間が短くなる傾向があります。範囲を絞ることで、精度の調整サイクルを何度も回せるためです。
なお、検索ではなく「複数のやり取りをまたいで一つの回答に要約させたい」というニーズもあります。たとえば「このお客様、これまでどんな相談をしてきたか」を1つのサマリーとして見たい場合は、検索結果を並べるだけでなく、AI SDKを使った要約生成を組み合わせる設計になります。この使い分けは社内規定を参照して回答するチャットボットで扱った「検索」と「対話・要約」の役割分担と同じ考え方です。
Next.jsではどう構成するか?
顧客対応データの取り込み処理と、検索・表示の画面を分離し、取り込み側はバッチ処理、検索側はServer Componentsで完結させる構成が扱いやすいです。
構成要素を分けて考えると次のようになります。
- データ取り込み層:メール・チャットのAPI連携やCSVインポートから、対話ログを取得しチャンク分割・ベクトル化してDBに保存する。リアルタイム性は不要なため、日次や数時間おきのバッチ処理で十分なケースがほとんどです
- 検索・表示層:担当者が検索する画面。Next.jsのServer Componentsで検索クエリを受け取り、ベクトルDBに問い合わせて結果を表示します。アクセス制御はここでRBACの権限情報と突合させます
- 要約生成層(任意):検索結果をさらに1つの回答にまとめたい場合、AI SDKで生成AIの呼び出しを行う層を別に持たせます
この3層を最初から密結合にせず、取り込み層だけ先に作って手動でデータを流し込み、検索層で精度を確認してから要約生成層を足す、という順番で進めると、途中でどこに問題があるか切り分けやすくなります。取り込み層のデータ品質が悪いまま検索層を作り込んでも、後から手戻りになるだけです。
検索・表示層のRoute Handlerは、次のように「クエリのベクトル化」「検索」「アクセス制御での絞り込み」の3ステップで組むのが基本形です。
// app/api/customer-search/route.ts のイメージ(簡略化)
import { NextResponse } from 'next/server';
import { getSessionUser } from '@/lib/auth';
export async function POST(request: Request) {
const user = await getSessionUser(request);
const { query } = await request.json();
// 1. 検索クエリをベクトル化
const embedding = await embeddingClient.create({ input: query });
// 2. ベクトルDBで類似する対話ログを検索
const results = await vectorDB.query({
vector: embedding.data[0].embedding,
topK: 10,
// 3. RBACの担当範囲でメタデータをあらかじめ絞り込む
filter: { assignedTeam: { $in: user.accessibleTeams } },
});
return NextResponse.json({
results: results.map((r) => ({
snippet: r.metadata.snippet,
customerName: r.metadata.customerName,
status: r.metadata.status, // 対応済み / 保留中
updatedAt: r.metadata.updatedAt,
})),
});
}
ポイントは、ベクトルDBへの問い合わせと同時に filter でアクセス制御をかけている点です。検索結果を取得してからアプリ側でフィルタリングする実装も見かけますが、それだと権限外のデータが一瞬でもサーバーのメモリに乗ってしまいます。問い合わせの時点で絞り込む設計の方が安全です。
よくある失敗パターン
実際にこの手の仕組みを作る中で、次のような失敗をよく見かけます。
- チャンクの分割単位が対話ログに合っていない:マニュアル向けの「数百文字ごとに機械的に分割」する方法をそのまま流用すると、1つの相談のやり取りが複数のチャンクにバラバラに分割され、文脈が失われます。対話ログは「1件の問い合わせ=1チャンク」を基本単位にする方が精度が安定します
- ステータス表示を後回しにする:検索結果に「対応済み」か「保留中」かの表示を付けずにリリースしてしまい、担当者が解決済みだと誤認してお客様への回答が止まってしまうケースがあります。ステータス表示は最初のリリースから含めるべき項目です
- アクセス制御をUI側だけで実装する:画面上は担当外の顧客データを非表示にしていても、APIのレスポンス自体に権限外データが含まれていると、開発者ツールから閲覧できてしまいます。フィルタリングは必ずサーバー側・DBへの問い合わせ時点で行います
まとめ
この記事で持ち帰れることは、次の3点です。
- 顧客対応履歴の検索が使われない理由と、ベクトル検索がそれをどう解決するかを説明できる(表現の違いを意味で吸収する仕組みだと理解した上で判断できる)
- 社内マニュアル検索と顧客対応履歴検索の設計上の違い(時系列の重み・メタデータ・未解決案件の扱い・個人情報保護)を区別して考えられる
- 着手範囲の決め方(1窓口・直近1年分から始めて広げる進め方)が分かり、いきなり全社統合を目指さなくてよいと判断できる
今日ひとりでできることを1つ挙げるなら、直近1ヶ月分の顧客対応履歴(メールでもチャットログでも構いません)を眺めて、「同じような相談が過去にもあった気がするけれど、探すのに時間がかかった」というケースが実際に何件あったか数えてみてください。この件数が、検索の仕組みに投資する価値があるかどうかの判断材料になります。
ゼットリンカーでは、中小企業向けのNext.jsフルスクラッチ受託で、顧客対応履歴の検索・要約のような業務システムをAI駆動開発(Claude Code)を実装工程に組み込みながら設計しています。「うちの顧客対応ログは検索できる状態にできるのか」「まずどの窓口から着手すべきか」といった個別の判断からで構いませんので、お問い合わせからご相談ください。
よくある質問
Q. ベクトル検索を導入すれば、社内マニュアル検索と同じ仕組みを流用できますか?
技術的な土台(文書をベクトル化して意味で検索する仕組み)は共通していますが、そのまま流用すると精度が落ちます。顧客対応履歴は時系列の重み付け・担当者や部署のメタデータ・未解決案件の扱い・個人情報保護といった、マニュアル検索には無い設計要素が必要になるため、対象データに合わせた調整が必要です。
Q. 顧客対応履歴を外部のAI APIに送っても個人情報の扱いは大丈夫ですか?
多くの法人向けAPIプランはデフォルトで学習利用がオプトアウトされていますが、契約内容の確認は必須です。あわせて、氏名・電話番号などの個人情報はベクトル化前にマスキングし、メタデータとして別に保持して表示時だけ突合する設計にすると、検索精度への影響を抑えながらリスクを下げられます。
Q. どのくらいのデータ量から検索の仕組みを作る意味がありますか?
明確な下限はありませんが、1つの窓口の直近1年分のような、絞った範囲から始めるのが現実的です。全社・全期間のデータを一度に統合しようとすると、データ形式の統一だけで工数がかさみ、検索精度の検証もしづらくなります。小さく始めて手応えを確認し、対象を広げる進め方の方が完成までの期間が短くなる傾向があります。
Q. 検索と要約(チャットボット)、どちらを先に作るべきですか?
過去の対応履歴を「探す」ニーズが主なら検索を、複数のやり取りをまたいで「1つの回答にまとめたい」ニーズが主なら要約生成を優先します。多くの現場では検索の精度を先に固めてから、必要に応じて要約生成層を追加する順番が扱いやすいです。
本記事は Next.js 16.x 時点の情報です
最終更新:2026年8月11日