pgvector を活用した RAG システムによるヘルプデスクチャットボットの実装


## はじめに

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` 拡張機能を有効化します。
```sql
CREATE EXTENSION vector;
```
次に、ベクトル埋め込みを格納するヘルプデスクドキュメントのテーブルを作成します。
```sql
CREATE TABLE helpdesk_articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    content TEXT,
    embedding vector(1536) -- OpenAIの ada-002 の埋め込みサイズ
);
```

---

### **2. ヘルプデスク記事の埋め込みを生成**

OpenAI の `text-embedding-ada-002` モデルを使用して、各ドキュメントのベクトル埋め込みを生成します。
```ts
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. 長文コンテンツの処理: チャンク化戦略**

長文ドキュメントをそのまま検索対象にすると、**非効率な検索結果** になりがちです。そのため、**チャンク化（分割）** することで、より適切な検索が可能になります。

```ts
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. ベクトル検索を用いた情報取得**

ユーザーの問い合わせがあった際に:
1. クエリを **ベクトル埋め込み** に変換
2. **pgvector データベース** で最も近いチャンクを検索
3. 上位の結果を取得し、GPT にコンテキストとして渡す

#### **類似検索の SQL クエリ**
```sql
SELECT id, title, content, embedding <=> $1 AS distance
FROM helpdesk_articles
ORDER BY distance
LIMIT 3;
```

TypeScript での実装:
```ts
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 モデルに渡します。
```ts
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** の実装について、あなたの経験をシェアしてください！