拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于 LangGraph 的多智能体简历优化系统(二):RAG 事实约束引擎详解

系列文章之二。本文深入讲解项目的核心创新——RAG 事实约束引擎解决 LLM 简历优化中编造事实的根本问题。0. 问题LLM 简历优化的幻觉困境用 LLM 优化简历最大的痛点不是写不好而是编太多。你让 LLM 优化一段简历描述它可能会这样改原文负责后端接口开发使用 Java 和 Spring Boot优化了部分接口性能LLM 优化后常见幻觉主导后端核心接口架构设计与开发基于 Java Spring Boot 微服务框架通过引入缓存机制和 SQL 优化使接口响应时间降低 50%QPS 提升 3 倍看到了吗“主导”、“架构设计”、“缓存机制”、“SQL 优化”、“降低 50%”、“QPS 提升 3 倍”——原文一个都没有。这在面试中是致命的。面试官问“你说的缓存机制是什么SQL 优化具体怎么做的” 答不上来直接挂。传统方案是用 Prompt 约束“不要编造数据”、“只优化表达不改变事实”。但 Prompt 约束不可靠LLM 经常理解偏差该约束的地方不约束不该约束的地方反而过度约束。我的方案不依赖 Prompt用代码级别的硬约束——提取原文事实生成后校验分级处理。1. 事实约束引擎设计1.1 整体思路用户原文 ──→ 提取事实 ──→ LLM 优化 ──→ 校验生成 ──→ 分级处理 ──→ 最终输出 │ │ │ │ ▼ ▼ ▼ ▼ 数字集合 优化文本 新数字集合 硬约束删除 技术词集合 事实报告 新技术词集合 软约束标记 中文关键词 校验结果 对比结果 白名单允许增强核心思想事实是锚点优化是装饰。锚点不能动装饰可以加。1.2 事实提取importrefromtypingimportDict,Set,Tuple# 技术栈白名单常见的编程技术术语TECH_KEYWORDS{java,spring,spring boot,spring cloud,mybatis,mysql,redis,mongodb,elasticsearch,kafka,rabbitmq,docker,kubernetes,k8s,nginx,linux,git,maven,gradle,python,django,flask,fastapi,pandas,numpy,torch,tensorflow,langchain,langgraph,llm,rag,向量, embeddings,react,vue,angular,typescript,javascript,html,css,go,golang,grpc,protobuf,grpc,protobuf,aws,azure,gcp,aliyun,tencent cloud,k8s,docker,jenkins,github actions,gitlab ci,prometheus,grafana,logstash,kibana,filebeat,tcp,udp,http,https,websocket,rest,graphql,sql,nosql,orm,migrations,seed,微服务,分布式,高并发,缓存,消息队列,负载均衡,幂等,熔断,限流,降级,灰度,A/B,}# 允许增强的动词白名单可以升级表达但不改变事实ENHANCEMENT_VERBS{负责,主导,参与,设计,开发,实现,优化,重构,搭建,部署,维护,监控,分析,调研,评估,规划,协调,推进,落地,落地,落地,落地,}defextract_facts(text:str)-Dict[str,Set[str]]:从原始文本中提取可验证的事实锚点# 1. 提取所有数字整数和小数numbersset(re.findall(r\d\.?\d*,text))# 2. 提取技术关键词大小写不敏感text_lowertext.lower()tech_terms:Set[str]set()forkwinTECH_KEYWORDS:ifkw.lower()intext_lower:tech_terms.add(kw)# 3. 提取中文核心关键词2-4字的中文词组cn_keywordsset(re.findall(r[\u4e00-\u9fff]{2,4},text))return{numbers:numbers,tech_terms:tech_terms,cn_keywords:cn_keywords,}提取逻辑说明数字用正则\d\.?\d*提取所有数字包括整数和小数。这些是硬约束锚点——原文有的数字可以保留或增强比如优化了接口→接口响应时间降低 30%但原文没有的数字绝对不能出现。技术词维护一个技术栈白名单检查原文中出现了哪些。优化后如果出现了白名单外的技术词标记为可疑。中文关键词提取 2-4 字的中文词组作为语义锚点帮助判断优化是否偏离了原文语义。2. 校验与分级处理2.1 校验逻辑defvalidate_generation(generated:str,original_facts:Dict[str,Set[str]],)-Dict[str,any]:校验生成内容是否编造了新事实# 提取生成内容中的新数字new_numbersset(re.findall(r\d\.?\d*,generated))hallucinated_numbersnew_numbers-original_facts[numbers]# 提取生成内容中的新技术词generated_lowergenerated.lower()new_techset()forkwinTECH_KEYWORDS:ifkw.lower()ingenerated_lowerandkwnotinoriginal_facts[tech_terms]:new_tech.add(kw)# 提取生成内容中的新中文关键词new_cnset(re.findall(r[\u4e00-\u9fff]{2,4},generated))suspect_cnnew_cn-original_facts[cn_keywords]return{hallucinated_numbers:list(hallucinated_numbers),# 硬约束新增数字suspect_tech_terms:list(new_tech),# 软约束新增技术词suspect_keywords:list(suspect_cn),# 软约束新增中文词is_safe:len(hallucinated_numbers)0,# 是否安全risk_level:classify_risk(hallucinated_numbers,new_tech),# 风险等级}defclassify_risk(hallucinated_numbers:list,new_tech_terms:list,)-str:根据幻觉程度分类风险等级ifhallucinated_numbers:returnhigh# 高风险编造了数字ifnew_tech_terms:returnmedium# 中风险编造了技术词returnlow# 低风险安全2.2 分级处理策略defsanitize_generation(generated:str,original_facts:Dict[str,Set[str]],validation:Dict[str,any],)-Dict[str,str]:根据校验结果分级处理生成内容resultgenerated# 硬约束过滤编造的数字 ifvalidation[hallucinated_numbers]:fornuminvalidation[hallucinated_numbers]:# 删除原文没有的数字保留有原文数字的上下文resultre.sub(rf\b{re.escape(num)}\b,,result)# 清理多余空格resultre.sub(r\s{2,}, ,result).strip()# 软约束标记可疑技术词 suspect_termsvalidation[suspect_tech_terms]ifsuspect_terms:forterminsuspect_terms:# 用特殊标记包裹可疑技术词方便前端展示提醒resultresult.replace(term,f【{term}原文未提及请确认】)# 白名单增强允许动词升级 # 这部分不修改内容只是在 Prompt 中告诉 LLM 可以使用这些动词# 例如负责 → 主导参与 → 深度参与优化 → 深度优化return{sanitized_text:result,has_hallucinations:len(validation[hallucinated_numbers])0,has_suspect_terms:len(suspect_terms)0,risk_level:validation[risk_level],}分级处理策略总结级别触发条件处理方式示例硬约束新增了原文没有的数字直接删除该数字原文优化了接口生成降低50% → 删除50%软约束新增了原文没有的技术词标记为可疑保留内容原文没提Redis生成中出现了 → 标记【Redis原文未提及请确认】白名单增强使用白名单动词允许措辞升级“负责” → “主导”“参与” → “深度参与”3. RAG 知识库注入3.1 知识库结构项目内置了一个 STAR 案例库data/kb/star_cases.json包含 12 个高质量范例覆盖算法、后端、前端、数据、产品、运营等方向。每个案例包含{id:backend-java-001,field:backend,title:高并发订单系统重构,background:电商平台订单系统日均处理 10 万 订单...,architecture:采用 Spring Cloud 微服务架构...,responsibility:负责订单模块的核心开发...,results:接口响应时间从 500ms 降低到 50ms...,tech_stack:Java, Spring Boot, MySQL, Redis, Kafka...,star_example:S: 电商平台订单系统日均处理 10 万 订单...,quantification_phrases:[提升了...,降低了...,缩短了...,增加了...]}3.2 BM25 检索fromtypingimportList,TupleclassBM25Retriever:基于 BM25 的本地检索器def__init__(self,documents:List[str]):self.documentsdocuments self.idfself._compute_idf()def_compute_idf(self)-Dict[str,float]:计算逆文档频率doc_freq{}fordocinself.documents:wordsset(doc.lower().split())forwordinwords:doc_freq[word]doc_freq.get(word,0)1nlen(self.documents)return{word:math.log((n-count0.5)/(count0.5)1)forword,countindoc_freq.items()}defsearch(self,query:str,top_k:int3)-List[Tuple[str,float]]:检索最相关的文档query_wordsset(query.lower().split())scores[]fori,docinenumerate(self.documents):doc_wordsset(doc.lower().split())# BM25 简化版基于词频和 IDF 的加权评分score0forwordinquery_words:ifwordindoc_words:tfdoc.lower().count(word)idfself.idf.get(word,0)scoretf*idfifscore0:scores.append((doc,score))# 按评分排序返回 top_kscores.sort(keylambdax:x[1],reverseTrue)returnscores[:top_k]3.3 注入到 Prompt在简历优化的 Prompt 中检索到的 STAR 案例会被注入到 System Promptdefbuild_optimize_prompt(resume_text:str,original_facts:Dict[str,Set[str]],retrieved_cases:List[Tuple[str,float]],)-str:构建优化 Prompt注入 RAG 检索结果# 拼接检索到的 STAR 案例rag_contextforcase_text,scoreinretrieved_cases:rag_contextf【参考案例】{case_text}\npromptf 你是一个专业的简历优化专家。请根据以下规则优化用户的简历 ## 事实约束硬性要求 - 原文中的数字如10万用户、30%提升**必须保留** - 原文中没有的数字**绝对不能出现** - 原文中的技术栈关键词**必须保留** - 原文中没有的技术词**不能随意添加** ## 优化规则 - 使用 STAR 法则重构项目描述背景 → 架构 → 负责 → 成果 → 技术栈 - 允许使用白名单动词升级表达负责→主导、参与→深度参与 - 量化成果时只能使用原文中的数字或合理推算 ## 参考案例{rag_context}## 用户原始简历{resume_text}请输出优化后的简历内容Markdown 格式。 returnprompt4. 效果对比4.1 优化前无事实约束原文负责后端接口开发使用 Java 和 Spring Boot优化了部分接口性能LLM 优化无约束主导后端核心接口架构设计与开发基于 Java Spring Boot 微服务框架通过引入 Redis 缓存机制和 SQL 查询优化使接口响应时间降低 50%QPS 从 100 提升到 500问题“主导”、“架构设计”、“Redis 缓存”、“SQL 优化”、“降低 50%”、“QPS 提升 5 倍”——全部编造。4.2 优化后有事实约束原文负责后端接口开发使用 Java 和 Spring Boot优化了部分接口性能LLM 优化有约束负责后端接口开发使用 Java 和 Spring Boot优化了部分接口性能提升了系统稳定性事实校验数字原文无数字生成无新增数字 ✅技术词Java、Spring Boot 原文有 ✅动词“负责” 原文有未升级为主导 ✅新增内容“提升了系统稳定性”——软约束标记为可疑但因为是通用描述保留面试应对面试官问优化了什么接口怎么优化的 用户可以基于原文如实回答不会被编造的内容坑死。5. 工程实现细节5.1 节点集成事实约束引擎集成在 LangGraph 流水线的verify_facts节点中asyncdefverify_facts_node(state:ResumeOptimizeState)-ResumeOptimizeState:事实校验节点过滤编造内容标记可疑信息original_textstate[original_resume]optimized_textstate[optimized_items]# 1. 提取原文事实original_factsextract_facts(original_text)# 2. 对每个优化项进行校验sanitized_items[]foriteminoptimized_text:validationvalidate_generation(item[optimized],original_facts)resultsanitize_generation(item[optimized],original_facts,validation)# 3. 附加校验报告item[validation_report]{risk_level:result[risk_level],has_hallucinations:result[has_hallucinations],has_suspect_terms:result[has_suspect_terms],}item[optimized]result[sanitized_text]sanitized_items.append(item)return{**state,optimized_items:sanitized_items,fact_check_passed:all(notitem[validation_report][has_hallucinations]foriteminsanitized_items),}5.2 前端展示校验报告会传递给前端在优化结果中展示风险等级// 前端渲染优化结果div v-foritem in optimizedItems:keyitem.iddivclassrisk-badge:classitem.validation_report.risk_level{{{low:安全,medium:需确认,high:有风险}[item.validation_report.risk_level]}}/divdivclassoptimized-textv-htmlitem.optimized/divdiv v-ifitem.validation_report.has_suspect_termsclasssuspect-terms以下技术词原文未提及请确认{{item.validation_report.suspect_terms.join(、)}}/div/div6. 总结RAG 事实约束引擎的核心价值硬约束过滤用代码级别的数字校验杜绝 LLM 编造数据软约束标记对可疑内容做标记提醒不直接删除避免过度约束白名单增强允许合理的措辞升级提升表达质量RAG 注入用真实案例引导 LLM 学习 STAR 写法而不是凭空想象这个机制使得优化后的简历忠实于用户原始素材同时显著提升表达质量。用户反馈“优化后仍像自己写的但更专业了。”*作者达不溜记 | 项目地址https://gitee.com/qinghe-sixteen/multiagentmianshizhu系列文章预告系列三LangGraph 简历优化流水线实战8 节点详解系列四全栈开发实战FastAPI Vue3 前后端联调系列五踩坑记录与工程实践
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门