Anila-Ijaz/InsightRAG

GitHub: Anila-Ijaz/InsightRAG

面向 SEC 10-K 财务文件的生产级 RAG 系统,集混合检索、微调重排序、安全护栏与自动化评估于一体。

Stars: 1 | Forks: 0

# InsightRAG **专为 SEC 10-K 财务文件打造的生产级检索增强生成系统。** [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/0e777565df053313.svg)](https://github.com/Anila-Ijaz/insightrag/actions) [![Python](https://img.shields.io/badge/Python-3.11+-3776AB?logo=python&logoColor=white)](https://www.python.org/) [![FastAPI](https://img.shields.io/badge/FastAPI-async-009688?logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com/) [![Pydantic](https://img.shields.io/badge/Pydantic-v2-E92063?logo=pydantic&logoColor=white)](https://docs.pydantic.dev/) [![Qdrant](https://img.shields.io/badge/Qdrant-vector%20DB-24386C?logo=qdrant&logoColor=white)](https://qdrant.tech/) [![OpenAI](https://img.shields.io/badge/OpenAI-embeddings%20%2B%20LLM-412991?logo=openai&logoColor=white)](https://platform.openai.com/) [![Streamlit](https://img.shields.io/badge/Streamlit-chat%20UI-FF4B4B?logo=streamlit&logoColor=white)](https://streamlit.io/) [![Hybrid RAG](https://img.shields.io/badge/RAG-Hybrid%20%2B%20Reranker-6E40C9)](#是什么让它与典型的-rag-demo-不同) [![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white)](https://www.docker.com/) [![AWS](https://img.shields.io/badge/AWS-EC2%20deployed-FF9900?logo=amazonec2&logoColor=white)](http://63.182.64.177:8501) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) ### 🔗 在线演示:**[InsightRAG 聊天界面](http://63.182.64.177:8501)**  ·  [API 文档](http://63.182.64.177:8000/docs) InsightRAG 能够以自然语言回答有关 SEC EDGAR 10-K 文件语料库的问题,并提供带引用的有据可依的回答。本项目的构建旨在展示那些将入门教程与生产级 RAG 系统区分开来的工程决策。 它附带了一个 **Streamlit 聊天界面**,并支持在两种配置下运行: | 配置 | 展示内容 | 资源占用 | 用途 | |---|---|---|---| | **完整版** (`docker-compose.yml`) | 本地 BGE embeddings + cross-encoder 重排序器,完整的可观测性技术栈 | ~5 GB 镜像,6–8 GB 内存 | 展示完整的检索工程,在本地运行 | | **轻量版** (`docker-compose.lite.yml`) | OpenAI embeddings,关闭重排序器,仅包含 `api + qdrant + UI` | ~508 MB 镜像,<1 GB 内存 | 云平台免费层部署 + 在线演示 | 这两种配置共享一套代码库;embedding 提供商和重排序器通过环境变量 (`EMBEDDING_PROVIDER`, `ENABLE_RERANKER`) 进行切换。轻量版配置正是部署到 AWS 免费层的方式。 ## 是什么让它与典型的 RAG demo 不同 大多数公开的 RAG 项目仅停留在“embedding → 存储 → 检索 → 生成”的阶段。而真正有价值的工程设计远不止于此。本仓库展示了: | 关注点 | 朴素方案 | 本仓库 | |---|---|---| | **检索** | 仅依赖向量相似度 | 混合检索 (dense BGE + BM25) 结合 RRF 融合 | | **重排序** | 无 | 在合成问答 + 困难负样本上微调的 cross-encoder (BGE-reranker) | | **分块** | 固定大小的字符分割 | 递归、感知 token 且保留结构的分块 (10-K 章节元数据) | | **Prompt 安全** | 盲目拼接并祈祷不出错 | 多层防护:长度限制、注入模式检测、PII 脱敏、输出引用验证 | | **评估** | “看起来还不错” | 在夜间 CI 中运行 RAGAS + 针对 4 种配置的检索基准测试 (MRR/nDCG/Recall) | | **可观测性** | print 语句 | 结构化 JSON 日志 (loguru)、Prometheus 指标、Langfuse 追踪 | | **部署** | 单个 `python app.py` | 多阶段 Docker、compose 技术栈、GitHub Actions CI/CD、GHCR 镜像仓库 | ## 架构 ``` ┌────────────────────┐ │ SEC EDGAR (10-K) │ └─────────┬──────────┘ │ download ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ INGESTION PIPELINE │ │ Parser (SGML→HTML→sections) → Semantic Chunker (token-aware) → │ │ Embedder (BGE) │ └────────────────┬────────────────────────────────┬───────────────────┘ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ │ Qdrant │ │ BM25 index │ │ (dense) │ │ (sparse) │ └───────┬───────┘ └────────┬────────┘ │ │ └────────────┬────────────────────┘ ▼ ┌──────────────────────┐ │ Hybrid Retriever │ ← Reciprocal Rank Fusion │ (top-20 candidates) │ └──────────┬───────────┘ ▼ ┌──────────────────────┐ │ Cross-Encoder │ ← Fine-tuned on SEC corpus │ Reranker (top-5) │ └──────────┬───────────┘ ▼ ┌──────────────────────┐ │ LLM Generation │ ← OpenAI/Anthropic, streamable │ with citation guard │ └──────────┬───────────┘ ▼ FastAPI (SSE) ``` ## 技术栈 **核心:** Python 3.11、FastAPI (异步)、Pydantic v2 **前端:** Streamlit 聊天界面 (引用 + 实时延迟面板) **Embeddings:** 本地 BGE (sentence-transformers) **或** OpenAI API — 提供商可切换 **检索:** sentence-transformers (BGE)、Qdrant、rank-bm25 **重排序器:** BGE-reranker-base (通过 `training/train_reranker.py` 微调) **生成:** OpenAI / Anthropic SDK (对提供商进行抽象隔离) **存储:** Postgres (元数据)、Redis (缓存)、S3 (原始文档) **异步任务:** Celery **护栏:** Presidio (PII)、正则表达式模式匹配 (注入)、输出引用验证 **可观测性:** loguru (结构化日志)、prometheus-client、OpenTelemetry、Langfuse **评估:** RAGAS、自定义检索基准 (MRR/nDCG/Recall@k) **基础设施:** Docker (多阶段)、docker-compose、GitHub Actions、GHCR **测试:** pytest、pytest-asyncio、ruff、mypy ## 快速开始 ### 轻量版配置 (推荐 — 聊天界面,无需 GPU,~1 GB 内存) ``` git clone https://github.com/Anila-Ijaz/insightrag cd insightrag cp .env.lite.example .env # then set OPENAI_API_KEY in .env docker compose -f docker-compose.lite.yml up -d --build ``` 然后打开 **聊天界面** 并开始提问: - **聊天界面:** http://127.0.0.1:8501 *(如果你的 Docker 存在 IPv6 兼容问题,请使用 `127.0.0.1`,而不是 `localhost`)* - **API 文档:** http://127.0.0.1:8000/docs 从界面侧边栏或通过 API 导入文件: ``` # 从 SEC EDGAR 索引 Apple 最新的 10-K curl -X POST http://127.0.0.1:8000/v1/ingest \ -H "content-type: application/json" \ -d '{"ticker":"AAPL","limit":1}' # 提出问题 curl -X POST http://127.0.0.1:8000/v1/query \ -H "content-type: application/json" \ -d '{"question":"What were Apple total net sales?","ticker":"AAPL","top_k":5}' ``` ### 完整版配置 (本地 BGE + cross-encoder 重排序器 + 可观测性) ``` cp .env.example .env # set OPENAI_API_KEY make up # api + qdrant + postgres + redis + prometheus + grafana make ingest TICKER=AAPL ``` ## 评估结果 ### 观察到的延迟 — 轻量版配置,gpt-4o-mini 在轻量级技术栈 (OpenAI embeddings,关闭重排序器)、仅索引单个 AAPL 10-K 文件 (110 个 chunks) 的情况下,测得的端到端数据: | 阶段 | 观察结果 | |---|---:| | 检索 (混合检索,OpenAI embed + Qdrant + BM25) | ~1.1–2.3 秒 | | 重排序 | 0 毫秒 *(在轻量版中已禁用)* | | 生成 (gpt-4o-mini) | ~1.5–3.3 秒 | | **总计** | **~3.8–4.4 秒** | ### 检索基准测试 语料库:AAPL + MSFT + GOOGL 10-K 文件 (466 个 chunks)。测试集:由 `scripts/generate_retrieval_testset.py` 生成的 60 个合成 `(查询 → 相关 chunk)` 标签,通过 `evals/benchmark.py` 在 k=10 时进行评分。 轻量版配置,因此“Dense” = OpenAI `text-embedding-3-small` (非 BGE)。 | 配置 | MRR@10 | Recall@10 | nDCG@10 | |---|---:|---:|---:| | Dense (OpenAI text-embedding-3-small) | 0.689 | 0.883 | 0.735 | | Sparse (BM25) | **0.762** | **0.967** | **0.811** | | Hybrid (RRF) | 0.618 | 0.917 | 0.693 | | Hybrid + 微调重排序器 | _不适用 (完整技术栈)_ | — | — | ### 端到端 RAG 质量 (RAGAS) `evals/run_ragas.py` 针对 8 个问题 (按 ticker 过滤)、使用 gpt-4o-mini,并基于完整的检索上下文进行评判: | 指标 | 得分 | |---|---:| | Faithfulness | 0.77 | | Answer Relevancy | 0.72 | | Context Precision | _需要标注的参考答案_ | | Context Recall | _需要标注的参考答案_ | ## 设计决策 (及其原因) ### 为什么使用混合检索,而不是纯粹的向量搜索? Dense embeddings 会漏掉财务文件中至关重要的精确匹配信号 —— 如特定金额、日期、ticker 代码和产品代号。BM25 则能捕捉到这些。单独使用任何一种都会产生明显且相互背离的失败模式;通过 RRF 结合起来,它们可以互补盲区。 (值得注意的是,在上面的合成基准测试中,BM25 的表现*优于*dense 和 hybrid —— 因为 LLM 生成的问题复用了源 chunk 的确切措辞。这很好地提醒了我们,“混合检索永远是赢家”只是一个神话:正确的融合方式取决于你的查询分布,这正是本仓库同时内置两种排序器和消融测试工具的原因。) ### 为什么选择 Reciprocal Rank Fusion 而不是加权分数融合? RRF 无需参数 (无需针对每个语料库调整 `alpha`),对排序器之间分数分布的差异具有鲁棒性,并且在已发布的基准测试中始终表现强劲 (Cormack et al., 2009)。加权融合则主要用于消融研究而实现。 ### 为什么要微调重排序器? 基础的 BGE-reranker 是在 MS-MARCO 上训练的。而财务文件拥有独特的术语 —— 基于你自己的语料库生成的合成问答对进行微调,可以显著提升领域内查询的 nDCG@10。从现有检索器中挖掘出的困难负样本则让整个流程闭环。 ### 为什么要设置独立的输出护栏? 当仅提供了 5 个 chunks 时,LLM 偶尔会凭空捏造诸如 `[99]` 这样的引用。输出护栏会在响应离开 API 之前剔除无效的索引。这是一种低成本、高鲁棒性的深度防御手段。 ### 为什么使用带 SSE 的流式传输,而不是 WebSockets? SSE 原生兼容 HTTP (无需特殊配置即可穿透 CDN、负载均衡器和代理),其单向特性非常契合我们的用例,并且在任何客户端都能轻松调用 (`fetch` 结合 `EventSource`)。使用 WebSockets 反而会显得过于复杂。 ### 为什么要对 LLM 进行提供商抽象? 生产系统常常因为供应商锁定和提供商宕机而蒙受损失。`LLMClient` ABC 允许你通过一个环境变量从 OpenAI 切换到 Anthropic,并使得未来添加自托管的 Llama 后备方案变得轻而易举。 ## 项目结构 ``` insightrag/ ├── src/insightrag/ │ ├── ingestion/ # SEC parser, semantic chunker, embedder │ ├── retrieval/ # Qdrant store, BM25 index, hybrid retriever, reranker │ ├── generation/ # LLM client (OpenAI/Anthropic), prompts, RAG chain │ ├── guardrails/ # Input (injection, PII) + output (citation validation) │ ├── api/ # FastAPI app, schemas, dependencies │ ├── observability/ # Logging, Prometheus metrics │ └── config.py # pydantic-settings (provider switches) ├── frontend/ # Streamlit chat UI (app.py + Dockerfile) ├── training/ # Reranker fine-tuning (synthetic Q&A + hard negatives) ├── evals/ # RAGAS runner + retrieval benchmark + test set ├── tests/ # pytest suite (chunker, retrieval, guardrails, API) ├── infra/ # Prometheus config, k8s manifests, terraform ├── .github/workflows/ # ci.yml (lint/test/build), eval.yml (nightly RAGAS) ├── Dockerfile # Full image (local ML models) ├── Dockerfile.lite # Lite image (~508 MB, OpenAI embeddings, no torch) ├── docker-compose.yml # Full: api + qdrant + postgres + redis + prom + grafana ├── docker-compose.lite.yml # Lite: api + qdrant + Streamlit UI ├── requirements-lite.txt # Lite runtime deps (subset of pyproject) ├── pyproject.toml # Dependencies, ruff/mypy/pytest config └── Makefile # install / dev / test / eval / up / down / ingest ``` ## 状态 - ✅ **在 AWS 上运行** — EC2 (法兰克福)、Elastic IP、API 密钥认证 - ✅ **Streamlit 聊天界面** — 引用 + 实时延迟面板 - ✅ **两种运行配置** — 完整版 (本地 BGE + cross-encoder 重排序器) 和轻量版 (OpenAI,可部署在免费层) - ✅ **已完成基准测试** — 检索 (MRR/nDCG/Recall) + RAGAS,以及上文展示的真实数据 - ✅ **CI** — ruff 代码检查/格式化,25 个通过的测试,容器构建 → GHCR ## 许可证 MIT — 详见 [LICENSE](LICENSE)。
标签:AV绕过, Docker, FastAPI, Kubernetes, RAG系统, 人工智能, 向量检索, 安全规则引擎, 安全防御评估, 搜索引擎查询, 测试用例, 用户模式Hook绕过, 自定义请求头, 请求拦截, 逆向工具, 金融数据