手把手搭建 Adaptive RAG 系统:从向量检索到 Streamlit 前端全流程
本文会带你从零搭建一个完整的概念验证项目POC技术栈涵盖 Adaptive RAG、LangGraph、FastAPI 和 Streamlit 四个核心组件。Adaptive RAG 负责根据查询复杂度自动调整检索策略LangGraph 把多步 LLM 推理组织成有状态的可靠工作流FastAPI 作为高性能后端暴露整条 AI 管道Streamlit 则提供一个可以直接交互的前端界面。读完这篇文章你拿到的不只是理论——而是一个跑得起来的端到端 AI 系统。要构建的是一个技术支持智能助手。它能理解用户查询根据问题复杂度动态选择检索深度Adaptive RAG通过 LangGraph 执行推理工作流经由 FastAPI 返回结果最后在 Streamlit UI 上呈现响应。这个场景针对的是一个真实痛点团队面对大规模文档集时传统 RAG 在模糊查询或多步骤问题上经常答非所问。技术概览Adaptive RAG可以把 Adaptive RAG 理解为搜索之前先思考的 RAG。简单查询走轻量级检索就够了遇到复杂问题则自动切换到多跳深度搜索、重排序或查询扩展用更低的延迟换更高的准确率。LangGraphLangGraph 是用来构建有状态、多步骤 AI 工作流的框架。和传统链式调用不同它把 LLM 工作流建模成一张图——每个节点对应一个步骤检索 → 推理 → 验证 → 响应原生支持重试、记忆、循环和故障转移。对于需要在生产环境中保证可预测行为的场景这种抽象比线性 chain 灵活得多。FastAPIFastAPI 把 Adaptive RAG LangGraph 包装成 API 接口对外暴露处理请求分发天然适配异步 I/O。Streamlit前端用 Streamlit 搭建聊天风格的界面不需要写 HTML/CSS做 POC 演示足够了。系统架构数据流走向User → Query → Streamlit UI Streamlit → Sends request → FastAPI FastAPI → Passes query → LangGraph LangGraph → Runs Adaptive RAG → Retriever Retriever → Gets chunks → Vector DB Vector DB → Returns results → LangGraph LangGraph → Generates final response FastAPI → Sends to UI → User文件夹结构项目结构尽量精简ai-poc/ │ ├── backend/ # 后端逻辑 │ ├── app.py # FastAPI API 服务器 │ ├── rag_pipeline.py # Adaptive RAG 检索 │ ├── graph_workflow.py # LangGraph 工作流 │ ├── config.py # 配置和环境设置 │ ├── data/ # 源文档 │ └── __init__.py # 包初始化器 │ ├── frontend/ # UI 层 │ ├── ui.py # Streamlit 界面 │ └── __init__.py # 包初始化器 │ ├── .env # API 密钥和机密信息 ├── requirements.txt # 项目依赖 └── README.md # 设置说明requirements.txt 文件fastapi uvicorn[standard] streamlit requests pydantic langchain langchain-community langgraph faiss-cpu sentence-transformers openai python-dotenv代码实现关键代码片段Adaptive RAG 管道rag_pipeline.py# backend/rag_pipeline.py from typing import List from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.schema import Document class AdaptiveRAG: Adaptive Retrieval Pipeline def __init__(self, vector_db: FAISS): self.db vector_db def retrieve(self, query: str) - List[Document]: if not query.strip(): return [] # Adaptive heuristic token_count len(query.split()) k 3 if token_count 6 else 8 return self.db.similarity_search(query, kk) def build_vector_store(texts: List[str]) - FAISS: Build FAISS index from raw texts (POC only). In production load persisted DB instead. embeddings HuggingFaceEmbeddings( model_namesentence-transformers/all-MiniLM-L6-v2 ) splitter RecursiveCharacterTextSplitter( chunk_size1000, chunk_overlap100 ) docs [] for text in texts: chunks splitter.split_text(text) for chunk in chunks: docs.append(chunk) return FAISS.from_texts(docs, embeddings)自适应的核心逻辑其实很简单根据查询的 Token 数决定检索深度。查询短于 6 个词就取 3 条结果否则拉 8 条。这是一个粗粒度的启发式方法在 POC 阶段够用生产环境可以替换成更精细的分类器。build_vector_store函数从原始文本构建 FAISS 索引。注意这里每次启动都重建索引生产上应该加载持久化的数据库。LangGraph 工作流graph_workflow.py# backend/graph_workflow.py from typing import TypedDict, List from langgraph.graph import StateGraph, END from langchain.schema import Document from langchain_openai import ChatOpenAI class GraphState(TypedDict): question: str docs: List[Document] answer: str def create_workflow(rag): llm ChatOpenAI(modelgpt-4o-mini, temperature0) workflow StateGraph(GraphState) # Retrieval Node async def retrieve_node(state: GraphState): docs rag.retrieve(state[question]) return {docs: docs} # Reasoning Node async def reasoning_node(state: GraphState): question state[question] docs state.get(docs, []) context \n\n.join([d.page_content for d in docs]) prompt f You are a technical assistant. Use ONLY the context below to answer the question. If the answer is not in the context, say you dont know. Context: {context} Question: {question} response await llm.ainvoke(prompt) return {answer: response.content} # Add nodes workflow.add_node(retrieve, retrieve_node) workflow.add_node(reason, reasoning_node) # Connect nodes workflow.set_entry_point(retrieve) workflow.add_edge(retrieve, reason) workflow.add_edge(reason, END) return workflow.compile()整个工作流只有两个节点retrieve 负责检索reason 负责推理生成答案。GraphState 作为 TypedDict 在节点间传递状态。流程很线性——先检索再推理然后结束。实际项目中可以在这个图上加验证节点、循环重试等分支LangGraph 的图结构天然支持这种扩展。FastAPI 后端app.py# backend/app.py import os from fastapi import FastAPI, HTTPException from pydantic import BaseModel from dotenv import load_dotenv from rag_pipeline import AdaptiveRAG, build_vector_store from graph_workflow import create_workflow load_dotenv() app FastAPI(titleAdaptive RAG API) # --------------------------- # Startup Initialization # --------------------------- class AskRequest(BaseModel): query: str app.on_event(startup) async def startup_event(): global workflow # Sample knowledge base (replace with real docs) sample_docs [ LangGraph supports stateful workflows and retry logic., Adaptive RAG dynamically changes retrieval depth based on query complexity., FastAPI is a high-performance async Python framework., ] vector_db build_vector_store(sample_docs) rag AdaptiveRAG(vector_db) workflow create_workflow(rag) # --------------------------- # API Endpoint # --------------------------- app.post(/ask) async def ask(payload: AskRequest): if not payload.query.strip(): raise HTTPException(status_code400, detailQuery cannot be empty) try: result await workflow.ainvoke( {question: payload.query} ) return {response: result[answer]} except Exception as e: raise HTTPException( status_code500, detailInternal RAG processing error )后端在启动时完成向量库构建和工作流初始化之后通过 /ask 端点接收查询请求。这里用了 global 变量来持有 workflow 实例——POC 阶段这样做没问题上生产建议用依赖注入替代。Streamlit UIui.py# frontend/ui.py import streamlit as st import requests API_URL http://localhost:8000/ask st.set_page_config(page_titleAdaptive RAG Assistant) st.title(Adaptive RAG Support Assistant) query st.text_input(Enter your question) if st.button(Ask): if not query.strip(): st.warning(Please enter a question.) else: try: with st.spinner(Thinking...): response requests.post( API_URL, json{query: query}, timeout60 ) response.raise_for_status() answer response.json()[response] st.markdown(### Answer:) st.write(answer) except Exception as e: st.error(fError: {e})前端就这么几行输入框接收问题按钮触发请求拿到结果直接渲染。Streamlit 的好处就是不用折腾前端那套东西做 POC 验证概念足够。运行项目安装依赖pip install -r requirements.txt设置 OpenAI Keyexport OPENAI_API_KEYyour_key_here Or(For Windows) setx OPENAI_API_KEY your_key_here启动后端uvicorn backend.app:app --reload启动前端streamlit run frontend/ui.py内部执行流程在 UI 中输入这样一条查询How does retry logic work in LangGraph workflows?请求先到达 FastAPI 后端。LangGraph 工作流从 retrieve 节点启动Adaptive RAG 根据查询长度动态选定检索深度——短查询取k3长查询取k8。从向量数据库拉到相关文档块后reasoning 节点把这些上下文拼装成 prompt交给 LLM 生成答案。LLM 的回答被限定在检索到的上下文范围内最终结果沿原路返回到 UI。一切正常的话你现在手上就有了一条完整的端到端 RAG 管道UI → API → Graph → Retriever → LLM → Response。下一步生产部署POC 跑通了但离生产还有距离。下面按模块列一下需要补强的方向。检索层向量相似度搜索可以跟 BM25 关键词搜索做混合在一些 edge case 上召回率会好很多。检索完 top-k 文档后再套一层 Cross-Encoder 做重排序排序精度能上一个台阶。如果系统要支持多团队或多租户还得引入基于命名空间的文档隔离防止跨域信息泄漏。工作流当前工作流里没有验证环节。生产环境建议加一个验证节点检查生成的答案是否真的有检索上下文支撑——这对控制幻觉至关重要。另外如果要做多轮对话就需要往图里加记忆节点来持久化对话状态。重试与回退LLM 调用失败后要有重试机制。主模型不可用时能自动降级到更小或更便宜的备选模型。超时控制和优雅降级也不能少。成本控制也值得考虑简单查询走轻量模型只在必要时才升级到大模型。可观测性与评估日志要记全——检索分数、命中的文档、响应延迟、Token 消耗这些都得有。定期做离线评估准备好测试数据集跑检索质量和回答质量的指标。幻觉监控单独拎出来盯追踪那些答案脱离检索上下文的 case。UI 改进聊天界面得支持历史记录和多轮对话。回答来源要做高亮——用户应该能看到答案是从哪几段文档生成的。再加上反馈按钮让用户对回答打分收集回来的数据可以用于后续评估和微调。部署与基础设施前后端都做 Docker 容器化保证部署的可复现性。上云的话 AWS、GCP、Azure 都行记得配自动扩缩容。端点要上 HTTPS 和 Token 认证JWT 或 OAuth。生产环境的 API Key 不要再靠环境变量了换托管的密钥存储服务。总结一个模块化、可扩展的 RAG 架构就搭建完成了它完全可以从当前的 POC 状态逐步演化成生产级系统。自适应检索、有状态编排、可扩展 API、简洁的交互界面——这几个构建块拼在一起基本覆盖了现代 LLM 应用的核心架构需求。https://avoid.overfit.cn/post/770176403fa04ac49a55a145c36266b8by Robi Kumar Tomar

