poc节点rag-mcp
一个轻量级的纯Node.js POC,演示了:
- 检索增强生成 (摄取PDF/文本文档、块、嵌入、存储向量、检索top-k)
- MCP风格的工具调用 通过HTTP(
/mcp) - 政策问答 (检索政策文本+向LLM询问引用的答案)
- 普通LLM聊天 (无需检索即可直接进行LLM交互)
- 配置生成 从简单的英语规则
- 交互式CLI聊天UI
______________________________________________________________________
1) 高级设计
该项目有意采用模块化设计,易于推理:
- RAG层(
src/rag)
- 从以下位置读取文件 docs/ - 解析PDF/文本 - 分割成块 - 生成嵌入 - 在本地存储/查询向量 vectra 索引
- 工具层(
src/mcp/tools)
- retrieval:语义查找+带引用的法学硕士基础答案 - configGenerator:可选检索+LLM提示->JSON配置
- MCP HTTP层(
src/mcp/server.js)
- 暴露 POST /mcp 用于工具调用 - 暴露 GET /health
- UI层(
src/ui/chat-ui.js)
- CLI提示流 - 使用axios通过HTTP调用MCP工具
这种分离允许您以最小的更改交换内部(嵌入提供程序、向量DB、LLM端点)。
______________________________________________________________________
2) 嵌入提供者(切换方式 .env 旗帜)
您可以通过设置来切换嵌入后端 EMBEDDING_PROVIDER 在 .env:
natural(默认):通过npm包进行基于哈希的本地轻量级嵌入naturalxenova:通过以下方式嵌入本地变压器@xenova/transformers使用Xenova/all-MiniLM-L6-v2api:调用您的嵌入端点(LLM_API_BASE/embeddings)
推荐默认值
- 无外部依赖性:
EMBEDDING_PROVIDER=natural - 对于更高质量的本地嵌入:
EMBEDDING_PROVIDER=xenova
重要提示: 如果在索引后更改嵌入提供程序,请清除 vector-index/ 并重新摄取文档以保持向量维度的一致性。______________________________________________________________________
3) 项目结构和文件责任
poc-node-rag-mcp/
├── src/
│ ├── config/
│ │ └── index.js # Loads and exports env-driven config
│ ├── rag/
│ │ ├── index.js # ingestDocument, ingestDocsFolder, retrieve, getIndex
│ │ └── utils.js # chunkText helper
│ ├── mcp/
│ │ ├── server.js # HTTP MCP gateway (/mcp, /health)
│ │ ├── tools/
│ │ │ ├── retrieval.js # retrieval tool definition
│ │ │ ├── configGenerator.js# config generation tool definition
│ │ │ ├── chat.js # general LLM chat tool
│ │ │ └── index.js # allTools export
│ │ └── utils.js # Zod schemas for tool input validation
│ ├── ui/
│ │ └── chat-ui.js # CLI chatbot-like interface
│ ├── utils/
│ │ └── api.js # embedText, localEmbed, xenovaEmbed, llmComplete
│ └── index.js # startup orchestration (optional ingest + start server)
├── docs/ # input docs (pdf/txt/md)
├── vector-index/ # local vectra index (gitignored)
├── .env.example
├── .gitignore
├── package.json
└── README.md______________________________________________________________________
4) 功能级别说明(简易地图)
src/index.js
ensureDirectories()
- 确保 docs/ 和 vector-index/ 存在。
run()
- 阅读 --ingest 旗帜。 - 可选地摄取文件。 - 启动MCP HTTP服务器。
src/rag/index.js
getIndex()
- 懒散地初始化本地 vectra 指数。
parseDocument(filePath)
- 用途 pdf-parse PDF;UTF-8用于文本/标记。
ingestDocument(filePath)
- 解析->块->嵌入->将每个块插入索引。
ingestDocsFolder(folderPath)
- 查找支持的文件并按顺序摄取。
retrieve(query, topK)
- 嵌入查询并返回最匹配项。
src/utils/api.js
localEmbed(text, dimensions)
- 将Tokenize+stem+hash转化为密集的固定向量,进行归一化处理。
xenovaEmbed(text)
- 用途 @xenova/transformers 特征提取流水线(Xenova/all-MiniLM-L6-v2).
embedText(input)
- 交换机提供商基于 EMBEDDING_PROVIDER (natural, xenova, api).
llmComplete({ prompt, systemPrompt })
- 调用聊天完成端点以生成配置输出。
src/mcp/server.js
startMcpServer(port)
- 启动HTTP服务器。 - 手柄 POST /mcp { tool, arguments }. - 验证工具是否存在并执行工具处理程序。
src/mcp/tools/retrieval.js
handler(args)
- 使用Zod进行验证。 - 呼叫 retrieve 以获得顶级块。 - 将用户查询+检索到的策略文本发送到LLM以获得可靠的答案。 - 退货 answer, citations,和生 results.
src/mcp/tools/chat.js
handler(args)
- 使用Zod进行验证。 - 将用户消息直接发送到LLM进行一般聊天响应。
src/mcp/tools/configGenerator.js
handler(args)
- 使用Zod进行验证。 - 可选择检索示例。 - 提示LLM返回严格的JSON配置。
src/ui/chat-ui.js
run()
- CLI循环 retrieval / config 模式。 - 调用MCP端点并打印结果。
______________________________________________________________________
5) 安装程序
先决条件
- Node.js 18+
安装
npm install配置
cp .env.example .env编辑 .env 根据需要。
______________________________________________________________________
6) .env 参考
LLM_API_BASE=https://your-internal-llm-api.com
LLM_API_KEY=your-api-key
LLM_EMBED_MODEL=text-embedding-model
EMBEDDING_PROVIDER=natural
EMBEDDING_DIMENSIONS=256
XENOVA_MODEL=Xenova/all-MiniLM-L6-v2
LLM_CHAT_MODEL=chat-completion-model
MCP_PORT=3001
TOP_K=5
CHUNK_SIZE=500
CHUNK_OVERLAP=50
DOCS_DIR=./docs
VECTOR_INDEX_DIR=./vector-index______________________________________________________________________
7) 如何跑步
步骤A:添加文档
广场 .pdf, .txt,或 .md 文件位于 docs/.
步骤B:将文档引入向量索引
npm run ingest步骤C:启动MCP服务器
npm start健康检查:
curl http://localhost:3001/health步骤D:手动调用工具
curl -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-d '{"tool":"retrieval","arguments":{"query":"filter condition","topK":5}}'步骤E:启动CLI聊天UI
npm run ui然后选择模式:
retrieval基于文件的政策答案(+引用)chat用于直接LLM对话config用于JSON配置生成
______________________________________________________________________
8) 设计原理
- 按域模块化:
rag,mcp,ui,并共享utils是孤立的。 - POC简单:没有前端打包器,也没有重量级框架。
- 可交换嵌入:一个函数背后的natural/xenova/api(
embedText). - 可交换存储:vectra是本地的;稍后可以更换
src/rag而无需更改工具/UI合同。 - 验证优先工具:Zod模式可防止格式错误的工具调用。
______________________________________________________________________
9) 常见故障排除
retrieval返回较差的结果
- 更改嵌入提供程序后重新摄取。 - 增加 CHUNK_SIZE 或调谐 TOP_K.
- Xenova车型首次加载缓慢
- 首次运行下载模型工件;稍后的跑步速度更快。
- 配置生成失败
- 验证 LLM_API_BASE, LLM_API_KEY,以及聊天模型端点兼容性。
______________________________________________________________________
10) 脚本
npm start→ 运行MCP服务器npm run ingest→ 摄取文档,然后运行服务器启动路径npm run ui→ 启动CLI UI(检索模式+直接LLM聊天模式+配置模式)
11) 固定政策问答流程
当用户问“什么是休假政策?”时:
retrieval该工具嵌入问题并从vectordb中获取顶级策略块。- 该工具使用引用ID构建上下文块(
[C1],[C2], ...). - 它向LLM发送问题+上下文,并附有严格的接地说明:
- 仅从上下文中回答 - 如果不确定,请清楚地说出来 - 包括引用
- 响应返回为
{ answer, citations, results }.
这通过将用户指向特定的检索块来保持聊天机器人的真实性和可审计性。
12) 聊天模式
此聊天机器人支持两种请求的体验:
- 知识检索聊天(
retrieval)
- 使用RAG检索+LLM基础和引用([C1], [C2]). - 最适合政策/文件问答。
- LLM直接聊天(
chat)
- 将消息直接发送到LLM,无需检索。 - 最适合与索引文档无关的开放式帮助。
