全栈实战:LlamaIndex + Flask + React 构建智能问答 Web 应用
pythonreturn jsonify({"error": "请提供查询文本"}), 400}), 200通过 LlamaIndex 与全栈技术的结合,我们实现了从文档上传、索引构建到智能问答的完整流程。这种架构不仅适用于企业知识库、智能客服等场景,还可通过扩展索引类型(如 TreeIndex)、优化检索策略(如 HybridRetriever)进一步提升性能。
我们在开发智能问答系统时,常常面临前后端集成与大语言模型(LLM)交互的挑战。如何让非结构化文档数据高效接入 LLM?怎样设计一个支持用户上传、查询、管理的全栈应用?本文将以 LlamaIndex 为核心,结合 Flask 后端与 React 前端,手把手教你搭建一个具备文档检索与智能回答能力的全栈 Web 应用。
一、技术选型与整体架构
核心技术栈:
- 后端:Flask(轻量级 Web 框架) + LlamaIndex(LLM 索引构建) + BaseManager(并发控制)
- 前端:React + TypeScript(组件化开发) + Axios(API 请求)
- 交互流程:
- 用户通过前端上传文档或输入查询
- 后端使用 LlamaIndex 构建 / 查询索引,并返回结构化结果
- 前端渲染回答及相关文档来源
二、Flask 后端开发:从基础 API 到索引管理
1. 快速搭建 Flask 基础服务
python
# flask_demo.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/")
def home():
return "LlamaIndex全栈应用后端"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5601, debug=True)
访问http://localhost:5601
即可看到 “LlamaIndex 全栈应用后端” 字样,基础服务搭建完成。
2. 集成 LlamaIndex 实现查询功能
(1)索引初始化与持久化
python
# 引入LlamaIndex组件
from llama_index.core import (
SimpleDirectoryReader, VectorStoreIndex, StorageContext, load_index_from_storage
)
import os
# 全局索引对象
index = None
index_dir = "./.index"
os.environ["OPENAI_API_KEY"] = "your-key-here" # 仅测试用,生产环境需配置管理
def initialize_index():
global index
storage_context = StorageContext.from_defaults()
if os.path.exists(index_dir):
# 加载已有索引
index = load_index_from_storage(storage_context)
else:
# 首次构建索引
documents = SimpleDirectoryReader("./documents").load_data()
index = VectorStoreIndex.from_documents(
documents, storage_context=storage_context
)
storage_context.persist(index_dir) # 持久化索引到磁盘
# 在Flask启动时初始化索引
if __name__ == "__main__":
initialize_index()
app.run(...)
(2)定义查询接口
python
@app.route("/query", methods=["GET"])
def query_index():
query_text = request.args.get("text")
if not query_text:
return jsonify({"error": "请提供查询文本"}), 400
query_engine = index.as_query_engine()
response = query_engine.query(query_text)
return jsonify({
"answer": str(response),
"sources": [node.get_content() for node in response.source_nodes]
}), 200
测试 URL:http://localhost:5601/query?text=what did the author do growing up
3. 高级功能:支持用户文档上传与并发控制
(1)多线程安全的索引服务器
python
# index_server.py(独立进程)
from multiprocessing import Lock
from multiprocessing.managers import BaseManager
from llama_index.core import Document
index = None
lock = Lock()
def initialize_index():
global index
with lock:
# 索引初始化逻辑同上...
def query_index(query_text):
with lock:
return index.as_query_engine().query(query_text)
def insert_into_index(filepath, doc_id=None):
with lock:
document = SimpleDirectoryReader([filepath]).load_data()[0]
if doc_id:
document.doc_id = doc_id
index.insert(document)
index.storage_context.persist() # 插入后重新持久化
# 启动索引服务器
if __name__ == "__main__":
BaseManager.register("query_index", query_index)
BaseManager.register("insert_into_index", insert_into_index)
manager = BaseManager(("", 5602), b"password") # 端口5602,密码验证
server = manager.get_server()
print("索引服务器启动,端口5602")
server.serve_forever()
(2)Flask 对接索引服务器
python
# flask_demo.py(修改后)
from multiprocessing.managers import BaseManager
# 连接索引服务器
manager = BaseManager(("", 5602), b"password")
manager.register("query_index")
manager.register("insert_into_index")
manager.connect()
@app.route("/uploadFile", methods=["POST"])
def upload_file():
file = request.files["file"]
filename = secure_filename(file.filename)
filepath = os.path.join("documents", filename)
file.save(filepath)
try:
manager.insert_into_index(filepath, doc_id=filename)
return jsonify({"message": "文件上传并索引成功"}), 200
except Exception as e:
os.remove(filepath)
return jsonify({"error": str(e)}), 500
三、React 前端开发:构建用户交互界面
1. 封装 API 请求
typescript
// src/apis/index.ts
export const fetchDocuments = async () => {
const res = await fetch("http://localhost:5601/getDocuments", { mode: "cors" });
return res.json() as Promise<{ id: string; text: string }[]>;
};
export const queryIndex = async (query: string) => {
const url = new URL("http://localhost:5601/query");
url.searchParams.append("text", query);
const res = await fetch(url, { mode: "cors" });
return res.json() as Promise<{ answer: string; sources: string[] }>;
};
export const uploadFile = async (file: File) => {
const formData = new FormData();
formData.append("file", file);
formData.append("filename_as_doc_id", "true");
return fetch("http://localhost:5601/uploadFile", {
method: "POST",
body: formData,
mode: "cors"
});
};
2. 核心组件:查询界面
typescript
// src/components/QueryForm.tsx
import { useState } from 'react';
import { queryIndex } from '../apis';
const QueryForm = () => {
const [query, setQuery] = useState<string>('');
const [answer, setAnswer] = useState<string>('');
const [sources, setSources] = useState<string[]>();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await queryIndex(query);
setAnswer(result.answer);
setSources(result.sources);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="输入你的问题..."
className="w-full p-2"
/>
<button type="submit" className="bg-blue-500 text-white p-2 ml-2">
搜索
</button>
{answer && (
<div className="mt-4">
<h3>回答:</h3>
<p>{answer}</p>
<h4 className="mt-2">参考来源:</h4>
<ul>
{sources?.map((source, idx) => (
<li key={idx}>{source.substr(0, 100)}...</li>
))}
</ul>
</div>
)}
</form>
);
};
四、关键技术点解析
1. 并发控制与线程安全
- 问题:多用户同时上传文档可能导致索引损坏
- 方案:
- 使用
multiprocessing.Lock
确保同一时间只有一个线程操作索引 - 通过独立的
index_server.py
进程管理索引,避免 Flask 多线程冲突
- 使用
2. CORS 跨域处理
python
# Flask端安装flask-cors并配置
from flask_cors import CORS
CORS(app, resources={r"/*": {"origins": "*"}})
typescript
// 前端请求添加mode: "cors"
fetch(url, { mode: "cors" })
3. 文档管理增强
- 需求:跟踪已上传文档列表
- 实现:
- 后端新增
/getDocuments
接口,遍历索引节点元数据 - 前端渲染文档标题与摘要
- 后端新增
python
@app.route("/getDocuments", methods=["GET"])
def get_documents():
documents = []
for node in index.docstore.docs.values():
documents.append({
"id": node.doc_id,
"text": node.text[:200] # 取前200字作为摘要
})
return jsonify(documents)
五、部署与扩展建议
1. 本地部署步骤
- 启动索引服务器:
python index_server.py
- 启动 Flask 后端:
python flask_demo.py
- 启动前端:
cd react_frontend && npm start
2. 生产环境优化
- 索引存储:将
StorageContext
切换为云存储(如 S3) - 向量数据库:使用 Pinecone/Chroma 替代内存存储
- 用户认证:添加 JWT token 验证接口访问
- 负载均衡:使用 Gunicorn 部署 Flask,配合 Nginx 处理并发请求
结语
通过 LlamaIndex 与全栈技术的结合,我们实现了从文档上传、索引构建到智能问答的完整流程。这种架构不仅适用于企业知识库、智能客服等场景,还可通过扩展索引类型(如 TreeIndex)、优化检索策略(如 HybridRetriever)进一步提升性能。
如果本文对你有帮助,别忘了点赞收藏,关注我,一起探索更高效的开发方式~

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)