ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【RAG实战】从JWT认证到RAGAS评估:一个完整RAG系统上线前我做了哪些事(附踩坑记录)

2026/8/21 21:07:55 拓冰建站 浏览量
【RAG实战】从JWT认证到RAGAS评估:一个完整RAG系统上线前我做了哪些事(附踩坑记录) 系列文章本篇是第 4 篇 / 共 4 篇项目地址GitHub - RAG_Agent_project前情提要第 1 篇 | 第 2 篇 | 第 3 篇[引言]前三篇把 RAG 的核心技术讲完了架构设计、检索策略、混合检索精排。但一个 RAG 系统要从能跑变成能上线中间还差着一大段工程化的路。这篇讲的就是认证怎么做、数据库怎么设计、效果怎么量化评估以及那些让我反复调试的坑。认证与配额怎么控制谁能用、用多少JWT 认证# auth/auth_service.py SECRET_KEY your-secret-key ALGORITHM HS256 ACCESS_TOKEN_EXPIRE_HOURS 24 ​ def create_token(user_id: int, username: str, role: str) - str: payload { user_id: user_id, username: username, role: role, exp: datetime.utcnow() timedelta(hours24) } return jwt.encode(payload, SECRET_KEY, algorithmALGORITHM) ​ def verify_token(token: str) - dict: return jwt.decode(token, SECRET_KEY, algorithms[ALGORITHM])FastAPI 路由保护async def get_current_user(token: str Depends(oauth2_scheme)) - dict: try: payload verify_token(token) return payload except jwt.ExpiredSignatureError: raise HTTPException(status_code401, detailToken expired) except jwt.InvalidTokenError: raise HTTPException(status_code401, detailInvalid token) ​ router.post(/api/query) async def query(user: dict Depends(get_current_user)): # 受保护的接口 ...密码存储用的是 sha256 random salt不存明文import hashlib, secrets ​ def hash_password(password: str) - tuple[str, str]: salt secrets.token_hex(16) # 32位随机盐 hash_value hashlib.sha256((password salt).encode()).hexdigest() return hash_value, salt ​ def verify_password(password: str, hash_value: str, salt: str) - bool: return hashlib.sha256((password salt).encode()).hexdigest() hash_value邀请码与配额每个用户有total_quota总额度和used_count已用次数每次查询前检查。邀请码可以兑换额外额度。# 每次查询前检查配额 async def check_quota(user_id: int): user await db.get_user(user_id) if user[used_count] user[total_quota]: raise HTTPException(status_code403, detail配额不足请兑换邀请码) ​ # 兑换邀请码 async def redeem_code(user_id: int, code: str): invite await db.get_invite_code(code) if not invite or invite[used]: raise HTTPException(status_code400, detail无效邀请码) await db.increment_quota(user_id, invite[quota]) await db.mark_code_used(code)数据库设计7 张表各司其职-- 1. 用户表含配额 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password_hash VARCHAR(128) NOT NULL, salt VARCHAR(32) NOT NULL, role ENUM(user, admin) DEFAULT user, total_quota INT DEFAULT 100, used_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ​ -- 2. 邀请码表 CREATE TABLE invite_codes ( id INT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(32) UNIQUE NOT NULL, quota INT DEFAULT 50, used BOOLEAN DEFAULT FALSE, used_by INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (used_by) REFERENCES users(id) ); ​ -- 3. FAQ 知识库BM25 目标 CREATE TABLE management_faq ( id INT PRIMARY KEY AUTO_INCREMENT, question TEXT NOT NULL, answer TEXT NOT NULL, keywords VARCHAR(500), category VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ​ -- 4. 对话历史保留最近5轮作为LLM上下文 CREATE TABLE conversations ( id INT PRIMARY KEY AUTO_INCREMENT, session_id VARCHAR(36) NOT NULL, user_id INT NOT NULL, role ENUM(user, assistant) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); ​ -- 5. 问答审计日志记录来源、策略、耗时 CREATE TABLE qa_records ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, question TEXT NOT NULL, answer TEXT NOT NULL, source ENUM(faq, rag, hot_question) NOT NULL, strategy VARCHAR(50), response_time_ms INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); ​ -- 6. 热门问题缓存hit_count 3 自动进入 CREATE TABLE hot_questions ( id INT PRIMARY KEY AUTO_INCREMENT, question TEXT NOT NULL UNIQUE, answer TEXT NOT NULL, hit_count INT DEFAULT 0, last_hit_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ​ -- 7. 知识库版本追踪 CREATE TABLE kb_versions ( id INT PRIMARY KEY AUTO_INCREMENT, version VARCHAR(32) NOT NULL, description TEXT, doc_count INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );几个设计上的考量conversations vs qa_recordsconversations 只保留最近 5 轮对话作为 LLM 上下文窗口qa_records 保留全部问答记录用于审计、分析、统计。两个表职责不同不要混用。hot_questionshit_count 3时自动进入缓存。每次 RAG 回答后检查问题是否已存在存在则 hit_count1不存在则插入。被问 3 次以上的问题说明是高频问题直接缓存答案省掉 LLM 调用。密码安全sha256 random saltsalt 每人不同防止彩虹表攻击。Prompt 工程怎么让 LLM 稳定输出RAG 主 Promptdef rag_prompt(context: str, question: str) - str: return f你是一位管理学考试辅导专家专门帮助河南专升本考生解答管理学相关问题。 ​ 请根据以下参考资料回答用户问题 ​ 【参考资料】 {context} ​ 【回答要求】 1. 严格基于参考资料回答不要编造信息 2. 如果参考资料不足以回答请明确说明 3. 使用完整的专业术语不要使用缩写 4. 适当使用列表和对比表格便于记忆 5. 如果涉及多个知识点请分点阐述 6. 最后给出记忆技巧或考试重点提示 7. 如果无法回答请提供客服电话400-xxxx-xxxx ​ 用户问题{question}其他场景的 Prompt用途Prompt 核心指令HyDE假设你是一位管理学教授请针对以下问题给出一个简要回答子查询将以下问题分解为简单的子问题每行一个回溯将以下口语化问题转化为精确的学术问题策略选择根据问题特征选择检索策略只回答 A/B/C/D意图识别判断用户意图知识问答/闲聊/任务型设计原则temperature0.1确定性优先角色设定明确管理学辅导专家约束条件具体不编造、用术语、给记忆技巧。RAGAS 评估怎么量化 RAG 的效果RAG 系统最怕的就是感觉效果还行 —— 没有量化指标你就不知道改完之后是变好了还是变差了。4 个核心指标指标含义评估什么Faithfulness忠实度答案是否完全基于检索到的上下文有无幻觉Answer Relevancy答案相关性答案是否切题有没有答非所问Context Precision上下文精确度检索到的内容中有多少是真正有用的Context Recall上下文召回率回答所需的信息是否都被检索到了支持两种评估后端后端LLMEmbedding本地Ollama qwen2.5:7bmxbai-embed-large云端Qwen3.7-maxtext-embedding-v4# rag_qa/rag_assessment/ from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall ​ # 构造评估数据集 eval_dataset { question: [什么是决策, 比较泰勒和法约尔], answer: [rag_answer_1, rag_answer_2], contexts: [retrieved_docs_1, retrieved_docs_2], ground_truth: [standard_answer_1, standard_answer_2] } ​ # 执行评估 results evaluate(dataseteval_dataset, metrics[ faithfulness, answer_relevancy, context_precision, context_recall ])踩坑记录那些让我反复调试的参数坑1BM25 阈值 0.85 → 0.75一开始用默认阈值 0.85结果大量简单问题漏过 FAQ 层进入 RAG响应时间从 50ms 飙升到 3-5s。统计发现 FAQ 命中率只有 35%。降到 0.75 之后命中率提升到 62%整体响应时间下降 40%。教训阈值不是拍脑袋定的。建议先跑一批真实 query看 BM25 分数分布直方图再定阈值。坑2WeightedRanker 权重配比dense : sparse效果1.0 : 0.0纯语义术语匹配差0.5 : 0.5均衡但语义理解被稀释1.0 : 0.7最优语义为主关键词补充1.0 : 1.0稀疏权重过高噪声增大坑3父子块大小父块 / 子块问题800 / 200子块语义不完整检索噪声大1500 / 400子块太大和父块差距不大失去精准检索意义1200 / 300平衡点坑4表格处理当前表格扁平化处理会丢失行列结构。比如各领导理论对比表抽取后变成散乱文本LLM 很难理解行列对应关系。后续计划改为 Markdown 表格格式保留结构。技术选型总览技术选择理由EmbeddingBGE-M3稠密稀疏双向量中文 SOTARerankerBGE-Reranker-LargeCross-Encoder 精排精度远超 Bi-Encoder向量数据库Milvus原生混合检索 WeightedRankerLLMQwen3.7-Max中文强API 稳定性价比高WebFastAPI原生 async WebSocket切分ChineseRecursiveTextSplitter中文 6 级递归OCRRapidOCRPaddle/ONNX 双后端缓存RedisBM25 索引 热门问题双重缓存评估RAGAS4 核心指标支持本地/云端系列总结4 篇文章覆盖了整个 RAG 问答系统的核心技术栈篇目核心内容第 1 篇5 层架构 双层 Pipeline BM25 短路 意图识别第 2 篇4 种自适应检索策略HyDE / 子查询 / 回溯 / 直接第 3 篇BGE-M3 双向量 WeightedRanker Cross-Encoder 精排 父子块第 4 篇JWT 认证 数据库设计 RAGAS 评估 踩坑记录三个核心设计理念分层短路BM25 → 热门缓存 → RAG、自适应策略4 种覆盖所有问题类型、精准检索双向量 精排 父子块上下文恢复。项目地址GitHub - jlu55404-art/RAG_Agent_project: RAG问答系统 · GitHub系列完结有帮助欢迎 Star 支持问题评论区交流