资讯动态

基于RAG的AI全息智能体:从三千问知识库到多轮对话实践

发布时间:2026/10/21 17:03:08 来源:尧图企业网站定制
前阵子我拿到一套署名“天辛大师”的命理问答资料里面是三千多个由浅入深的问题覆盖了好几个传统术数方向。资料本身已经够扎实但我的第一个反应却是如果只是把它做成一本电子书那太浪费了。一位朋友提醒我可以试着把它做成一个“AI全息智能体”——用户随时来问AI能基于这套内容库作出完整回答还能结合对话上下文形成一套“用户画像”。这个想法有点意思。于是我用两周时间把这个项目从“一堆文档”做到可上线Demo内容包括知识库结构化、向量检索、意图识别、多轮记忆、回答校验与安全提醒最后还留出了可扩展的API接口。整体跑下来最大的感受是命理类内容天然适合智能体但也是最容易“翻车”的内容类型因为用户会把回答当结论用而不是当参考。这篇文章就把整个思考、架构、实操过程和一些踩坑记录完整写出来给所有想做传统文化内容智能化、知识问答型Agent的人一个参考。1. 项目整体设计为什么把“三千问”做成智能体1.1 一本书和一个Agent的真正不同很多人觉得把一套书做成AI问答无非是把PDF丢给一个大模型然后等着它“背答案”。但实测下来远不是这么简单。书的结构是“作者讲→读者听”问答录类书籍虽然已经有问题、有答案但读者通常知道自己不知道什么才会去翻书。而Agent面对的是一堆不会提问的用户你说“我最近运气一般”它得自己判断你想问财、问婚姻还是问事业开运。你说“我的盘里有什么问题”它必须知道你出生日期、性别、甚至现在处于什么年龄段。所以项目的第一步不是写代码而是把内容从“书的结构”转成“知识库的结构”。每一问都要拆出中心主题、适用人群、关键词、结论、依据、使用边界。三千个问题看起来多真正难的是让它们之间形成一张“可被检索和引用”的知识网络。一个智能体如果回答得再漂亮但引用不到原文里对应的判断逻辑那它就是一本“编答案的书”而不是“能查证的书”。1.2 “全息”到底指什么这里的“全息”不是摄像头扫描那种全息投影而是强调“多维度、多角度地理解用户”。同一个问题不同人问答案应该不一样。比如同样问“今年适不适合跳槽”应届毕业生和中年转行者需要的信息完全不同一个只想知道“要不要行动”的人和一个想理解“为什么”的人回答深度也要区分。因此我在设计里加了几个层次第一层是基础的问答检索第二层是用户画像记忆第三层是回答时的“视角切换”。用户画像里存的是出生日期、性别、当前关注主题、之前问过哪些问题、对哪些方向反馈过“有道理”还是“不准”。这样每次回答都像同一个老师面对同一个学生而不是每次都是陌生人。说直接一点传统QA只要“答得对”问答型Agent要“答得稳”而全息智能体要做到“答得像懂你”。这个目标很高实际操作时需要取舍。我从第一版起就只把“全息”定义成“在对话中维护一份可落地的用户档案”而不是去追求真能解读人的状态。这个定义帮我砍掉了很多不切实际的功能幻想。1.3 内容边界它是传统文化参考不是科研结论做命理类Agent绕不开一个问题这类内容到底算不算迷信我自己的处理原则是把它当作传统文化与民俗知识体系来处理标题就叫“传统文化参考库”而不是“命运预测结论库”。具体落地上有三条底线。第一任何回答都要带有“传统理论认为”“仅供参考”这类边界说明第二不做医疗、投资、法律、人生重大决策方面的建议哪怕用户主动问也必须把“务必咨询专业人士”放在最后一段第三不渲染宿命论不强调“你必须信”而是用“传统知识体系里通常怎么解读”这种表述。这三条不是给平台看的免责声明它们是在保护用户也是在保护开发者自己。很多智能体项目做到最后不是因为技术挂了是因为内容安全问题挂了。2. 知识库搭建三千个问答是怎么变成数据的2.1 从纸质书或文档到结构化QA资料拿到手先把格式统一。文档来源很杂有的是手打文本有的是PDF截图还有几页直接是图片。我先用OCR过了一遍再靠人工把明显识别错误的地方校正回来。这个环节听起来笨但绝对不能省。清洗之后我给每一条问答设计了预格式question_id: 唯一编号 category: 所属大类基础术语、八字、大运流年、婚姻、事业、健康等 question: 原始问题 answer: 原始答案 keywords: 关键词列表用逗号分隔 tags: 自定义标签用于辅助检索 source: 章节编号一个容易忽略的细节是很多问题是“复合问题”。比如“我去年财运一般今年换了城市会不会好转”这里既涉及财运又涉及换城市、大运流年变化。如果只把它当成一个整句去存检索时很容易漏掉“换城市”相关的答案。所以我在结构化时把这类复合问题拆成了23条子记录每条子记录共享同一个question_id但keywords和tags不同。这样后面做向量检索时每一条碎片都能被单独命中。2.2 分块和向量化的参数设计一套回答性问题库最麻烦的不是存储而是切块。太长则检索不精确太短则没有上下文。我最后用的分块策略是正文以512字为一个块重叠64字标题和keywords单独切块不塞正文。这样做的理由有两个一是512字大约能覆盖一个完整的问答结论不至于截断到一半二是重叠64字可以保证跨段落的语义不会被硬生生切断。我用表格简单总结一下分块对象分块大小重叠检索权重标题整句不分0高keywords整句不分0高正文512字64字中结论句单独抽取0中高向量化阶段我选的是一个中文Embedding模型。选择时只比较了两点中文长文本的检索召回率以及实际推理速度。队里有人建议直接用大的多模态模型做向量试了一次之后放弃因为千问级别的内容里“术语多、句子短、隐含关系强”小模型配好关键词权重反而更容易调准。2.3 混合检索为什么不能只用向量纯向量检索有一个很典型的问题用户问“我能不能创业”可能书里恰恰写的是“命中带财是否适合经商”。字面完全不同但语义接近向量模型能把它们拉在一起这很好。但反过来用户问“子平八字里的伤官是什么意思”这属于术语查询必须精确命中“伤官”这个关键词如果单纯靠语义检索模型可能把“食神”“偏印”都拉出来回答反而失真。所以我用了混合检索BM25做关键词召回向量模型做语义召回再把两路结果合并排序。关键词召回负责“精准匹配”向量召回负责“扩大范围”。合取之后用RRFReciprocal Rank Fusion重排让在两种检索里都排在前面的结果优先返回。这步改动让知识命中率提升明显。下面是我用的重排片段思路def rrf_fusion(keyword_hits, vector_hits, k60): score {} for rank, doc in enumerate(keyword_hits): doc_id doc[question_id] score[doc_id] score.get(doc_id, 0) 1 / (k rank 1) for rank, doc in enumerate(vector_hits): doc_id doc[question_id] score[doc_id] score.get(doc_id, 0) 1 / (k rank 1) return sorted(score.items(), keylambda x: x[1], reverseTrue)实际上这段代码最终被我封装成了检索服务里的一个小函数输入是两个召回结果列表输出是融合后的排序ID列表。代码不长但对比之前的“单向量检索”知识命中成功率从七成上下提升到了九成以上。3. 智能体核心模块拆解与实现3.1 识别用户意图和抽取命理参数一个命理Agent不会天生知道用户想干嘛所以第一步一定是意图识别和参数抽取。我处理的常见意图包括问运势、问婚姻、问事业、问学业、问健康、问术语解释、问择日、闲聊等。前六类需要走知识库检索术语解释走术语索引闲聊直接走通用对话。参数抽取是重点。命理问题里最常见也最关键的参数是“出生日期”和“性别”。用户说话的方式五花八门“我是96年3月初八凌晨两点出生的”“1996年3月8日大概下午”“公历96/03/08女”。这些都要被抽取成标准结构化字段才能做后续的“排盘”或者“找对应知识”。我用大模型来做这一步让模型返回固定格式JSON{ intent: career, birth_datetime: 1996-03-08T02:00:00, gender: female, question_type: 近期决策, valid: true }有了这个JSON后面知识库检索就知道该拿哪些关键词去查。这里我给一个建议参数抽取Prompt里一定要附加“如果用户没提供完整出生时间不要臆造直接标null”。因为AI一旦猜一个时间后面所有答案都会在错误地基上跑偏用户感受会非常差。3.2 三步式回答生成管线整个回答生成我拆成三步检索、拼装、校验。检索阶段把抽取出的意图、关键词、用户画像里的历史主题全部作为查询条件从知识库里混合检索出TopK条内容。拼装阶段把这些内容连同原始问题注入Prompt要求模型在引用原文时写明出处比如“参考《问答集·事业篇》第几则”。校验阶段再单独让模型过一遍输出检查是否有“这则原文里没有的新结论”。如果没有原文支撑必须删掉或改成“传统理论里较少提到这一点仅供参考”。拼装阶段的Prompt我改了很多版最终沉淀下来的核心写法大约是你是一个传统文化命理问答助手。回答只能基于以下知识片段。 知识片段1... 知识片段2... 要求 1. 先直接回答用户问题再补充依据。 2. 不要把知识片段里没有的信息写成确定结论。 3. 如果多个片段说法不一致要坦诚说明这是不同流派或不同条件下的解释。 4. 结尾加一句以上内容仅作为传统文化知识参考。这套“检索即引用、生成即校验”的架构把幻觉率压到了一个可控级别。虽然系统的回答自由度明显变低但用户要的是稳定和可追溯不是模型的即兴发挥。3.3 对话记忆和“全息画像”实现全息画像本质上是维护一份“用户状态档案”。我用一个JSON文件做存储每次用户发消息后更新。里面包含了基础字段user_id、birth_flag、birth_datetime、gender、topics_history、tone_preference、last_interaction、question_log。这份档案的作用在第三轮对话之后才会明显体现。举例来说用户第二问“那我的姻缘呢”系统已经知道ta性别为女、生日为1996年3月8日、上一轮问的是事业。于是第三轮的回答从一开始就按“已有完整出生信息、女性、当前关注从事业转向婚姻”的方向去组织和检索而不是重新问一遍生日。这种“记住前文”的能力才是全息画像真正值钱的地方。我在记忆管理上用了很笨但很稳的方案只存关键参数不存完整聊天记录。完整记录交给会话保存画像只维护“影响回答的参数”避免把无关闲聊塞成脏数据。这套方案省内存也好调试。3.4 安全边界与免责语气的实测方法安全Prompt要写得“不像免责声明”。如果每次回答都堆一长串“本内容仅供娱乐不构成任何建议”用户很快就烦了。我最终采用的是“轻提示模式”开篇不提示让回答自然开始中段如果涉及敏感领域理财、健康、婚姻决策在转折处补一句“传统理论认为但现实决策还需要咨询对应领域的专业人士”结尾固定加“以上内容仅作为传统文化知识参考”。实测下来这种表达方式既没有减弱回答的密度也能尽量避免用户把结论当行动指南。对于咨询“要不要投资”“要不要手术”这类问题系统会直接拒绝展开只回复“这类建议超出命理内容范围请你务必咨询专业机构”。这不是保守是必须有的护栏。4. 实操跑通技术栈选择与Demo过程4.1 我用的技术栈和选择理由技术选型不用追求最好追求最快验证加上后期可替换。我的基础栈是Python 3.11 FastAPI faiss-cpu向量索引直接用后端内存加载LLM统一走OpenAI兼容格式的推理API。选FastAPI是因为它自带Swagger文档方便前端对接选faiss-cpu是因为Demo阶段不需要上GPU。如果数据量到了几十万条再把索引迁移到专用向量数据库也不难。整个项目目录被我拆成四个模块loader、retriever、agent、api。loader管数据加载和向量化retriever管混合检索agent管Prompt拼装和模型调用api管参数校验和路由。模块之间全部用接口连接方便哪天把任意一块替换掉。很多人写智能体喜欢把逻辑全堆在一个文件里前期爽后期调参和排错会想死。4.2 核心代码API和RAG管道片段为了说明白整个链路我给出一段经过简化的核心调用流程。这个流程基本对应Agent主接口from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): user_id: str message: str app.post(/agent/chat) def chat(req: ChatRequest): # 1. 抽取意图和参数 parsed agent.extract_intent_and_params(req.message) # 2. 维护用户画像 profile memory.load(req.user_id) profile.update_history(parsed) # 3. 混合检索 docs retriever.hybrid_search( intentparsed[intent], keywordsparsed[keywords], profileprofile ) # 4. 拼装Prompt并生成 reply agent.generate(parsed, docs, profile) # 5. 安全校验与免责补充 reply agent.safety_check(reply, parsed[intent]) memory.save(req.user_id, profile) return {answer: reply}实际项目里这个接口背后的逻辑会比上面长不少参数抽取失败要回滚检索结果为空要换关键词生成结果要检测“超长”还是“过短”。但整体骨架就是这五步先跑通再优化比一开始就加一堆状态机稳妥得多。4.3 一个测试对话的完整过程为了说明“全息”在实践里是什么效果我记录了一段真实测试对话。用户第一轮问“我是98年10月12号下午5点出生的女生想问一下明年适不适合自己做点小生意。”系统抽取出生时间、性别、意图为“事业/创业”从知识库里检索出与“创业时机”“经商适合性”相关的多条记录。回答先指出传统理论中这类出生时间配置下的几个常见分析点再说明做生意更适合从哪些细分领域切入结尾加了一个“传统理论认为具体结果要看大运流年”的边界说明。用户第二轮追问“那你觉得我前面适合合伙吗”这里没有重新给出生信息系统靠用户画像自动补齐再结合上一轮里出现的“创业”主题把“合伙模式”相关内容也检索进来。回答里主动提到“你上一问里提到想自己做事这一问转向合伙在传统理论里需要看合作者关系带来的影响”用户会明显感觉系统在“延续对话”。这就是我认为的“全息感”不是玄学是信息被连贯地记忆了。4.4 评估与上线检查表项目测试阶段我列了一张验收清单用它来防止过度自信。这里分享出来检查项通过标准我的实测结果知识命中率随机50问能检索到对应原文46/50命中引用可追溯答案能指出参考章节或问答编号基本通过重复稳定性同一问题连问5次核心结论一致通过参数缺失处理不瞎编出生时间通过边界问题健康、投资类问题不展开建议通过长对话记忆第10轮仍能记得首轮关键参数通过回答速度P95小于3秒通过这张表我也会发给业务伙伴看让他们知道“可以上线”和“还凑合”的差距在哪。没有验收清单的智能体项目往往一上线就翻车。5. 踩坑记录与排查方法5.1 命中率低把标题和标签加进索引后才好转踩的第一个大坑是纯用正文向量检索导致很多问题命中不到。原因很简单用户不会按书本里的句子提问。书里写“财星为用者宜稳定职业”用户问“我该不该找个班上”这两句话语义有重叠但距离很远。后来我把标题和keywords单独作为索引块同时把检索权重提高命中率立刻上去。这类问题值得所有做知识问答的人引以为戒索引设计远不止文本切块而是要对齐用户的“真实问法”。5.2 模型把“天干地支”解释得前后矛盾大模型面对传统术语时会“自由发挥”。我在一次测试里问同一个术语两次回答居然给出了不同定义。排查之后发现问题出在知识库覆盖不足原文里这个词本来就有不同流派的解释但我没有把“流派分支信息”单独建条记录模型只能自己补。这就是知识库脏数据的问题。解决方法是给每一条涉及定义的内容增加“flux”字段标明所属流派或使用场景。如果原文没给出明确界定那这类内容就不允许被当作通用定义回答只能按“某种理论下……另一种理论中……”的口径去说。自那以后前后矛盾的问题就很少出现了。5.3 用户追问时上下文丢失session summary是关键早期把对话历史原样塞进Prompt里一长就爆一爆就丢。后来我改成一个轻量总结器每一轮对话结束后把这一轮的核心问题、抽取参数、回答结论浓缩成一条摘要再存入记忆。上下文长度被控制得很小但关键参数全都在。这条经验对任何客服型Agent都适用对话历史的读写要围绕“状态递进”来设计而不是“原样回放”。5.4 这些“坑”的共性Agent退化是常态要监控所有踩坑背后其实只有一个规律Agent每次生成的自由度都很高换个词换个句式都可能影响结果。必须把“生成结果稳定”当成持续运行的指标去监控。我在项目里设置了每日随机采样测试每次跑30个固定问题对比回答之间的核心结论一致性。有几天测试不跑稳定率就开始掉。想省这一步的人最后都会花更多时间在用户投诉上。6. 项目复盘、扩展方向与个人体会6.1 这套方案还能复用到哪些内容场景做到最后我发现这套思路不只适合命理内容。任何“有大量问答对、需要结合用户背景做个性化回答”的场景都能套用。比如企业内部规章制度问答、金融产品知识普及、历史知识科普、某类技能培训体系的问答库。核心都是同一件事把内容结构化把用户画像化把生成过程拆成“检索→拼装→校验”。差别只是术语体系和回答语气。6.2 后续可以做的三个扩展第一接入数字人播报让回答变成口播稿虚拟人画面适合短视频分发。第二增加实时“排盘”能力输入出生时间后动态生成盘面数据把知识库内容映射到盘面解释上这个方向对专业用户吸引力会很强。第三做移动端语音问答入口语音转文字后走同一套Agent逻辑适合中老年用户群体。这三个扩展我都想清楚了一期目标第一期只做数字人口播因为它的价值最容易感知。6.3 我的几点真实体会我自己的体会是这类传统文化内容智能体的难点不在技术而在“把握分寸”。技术再强如果系统给出的回答让用户把命理判断当成人生决策依据那这个项目迟早出问题。分寸感要通过数据准备、Prompt设定、安全校验三层一起守而不是靠一句话提示词。另一个真实感受是结构化处理文档的时间远超写Agent的时间。内容质量决定Agent上限模型决定下限。我对这件事的认识从“项目上线”之后才真正深刻起来——没有一套高质量的知识库再聪明的模型也只是个“幻觉放大器”。最后提醒想做类似项目的人先把边界想清楚再写第一行代码。想清楚“这个Agent不能回答什么”比想清楚“它能回答什么”更重要。建议从一个小而准的问答集开始不要一上来就追求三千问、一万问。把两百个高质量问答吃透了做出稳定可验收的Demo再逐步扩量才是最稳的路径。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