# nova-retrieve **Repository Path**: liukai_1900/nova-retrieve ## Basic Information - **Project Name**: nova-retrieve - **Description**: Nova Retrieve — Enterprise Agentic RAG - **Primary Language**: Python - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-19 - **Last Updated**: 2026-06-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Nova Retrieve — 企业级 Agentic RAG 基于 **LangChain + LangGraph + Qdrant + BGE-M3** 的可生产部署 Agentic RAG 框架。 > 英文版:[README.md](README.md) ## 特性 - **LangGraph 状态机驱动**:查询改写 → 路由 → 检索 → 文档评分 → 生成 → 幻觉检测 → 答案有用性评估,全部带回退路径。 - **CRAG / Self-RAG 思路**:检索不够时自动改写重试;生成有幻觉时自动重写;多轮失败兜底走 Web 搜索。 - **本地向量化(BGE-M3)**:通过 sentence-transformers 加载本地模型目录,数据不出域。设 `EMBEDDING_LOCAL_PATH` 即可。 - **Qdrant 向量库**:Docker 一键起,自动建集合。 - **OpenAI 兼容 LLM**:可对接 DeepSeek / 通义 / 智谱 / 任何兼容 endpoint。 - **Tavily Web 兜底**:知识库召回不足时切换实时网络搜索。 - **FastAPI + SSE 流式**:节点级事件流,前端可实时展示 agent 推理轨迹。 - **内置 Web UI**:零构建的单页前端(`/ui/`),实时显示每个 agent 步骤、耗时、引用来源。 - **MCP server**:另以 MCP 工具(`rag_search` / `rag_answer` / `rag_collections`)暴露整套能力,支持 stdio 与 streamable-http,**Hermes** 等 agent 可原生调用本知识库。 ## 架构 ``` ┌──────────────┐ │ rewrite_query│ └──────┬───────┘ ▼ ┌──────────────┐ │route_question│ └──┬────────┬──┘ vectorstore│ │web_search ▼ ▼ ┌──────┐ ┌─────────┐ │retrieve │web_search│ └──┬───┘ └────┬────┘ ▼ │ ┌──────────────┐ │ │grade_documents│ │ └──┬───────┬────┘ │ relevant│ none │ │ ▼ ▼ │ ┌────────┐ transform │ │generate│◄──query──┐ │ └───┬────┘ │ │ ▼ retry│ │ ┌────────────────┐ │ │ │hallucination_ │──no──┘ │ │grader (CRAG) │ │ └───┬────────────┘ │ ▼ yes │ ┌────────────┐ │ │answer_grader│──no→transform_query └───┬────────┘ ▼ useful END ``` ## 快速开始 ### 1. 起 Qdrant ```bash docker compose up -d qdrant ``` ### 2. 装依赖 ```bash python -m venv .venv && source .venv/bin/activate pip install -e . ``` 把已下载的 BGE-M3 目录路径填入 `.env` 的 `EMBEDDING_LOCAL_PATH`(应包含 `config.json` / `tokenizer.json` / `model.safetensors` 等)。若留空则首次运行从 HuggingFace 下载。 ### 3. 配置 ```bash cp .env.example .env # 编辑 LLM_BASE_URL / LLM_API_KEY / TAVILY_API_KEY ``` ### 4. 灌库 ```bash python -m scripts.ingest_docs ./data/docs ``` ### 5. 启服务 ```bash uvicorn app.main:app --host 0.0.0.0 --port 8000 ``` 打开浏览器访问 即进入 Web UI(自动重定向到 `/ui/`)。 或交互式 CLI: ```bash python -m scripts.chat_cli ``` ## API | 端点 | 方法 | 说明 | |---|---|---| | `/health` | GET | 健康检查 | | `/ingest` | POST | 摄入文件/目录 | | `/chat` | POST | 阻塞式问答,返回完整答案 + 引用 | | `/chat/stream` | POST | SSE 流:`step` 事件按节点推送,`answer` 事件返回最终结果 | ### 示例 ```bash curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"question":"我们的退款政策是什么?"}' ``` SSE 流: ```bash curl -N -X POST http://localhost:8000/chat/stream \ -H "Content-Type: application/json" \ -d '{"question":"GPT-5 发布了吗?"}' ``` ## MCP server(对接 Hermes / 外部 agent) 除 HTTP API 外,整套 RAG 还以 **MCP server**(`app/mcp_server.py`)形式暴露,支持 MCP 的 agent(如 **Hermes**)可把它当原生工具调用。它是薄适配层:复用同一套 retriever 和 LangGraph 流程,不重复任何逻辑。 ### 工具 | 工具 | 开销 | 说明 | |---|---|---| | `rag_search` | 便宜,不走 LLM | 语义检索,返回带来源与相似度分数的原始 chunk,由 agent 自己推理并引用。完全离线(本地 BGE-M3 + Qdrant)。 | | `rag_answer` | 较重,自带 LLM 调用 | 跑完整 Agentic-RAG 流程,返回经幻觉评估的合成答案 + 来源列表。 | | `rag_collections` | 便宜 | 列出 Qdrant 集合(知识库)及其点数。 | ### 传输方式 由 `RAG_MCP_TRANSPORT` 选择: - **`stdio`**(默认):由 MCP 客户端拉起本进程,走 stdin/stdout。适合同机集成。 - **`http`**:常驻网络服务(streamable-http),地址 `http://:/mcp`。适合服务器间 / 离线机集成。 > stdio 下 **stdout 是 JSON-RPC 通道**:本服务把所有日志固定到 stderr,且从不调用 `setup_logging()`。工具内不要往 stdout 打印。 ### 启动 ```bash pip install -e . # 装上 `mcp` 依赖与 `nova-mcp` 命令 # A) HTTP 服务 —— Hermes 在另一台机器时推荐 RAG_MCP_TRANSPORT=http RAG_MCP_WARMUP=1 nova-mcp # → streamable-http 服务于 http://0.0.0.0:8765/mcp # B) stdio —— 同机,客户端拉起进程 RAG_MCP_TRANSPORT=stdio nova-mcp # 或:python -m app.mcp_server ``` `RAG_MCP_WARMUP=1` 在启动时预加载 embedding 模型 + Qdrant 集合,让首个请求不再承担该开销 —— http 常驻服务建议开启。 ### 接入 Hermes HTTP 传输(RAG 网络可达): ```yaml # Hermes cli-config.yaml mcp_servers: knowledge_base: url: http://:8765/mcp ``` stdio 传输(RAG 与 Hermes 同机): ```yaml mcp_servers: knowledge_base: command: nova-mcp args: [] env: RAG_MCP_TRANSPORT: stdio EMBEDDING_LOCAL_PATH: /abs/path/to/bge-m3 QDRANT_URL: http://localhost:6333 LLM_BASE_URL: http:///v1 LLM_API_KEY: ``` > stdio 下 Hermes 会全新拉起进程,**不会**继承你的 shell 或 `.env`。RAG 需要的配置(embedding 路径、Qdrant 地址、LLM endpoint)都要在 `env:` 里显式传入。注意 `rag_search` 不需要 LLM,仅 `rag_answer` 会调用 `LLM_BASE_URL`。 ### 配置项 | 环境变量 | 默认值 | 含义 | |---|---|---| | `RAG_MCP_TRANSPORT` | `stdio` | `stdio` 或 `http` | | `RAG_MCP_HOST` | `0.0.0.0` | http 绑定地址 | | `RAG_MCP_PORT` | `8765` | http 绑定端口 | | `RAG_MCP_MAX_CHARS` | `1500` | `rag_search` 每个 chunk 正文截断上限(保护 agent 上下文窗口) | | `RAG_MCP_WARMUP` | `0` | `1` = 启动即预加载 embedding/集合 | ### 排错 —— 弱本地模型死活不检索 **现象**:MCP server 连上了(`hermes mcp test ` 能列出工具),但 agent 凭自己的知识答,从不调 `rag_search` / `rag_answer`。 强模型很少出这问题;小本地模型(如 vLLM 跑的 **llama-3.1-8B**)几乎必然出 —— 病根在*工具面*,不在模型。按这个顺序排查: 1. **模型到底能不能发 tool call?** 直接 `curl` LLM endpoint,带一个假工具 + `"tool_choice":"auto"`,确认 `choices[0].message.tool_calls` 有值。vLLM 跑 Llama 3.1 必须带 `--enable-auto-tool-choice --tool-call-parser llama3_json`,否则任何带 tools 的请求都 HTTP 400。 2. **工具真注册上了吗?** `hermes mcp test `、`/tools` 命令,或 grep `~/.hermes/logs/agent.log` 里的 `registered N tool(s)`。 3. **这次请求带了几个工具?** 看 verbose 日志的 `Tools: N` / `prompt_tokens` —— 25 个工具(约 1.2 万 token 的 schema)足以淹没 8B。 4. **它实际调的是哪个工具?** 调错工具 = 有干扰工具在抢。 我们在 llama-3.1-8B 上踩到的四个根因及修法(除注明外均在 `~/.hermes/config.yaml`): | # | 根因 | 修法 | |---|---|---| | 1 | 默认 `hermes-cli` 预设带 25 个工具,淹没 8B | `platform_toolsets: { cli: [file, mcp-] }` —— 白名单**必须**含 `mcp-`,否则会把 RAG 工具一起静默丢掉 | | 2 | 通用工具 `clarify` 抢跑 | `agent: { disabled_toolsets: [clarify] }` | | 3 | 无常驻引导 —— Hermes 丢弃 MCP server 的 `instructions`,只把每个工具的 `description` 发给模型 | 加一条 `agent.personalities`,里面点名 `mcp__rag_search`,并用 `/personality` 启用。`mcp_server.py` 里强化过的工具 docstring 也有用 —— 那是 Hermes 唯一会转发的逐工具通道。 | | 4 | 8B 瞎编可选参数 `collection` → 查到不存在的 Qdrant 集合 → "无结果" | 服务端把 `rag_search`/`rag_answer` 收成单个必填参数(本仓库已做)。再砍掉 MCP 的 prompt/resource 元工具:`mcp_servers..tools: { prompts: false, resources: false, include: [rag_search, rag_answer, rag_collections] }` | 每改一处:先校验 YAML(`python3 -c "import yaml; yaml.safe_load(open('/root/.hermes/config.yaml'))"`),重启 Hermes,再用 `/verbose` 复查。健康状态:`Tools:` ≈ 7,第一个 `Tool call:` 是 `mcp__rag_search` / `rag_answer` 且只带 `query` / `question` 参数,RAG 日志查的是**默认集合**(`QDRANT_COLLECTION`)。 > 收敛工具面、干掉干扰/元工具、把引导放到宿主真正会转发的地方(工具描述 + personality,而非 MCP `instructions`)、给工具最简签名。强模型能容忍杂乱工具面,弱本地模型不能。 #### 保留 Hermes 其它功能又不毁掉检索 上面那刀(`cli: [file, mcp-]`)是**调试动作** —— 把 Hermes 砍到只剩文件+RAG 是为了隔离过载问题,**并非**你想要的终态。两个机制要懂(`model_tools.py`): - `platform_toolsets.` 是**工具集名白名单**。一旦写了,就只有列进去的 toolset 生效 —— MCP 工具是普通的 `mcp-` toolset,**不会自动挂上**,没列就静默消失。 - `agent.disabled_toolsets` **最后做减法**,连 `hermes-cli` 这种复合预设里的工具也能精准剔除 —— 只删 `clarify` 不伤其它的正道。 - 名字可组合:`[hermes-cli, mcp-]` = 完整预设 ∪ RAG 工具。 代价是一个**仅对弱模型成立**的真实取舍:完整预设 + RAG ≈ 28 个工具,8B 直接回到过载(不再调 RAG)。这是 8B 的能力天花板,不是配置 bug —— 换强编排模型就没有这个取舍。8B 约束下按优先级三选一: | 方案 | `platform_toolsets.cli` | 结果 | |---|---|---| | A — 功能完整优先 | `[hermes-cli, mcp-]` | Hermes 不缺;RAG 检索**不稳**(8B 被淹),只能靠 personality 部分兜底 | | B — 折中(推荐) | `[file, web, terminal, todo, mcp-]` | 保住日常工具 + RAG;约 12 个工具,8B 仍能较稳检索 | | C — RAG 优先 | `[file, mcp-]` | RAG 最稳;Hermes 退化成只会读写文件 | 三者都保留 `agent.disabled_toolsets: [clarify]` 和引导 personality。推荐路径:从 B 出发,按你实际会用的 toolset 往回加(web/terminal/browser/skills/todo/tts/cronjob —— 见 `cli-config.yaml.example` 的 *Available toolsets*),每加一个就 `/verbose` 看 `Tools: N` —— 经验上 8B 在 ≈12 个工具内可靠,过 ≈20 就开始漏 RAG。 ## 目录结构 ``` app/ ├── config.py # pydantic-settings 配置 ├── main.py # FastAPI app + /ui 静态挂载 ├── mcp_server.py # 给 Hermes 用的 MCP 适配层(stdio / streamable-http) ├── api/ # 路由与 schema ├── core/ # llm / embeddings / vectorstore / logging ├── ingest/ # loader / chunker / pipeline ├── retrieval/ # 检索器 └── agent/ # state / nodes / edges / prompts / graph / tools web/ # 前端单页(无构建) ├── index.html ├── styles.css └── app.js scripts/ ├── ingest_docs.py └── chat_cli.py ``` ## 可扩展点 - **Reranker**:在 `retrieval/` 加 BGE-Reranker 二阶段精排。 - **多租户**:`ChatRequest.collection` 已经留好按租户隔离的钩子。 - **缓存**:在 `route_question` / `grade_documents` 上加 Redis 缓存可显著降本。 - **观测**:设置 `LANGSMITH_API_KEY` 即可全链路追踪。