相关新闻

编程技能的普及化与社会影响

编程技能的普及化与社会影响

编程技能的普及化与社会影响关键词:编程技能普及化、社会影响、教育变革、就业市场、创新驱动、数字化社会、技术素养摘要:本文深入探讨了编程技能普及化这一趋势及其对社会产生的广泛影响。首先介绍了研究的目的、范围、预期读者和文档结构,…

2026/7/2 21:48:26 阅读更多 →
大数据领域数据科学的质量控制与评估

大数据领域数据科学的质量控制与评估

大数据领域数据科学的质量控制与评估:从"脏数据"到"金数据"的蜕变之旅 关键词:数据质量、质量控制、数据评估、大数据、数据科学 摘要:在大数据时代,数据被称为"新石油",但未经提炼的&q…

2026/7/4 14:55:42 阅读更多 →
《百面大模型》技术人都在看,大模型面试最强解析(PDF分享)

《百面大模型》技术人都在看,大模型面试最强解析(PDF分享)

“AI 大模型太卷了,我每天用 DeepSeek 却不知道怎么在面试里答题。” “明明刷了很多题,可一到面试就慌得要死。” ——这是我最近听到最多的两句话。 如果你是正在准备秋招同学,或者是一位想转型大模型方向的工程师,下面这些内容…

