はじめに
AI 搭載のチャットボットが進化する中で、Retrieval-Augmented Generation (RAG) は 精度とコンテキスト理解を向上させる重要な手法 となっています。本記事では、PostgreSQL のベクトル検索拡張機能である pgvector を用いて、RAG ベースの ヘルプデスクチャットボット を実装する方法を紹介します。
ヘルプデスクにおける 長文ドキュメント の処理では、チャットボットが単に一般的な回答をするのではなく、ユーザーの質問に最も関連する情報を適切に検索 することが重要になります。本記事では、長い文章を分割する チャンク化戦略 による検索精度向上についても解説します。
なぜヘルプデスクチャットボットに RAG + pgvector を使うのか?
従来のチャットボットは 事前学習された知識のみ に基づくため、動的な問い合わせに対応しにくい という制約があります。一方、RAG ベースのチャットボット では、以下のようなメリットがあります。
✅ リアルタイムでヘルプデスクの情報を検索可能
✅ 外部知識を活用し、回答の精度を向上
✅ ハルシネーション(事実に基づかない回答)を抑制
pgvector を選ぶ理由
FAISS、Pinecone、Weaviate などのベクトルデータベースが存在しますが、pgvector には以下の利点があります。
- ✅ PostgreSQL にネイティブ対応しており、既存のデータベースと統合しやすい
- ✅ Approximate Nearest Neighbor (ANN) 検索をサポートし、高速な検索が可能
- ✅ 適切なインデックス戦略を用いることでスケール可能
実装: RAG + pgvector によるヘルプデスクチャットボット
1. PostgreSQL に pgvector をセットアップ
まず、PostgreSQL で pgvector
拡張機能を有効化します。
CREATE EXTENSION vector;
次に、ベクトル埋め込みを格納するヘルプデスクドキュメントのテーブルを作成します。
CREATE TABLE helpdesk_articles (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
embedding vector(1536) -- OpenAIの ada-002 の埋め込みサイズ
);
2. ヘルプデスク記事の埋め込みを生成
OpenAI の text-embedding-ada-002
モデルを使用して、各ドキュメントのベクトル埋め込みを生成します。
import { OpenAI } from "openai";
import { Client } from "pg";
const openai = new OpenAI({ apiKey: "YOUR_OPENAI_API_KEY" });
const db = new Client({ connectionString: "YOUR_DATABASE_URL" });
async function generateEmbedding(text: string) {
const response = await openai.embeddings.create({
model: "text-embedding-ada-002",
input: text,
});
return response.data[0].embedding;
}
async function storeEmbedding(id: number, title: string, content: string) {
await db.connect();
const embedding = await generateEmbedding(content);
await db.query(
"INSERT INTO helpdesk_articles (id, title, content, embedding) VALUES ($1, $2, $3, $4)",
[id, title, content, embedding]
);
await db.end();
}
この処理により、ヘルプデスク記事の ベクトル表現 を PostgreSQL に格納し、高速な類似検索が可能になります。
3. 長文コンテンツの処理: チャンク化戦略
長文ドキュメントをそのまま検索対象にすると、非効率な検索結果 になりがちです。そのため、チャンク化(分割) することで、より適切な検索が可能になります。
function splitText(text: string, chunkSize: number = 512): string[] {
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
let chunks: string[] = [];
let currentChunk = "";
for (const sentence of sentences) {
if (currentChunk.length + sentence.length > chunkSize) {
chunks.push(currentChunk.trim());
currentChunk = sentence;
} else {
currentChunk += " " + sentence;
}
}
if (currentChunk) chunks.push(currentChunk.trim());
return chunks;
}
このように チャンク化 しておくことで、検索精度が向上します。
4. ベクトル検索を用いた情報取得
ユーザーの問い合わせがあった際に:
- クエリを ベクトル埋め込み に変換
- pgvector データベース で最も近いチャンクを検索
- 上位の結果を取得し、GPT にコンテキストとして渡す
類似検索の SQL クエリ
SELECT id, title, content, embedding <=> $1 AS distance
FROM helpdesk_articles
ORDER BY distance
LIMIT 3;
TypeScript での実装:
async function searchHelpdesk(query: string) {
await db.connect();
const queryEmbedding = await generateEmbedding(query);
const result = await db.query(
"SELECT title, content FROM helpdesk_articles ORDER BY embedding <=> $1 LIMIT 3",
[queryEmbedding]
);
await db.end();
return result.rows;
}
5. RAG パイプライン: 最終的な回答を生成
取得した関連ドキュメントを コンテキスト として OpenAI の GPT モデルに渡します。
async function generateResponse(userQuery: string) {
const relevantDocs = await searchHelpdesk(userQuery);
const context = relevantDocs.map(doc => `${doc.title}: ${doc.content}`).join("\n\n");
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{ role: "system", content: "あなたはヘルプデスクのサポートエージェントです。" },
{ role: "user", content: `ユーザーの質問: ${userQuery}` },
{ role: "assistant", content: `関連情報:\n${context}` }
],
});
return response.choices[0].message.content;
}
まとめ
pgvector を活用した RAG システム を導入することで、ヘルプデスクチャットボットの 精度とコンテキスト理解が大幅に向上 しました。特に チャンク化とベクトル検索 を組み合わせることで、より適切な回答が可能になりました。
💬 ぜひ pgvector を活用した RAG の実装について、あなたの経験をシェアしてください!