文档检索增强生成系统

一个基于PostgreSQL和pgvector的本地Node.js RAG原型,用于内部文档检索和聊天交互。

  • by AlexandreLeonetti
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 AlexandreLeonetti/mcp-docs-rag

使用说明

MCP文件RAG

用于内部文档的本地Node.js RAG原型,现在由PostgreSQL+pgvector支持,而不是 data/index.json.

迁移有意保持增量:

  • docs仍然住在 docs/
  • npm run build-index 仍然可以读取、分块、丰富和索引文档
  • CLI聊天和MCP工具仍然使用相同的检索入口点
  • 单独的 web/ Next.js应用程序现在提供了一个简单的浏览器聊天UI
  • 检索仍然遵循相同的思维模式:词汇优先,可选的语义重新排序,然后是元数据感知的重新排序

Postgres MVP架构

src/
├── db/
│   ├── client.js
│   ├── init.js
│   ├── migrations/
│   └── schema.sql
├── indexing/
│   ├── build-index.js
│   ├── chunker.js
│   ├── embedder-client.js
│   ├── indexing.js
│   └── postgres-writer.js
├── retrieval/
│   ├── lexical-search.js
│   ├── semantic-search.js
│   ├── hybrid-search.js
│   ├── query-analysis.js
│   └── retrieval.js
├── cli/
│   └── chat.js
├── chat/
│   └── run-chat-turn.js
└── mcp/
    └── server.js

web/
├── app/
│   ├── api/chat/route.ts
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
└── components/
    ├── ChatShell.tsx
    ├── Composer.tsx
    └── MessageBubble.tsx

Postgres现在存储:

  • 文件
  • 块
  • 块元数据作为 jsonb
  • 全文搜索数据通过 tsvector
  • 通过以下方式进行可选块嵌入 pgvector

架构概述

documents 每个源文件存储一行:

  • source_path
  • filename
  • title
  • doc_type
  • content_hash
  • metadata_json

chunks 存储检索单元:

  • document_id
  • chunk_index
  • chunk_id
  • content
  • token_count
  • section_heading
  • date
  • month
  • metadata_json
  • content_tsv
  • embedding

目前的指标包括:

  • GIN指数 chunks.content_tsv
  • 文档/块查找字段上的btree索引
  • 独特的 chunk_id

对于此MVP,向量ANN索引被有意推迟。

检索流程

高水位流量:

  1. 分析查询并保留现有的模式/过滤逻辑
  2. 使用全文搜索从Postgres中获取候选词汇 chunks.content_tsv
  3. 使用现有的元数据感知词法逻辑重新存储应用程序代码中的候选项
  4. 可选地嵌入查询并将其与存储的块嵌入进行比较
  5. 结合词汇和语义得分
  6. 应用现有的重新排序和广义摘要逻辑

当本地嵌入被禁用或不可用时,检索将退回到仅词法模式。

设置

要求:

  • Node.js 18+
  • Docker用于本地Postgres
  • 可选的 DEEPSEEK_API_KEY 用于模型生成的聊天答案

安装依赖项:

npm install

示例 .env:

DATABASE_URL=postgresql://mcp:mcp@localhost:5432/mcp_docs_rag
PGHOST=localhost
PGPORT=5432
PGDATABASE=mcp_docs_rag
PGUSER=mcp
PGPASSWORD=mcp

LOG_DIR=./logs

# Optional local embeddings for indexing + hybrid retrieval
ENABLE_LOCAL_EMBEDDINGS=false
LOCAL_EMBEDDING_MODEL=Xenova/all-MiniLM-L6-v2
# LOCAL_EMBEDDING_BATCH_SIZE=16

# Optional retrieval debug lines in CLI output
# RETRIEVAL_DEBUG=1

# Optional DeepSeek answer generation
# DEEPSEEK_API_KEY=your_deepseek_api_key

启动Postgres

用pgvector启动PostgreSQL:

docker compose up -d

初始化架构:

npm run db:init

迁移运行器从以下位置应用SQL文件 src/db/migrations.

构建索引

将文档重新索引到Postgres中:

npm run build-index

启用本地嵌入后:

ENABLE_LOCAL_EMBEDDINGS=true npm run build-index

索引现在可以做到这一点:

  1. 从以下位置读取源文件 docs/
  2. 提取前体和推断元数据
  3. 使用现有的分块规则对内容进行分块
  4. 启用时计算嵌入
  5. 打乱文档行
  6. 删除并重新创建该文档的块行,以便重新运行时不会重复数据

运行聊天

npm run chat

使用检索调试:

RETRIEVAL_DEBUG=1 npm run chat

启用本地嵌入后:

ENABLE_LOCAL_EMBEDDINGS=true RETRIEVAL_DEBUG=1 npm run chat

MCP工具名称保持不变:

search_internal_docs

运行网络聊天

浏览器UI位于单独的 web/ 应用程序,并重用与CLI相同的后端聊天回合助手。

安装web应用依赖项:

cd web
npm install

启动web应用程序:

cd web
npm run dev

或者从repo根目录:

npm run web

然后打开 http://localhost:3000.

web应用程序添加了:

  • 一个聊天页面
  • 一条API路线位于 web/app/api/chat/route.ts
  • 一座薄薄的桥 src/chat/run-chat-turn.js

现有的CLI工作流保持不变,仍然可以使用 npm run chat.

运行Eval

npm run eval

本地设置示例

docker compose up -d
npm install
npm run db:init
npm run build-index
npm run chat

# optional web UI
cd web && npm install && npm run dev

注释和MVP限制

  • Postgres现在是主存储层。旧的JSON索引不再是主要的运行时信息来源。
  • 全文搜索目前对块内容进行索引,而元数据感知的词汇增强仍然发生在应用程序代码中。
  • 嵌入存储假定当前本地模型维度为384。
  • 该模式是基于迁移的,但有意轻量级。
  • 近似向量索引、高级元数据过滤器和零停机时间重新索引超出了此MVP的范围。
  • web UI有意与后端隔离 web/ 并通过一个小型共享助手调用现有的检索/应答流。
docs/
  ↓
src/indexing/chunker.js
  ↓
src/indexing/embedder-client.js
  ↓
src/indexing/postgres-writer.js
  ↓
Postgres
  ├─ documents
  └─ chunks (+ tsvector + pgvector)

user question
  ↓
src/retrieval/lexical-search.js
  ↓
src/retrieval/semantic-search.js
  ↓
src/retrieval/hybrid-search.js
  ↓
src/retrieval/retrieval.js
  ↓
src/chat/run-chat-turn.js
  ↓
MCP / CLI chat / Next.js web API