2026/7/3 17:35:26 阅读更多 →

最新新闻

大模型指纹识别技术:原理、攻防与实战应用

大模型指纹识别技术:原理、攻防与实战应用

1. 项目概述:当大模型学会“签名”,我们如何识别与应对? 最近在跟几个做AI安全的朋友聊天,大家不约而同地提到了一个词:“LLM指纹识别”。这听起来有点玄乎,指纹不是人的生物特征吗,怎么大语言模…

2026/7/4 16:38:50 阅读更多 →
AI冲击下数据岗位重构:国际人才策略与能力原子化实践

AI冲击下数据岗位重构:国际人才策略与能力原子化实践

1. 项目概述:这不是一份“就业报告”,而是一份人才迁徙路线图“2025年美国数据岗位市场”——光看标题,你可能以为这又是一份堆砌招聘平台统计数字、罗列热门职位名称的常规行业简报。但实际不是。我连续三年深度参与硅谷、纽约、奥斯汀三地的…

2026/7/4 16:36:50 阅读更多 →
STM32与MC6470 IMU的硬件协同与运动控制优化

STM32与MC6470 IMU的硬件协同与运动控制优化

1. MC6470与STM32L4S5ZI的硬件协同架构解析MC6470作为一款六轴惯性测量单元(IMU),其核心价值在于将三轴加速度计和三轴陀螺仪集成在单芯片方案中。在实际项目中,我测量到其加速度计量程可达16g,角速度测量范围达到2000dps,这对于大…

2026/7/4 16:34:49 阅读更多 →
XWiki路径遍历漏洞CVE-2025-55747复现与深度解析

XWiki路径遍历漏洞CVE-2025-55747复现与深度解析

1. 项目概述与漏洞背景 最近在梳理一些开源项目的安全公告时,XWiki的一个路径遍历漏洞(CVE-2025-55747)引起了我的注意。这个漏洞编号看着新鲜,但本质上又是一个经典的“输入验证不严”导致的安全问题。简单来说,攻击者…

2026/7/4 16:30:48 阅读更多 →
SpringBoot+Vue家政平台毕设实战:从工程化思维到生产级实现

SpringBoot+Vue家政平台毕设实战:从工程化思维到生产级实现

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度 你有没有过这样的经历:毕业设计选题时,面对“家政服务平台”这类看似普通的题目,感觉无从下手&a…

2026/7/4 16:30:48 阅读更多 →
PC微信小程序V1MMWX加密包逆向解析:AES+XOR双重加密原理与Python解密实战

PC微信小程序V1MMWX加密包逆向解析:AES+XOR双重加密原理与Python解密实战

1. 项目概述:为什么我们需要关注PC微信小程序的加密包?如果你是一名前端开发者、安全研究员,或者单纯对微信小程序的技术实现感到好奇,那么你很可能已经发现,直接从PC端微信获取到的小程序包(.wxapkg文件&a…

2026/7/4 16:30:48 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