【7万字长文,含案例】基于DeepSeek私有化部署RAGFlow行业知识库和智能体Agent,完美实现知识图谱和低代码开发

本文给出了AI知识库和Agent智能体在各个行业的应用场景,并详细演示了DeepSeek+RAGFlow的私有化部署过程、总体架构、知识库和Agent智能体搭建、核心模块代码等细节,以及如何完美实现知识图谱和低代码开发。

在信息爆炸的时代,每个与知识打交道的从业者都曾经历过这样的“至暗时刻”:

  • 法律行业:为了一份诉状,不得不在堆积如山的纸质卷宗、邮件和聊天记录中大海捞针,还要在多个法律数据库中反复检索相似判例,耗时耗力,心力交瘁。
  • 金融风控:客户经理面对海量的发票、流水数据和上下游合同,人工分析不仅效率低下,还容易遗漏关键风险点,企业财务状况如同一团乱麻,难以快速形成清晰的风控结论。
  • 电商客服:当客户咨询“敏感肌精华成分”时,客服不得不手动比对10+竞品手册,产品信息分散在100+份Excel参数表和PPT培训材料中,效率低得令人抓狂。
  • 制造业:新员工查询设备故障代码耗时超过30分钟,面对“主轴轴向窜动≤0.01mm”这样的专业术语,新人一脸茫然,导致设备故障长时间停工,生产效率大打折扣。
  • 医疗行业:医生在门诊开处方时,查找药品信息和治疗方案耗时耗力,病人排起长队,医患双方都倍感压力。
  • 教学课程设计:知识更新换代太快,教师每天大半时间花在教学资料、课程资源和学情分析上,真正用于教学的时间所剩无几。
  • 企业内部系统:OA、CRM、ERP、MES、OSS、BSS、SCM、PLM等系统数量繁多,员工经过大量培训后仍然无法上手使用,更别提编写SQL语句生成报表了。
  • 财务会计:每天审核员工的费用报销,如采购发票、火车票、机票和酒店住宿发票,繁琐的工作让人疲惫不堪。
  • HR招聘:每天筛选大量简历,却依然找不到合适的人选,招聘效率低下,人才流失严重。
  • 学术研究:研究人员查阅科研文献,无法快速定位信息来源,宝贵的时间浪费在无效的检索中。
  • 行业研究:每天花大量时间检索PDF财报信息,向AI提问财报细节,却得到胡编乱造的虚假数据,研究效率大打折扣。
  • 专利查新:无法快速定位专利权利要求中的技术特征,专利撰写和审核效率低下。
  • 法务审核:在成百上千份PDF中寻找某个关键条款,却被影印件的模糊文字难住,关键法律条款容易被遗漏,精准检索成为奢望。

上面这些痛点是当今知识工作者的共同挑战:信息过载、非结构化数据难以处理、专业知识壁垒高筑……

而这正是AI知识库大有可为的领域。

一、DeepSeek+RagFlow搭建AI知识库效果展示

职业教育云平台建设方案知识图谱

注:博主为某省编写的职业教育云平台建设方案(20万字)的知识图谱。

知识图谱将该方案中的政策背景、项目干系人、平台模块、业务实体、系统功能之间的逻辑关系展示得淋漓尽致、一览无余。

如:职业教育相关行业政策“六大新兴产业”、产业集群、专业群、1+X证书、学分银行、双元制大学、校企合作、产教融合、职业技能竞赛、微课资源、智慧校园等。

职业教育云平台建设方案知识图谱细节展示

《权力的游戏》知识图谱

注:根据美剧《权力的游戏》(Game of Thrones)构建的知识图谱,展示了不同角色和阵营的主要叙事线,也可以理解为故事的子情节或叙事分支,涵盖了政治、战争、家族、成长、性别与权力等多个维度。

《权力的游戏》龙母Daenerys的人物关系

知识图谱也可以显示为思维导图

文档处理的可视化和可解释性

注:用户上传的文档到底被处理成啥样了,如:分割了多少片,各种图表处理成啥样了,RAGFlow 不仅给出了处理结果,而且可以让用户查看文档解析结果并一次点击定位到原文,对比和原文的差异,可增可减可改可查。

上图为导入PDF版行业研究报告《智能机器人产业研究报告2025》后,知识库的chunking模块能够将其中的非结构化数据提取成表格数据,方便后续精准检索。

鼠标悬停查看原文

注:鼠标悬停hover在回答结果中的内容引用编号上,即可随时调出原文内容,甚至包含图表;

用户从而能够掌握大模型是基于哪些原文来生成答案的。如果还不能确定,再点一下便能定位到原文。

通过将检索技术与生成式AI结合,能够从非结构化数据中提取关键信息,并生成准确、有据可依的回答。

无论是Word文档、PDF、图片,还是网页内容,DeepSeek+RAGFlow 都能轻松驾驭,真正实现“大海捞针”式的精准检索。

有理有据,最大程度降低幻觉

注:通过可视化文本切片过程,支持手动调整,让用户掌控数据处理细节。

引用支持:回答不仅准确,还附带关键引用的快照,用户可追溯到原始文档的具体位置。

解决痛点:AI“幻觉”(生成虚假信息)是许多大模型LLM的通病,RAGFlow的“有据可查”特性极大提升了回答的可信度。

深度文档理解

注:核心能力:基于深度文档理解技术,能够从复杂格式的非结构化数据中提取真知灼见。

无限上下文:支持超长文本处理,在无限token场景下快速完成检索任务,真正实现从海量数据中“捞针”的壮举。

财务发票凭证

财务发票凭证智能解析

财务发票凭证智能检索

注:财务会计每天审核员工的费用报销,如采购发票、火车票、机票和酒店住宿发票,可一键上传,问答自如。

兼容各类异构数据源

注:不仅能够解析Word、PPT、Excel、PDF、txt、CSV、网页、扫描件、图片(jpg、png、gif等)、结构化数据等100+丰富的文件格式,还能理解表格、公式、图表中的隐藏信息,让"大海捞针"变成"精准定位"。

灵活性:无论数据来源多么复杂多样,RAGFlow都能统一处理,降低用户切换工具的成本。

实际价值:对于需要整合多源信息的企业(如市场分析、客户支持),这是一大福音。

使用文字查询数据库text2sql

注:用户能够使用自然语言查询MySQL、SQL Server、Oracle、Redis等数据库,非技术人员不懂SQL语法也可以自定义美观的个性化报表。

基于模板的文本切片

注:提供多种文本切片模板,用户可根据文档类型选择最适合的分割方式,确保语义完整性。

可解释性:不仅智能切分内容,还能让用户理解切分逻辑,满足不同行业需求(如财务报告、学术论文)。

优势:相比传统“一刀切”的分段方式,这种模板化设计让检索更精准,操作更透明。

Agent智能体工作流

注:集成了完全自动化的 RAG 工作流,同时提供易用的 API,可以轻松集成到各类企业系统。

提供全面优化的RAG工作流,从数据上传到问答生成,一气呵成。

配置灵活:支持自定义大模型(LLM)和向量模型,基于多路召回和融合重排序提升检索效果。

易集成:通过简单易用的API,企业可将其无缝嵌入现有系统,满足从个人应用到超大型生态的多样化需求。


随着人工智能技术的飞速发展,AI知识库作为新一代知识管理系统,正在逐渐改变传统知识库的面貌,知识库逐渐向智能化方向发展。

AI知识库以其独特的优势正在逐渐改变传统知识库的面貌,并为各行各业带来革命性的变化。

AI技术正从“替代人类”转向“增强人类”。在信息过载成为常态的今天,能否高效激活“知识资产”的价值,已成为个体与组织竞争力的关键指标。正如早期试用者所言:“它像是一个外接大脑,让我随时调用毕生所学——这才是AI应有的样子。”

据Gartner预测,到2026年,超过80%的企业将使用AI技术进行知识管理,而个人用户对定制化AI工具的需求年增长率将达65%。

“未来的竞争本质上是知识处理效率的竞争。知识库不仅是信息存储工具,更是通过AI实现的认知加速器——它将人类从信息整理的低效劳动中解放,让我们能专注于创造性思考。”

AI知识库在技术原理、知识存储与表示、查询与检索、智能化程度与应用场景等方面与传统知识库存在显著差异。

AI知识库的核心是大语言模型LLM,这些模型通过对海量文本数据的学习,形成了对自然语言的理解和生成能力,通过自然语言处理和知识图谱构建技术,能够将用户上传的文档、数据、笔记等进行深度解析和重构,形成一套“量身定制”的知识服务体系。

AI知识库不仅整合了自然语言处理、机器学习等先进技术,还具备自动采集、分类、更新和推理的能力。通过对数据的深度挖掘和智能化分析,知识库能够实现动态更新、精准推理以及语义搜索,大大提高了信息检索的效率和准确性。

当信息检索具备推理能力,AI知识库将不再是冰冷的存储系统,而是会思考、能追溯、持续进化的“数字大脑”。


二、AI知识库的应用场景

RAGFlow应用场景

应用场景 1:电信运营商智能运维知识管理

痛点

  • 运维知识碎片化,跨系统运维手册和案例分散在不同平台,检索效率低

  • 故障处理依赖专家经验,新员工难以上手,知识传承困难

  • 网络告警量大且复杂,故障根因分析耗时长,影响业务连续性

解决方案

  • 智能知识聚合:整合多源异构数据,包括故障案例库、设备运维手册、操作规程和日志文件,构建统一知识中心

  • 故障根因智能分析:结合大模型和故障知识图谱,输入"基站掉线"或"信令拥塞"等问题,系统自动匹配相似案例和根因分析步骤

  • 智能问答与操作指引:支持一线运维人员通过自然语言提问,如"如何处理基站功率过低"或"信令流异常分析",系统实时返回最佳处理方案

  • 专家经验沉淀:通过文本分类与语义分析,将专家经验自动提取并结构化存储,形成知识复用和经验共享机制

  • 智能告警推荐:接入实时监控系统,基于历史数据和专家知识,对突发告警进行分级预警和处理路径推荐

  • 可视化运维监控:将故障处理进度和操作指引以流程图方式展示,降低操作复杂性,提高协同效率


应用场景 2:法律诉状智能生成与判例推荐

痛点

  • 案件证据材料分散在纸质卷宗、邮件、聊天记录中,人工整理耗时耗力

  • 人工检索相似判例需遍历多个法律数据库(如裁判文书网、北大法宝、威科先行等)检索匹配相似判例,耗时且易遗漏,效率低下

  • 法条引用和赔偿金额需手动计算,易出错且缺乏数据支撑

解决方案

  • 构建律所或者律师个人的私有案例库:自动解析裁判文书PDF中的争议焦点、法条引用、赔偿金额等要素

  • 智能证据链整理:自动解析PDF/邮件/聊天记录,提取关键证据(如转账记录、合同条款),生成结构化证据清单

  • 判例精准匹配:输入案由(如“劳动仲裁+工伤赔偿”),自动推荐近3年相似判例,自动关联《工伤保险条例》第37条及近三年同类型判决赔偿金中位数

  • 诉状一键生成:基于案情描述,自动生成起诉状框架,填充法条引用(如《工伤保险条例》第37条)

  • 证据链完整性校验:根据案由自动生成必备证据清单(如劳动合同、医疗鉴定书等)

  • 构建统一案件知识库: 支持扫描件、邮件、聊天记录导入,利用OCR和NLP技术自动提取关键信息(当事人、时间、争议焦点、证据项)

  • 智能判例推荐: 输入案件要素(如“劳动纠纷+经济补偿金+N+1”),系统自动跨库检索并推送高相似度判例,分析判决理由和赔偿金额分布

  • 诉状要素智能填充与生成: 基于模板库,根据输入的案件标签和提取的证据信息,自动填充诉状关键要素(诉讼请求、事实与理由),引用相关法条,并生成初稿

应用场景 3:金融客户经理风控初筛及智能分析引擎

痛点

  • 客户经理需处理海量、多源异构的客户数据(发票、流水、合同、工商信息等),人工分析耗时长、效率低

  • 企业财务与经营状况信息分散且非结构化,难以快速形成清晰的风控结论

  • 企业财务数据(发票、流水、合同)非结构化,人工分析耗时长

  • 关键风险点(如资金链断裂、履约异常)易被遗漏

  • 风控报告依赖手工编写,难以快速响应

  • 非结构化数据(如合同条款)中的风险点不易被发现,容易遗漏关键风险信号

  • 难以快速整合分散信息,形成全面、清晰的企业财务与经营状况画像及风控结论

解决方案

  • 信息结构化:通过大模型对发票数据、流水数据、合同条款等进行关键信息抽取(如合同金额、付款周期、供应链关联方等),并自动分类和标注(如信用风险、履约风险)

  • 多模态检索:基于RAG技术,支持客户经理输入如“企业流动资金占比异常”或“上下游履约风险”问题,系统快速检索合同条款、流水异常记录,并返回具体的风险点分析和建议

  • 本地部署保障数据安全:所有数据分析均在本地完成,满足企业客户对敏感信息保护的合规要求

  • 数据智能抽取:自动解析发票金额、付款周期、供应链关联方,构建企业关系图谱

  • 风险实时预警:输入“流动资金异常”,系统自动比对历史数据,提示潜在风险(如“近3个月应付账款逾期率上升20%”)

  • 风控报告自动生成:基于分析结果,输出结构化报告(含风险等级、建议措施)

  • 多源数据结构化提取: 利用大模型OCR和NLP能力,自动从发票、银行流水、合同文本中提取关键字段(如金额、日期、交易对手、付款条件、限制性条款、担保信息)

  • 风险因子智能识别与预警: 基于风控规则库和历史数据,自动识别异常交易模式(如大额频繁对私转账)、高风险合同条款(如苛刻的违约责任)、供应链风险(关联方信用问题),并进行标注和预警

  • 自然语言交互式分析: 客户经理可通过自然语言提问(如“分析该客户近6个月现金流状况”、“合同中有哪些主要履约风险点”),系统基于整合后的知识库快速检索、分析并生成摘要报告或风险提示

  • 本地化部署保障数据安全: 可选择本地部署模型和知识库,确保敏感客户数据不出企业内网,满足金融行业的合规要求

应用场景 4:电商智能客服知识中枢

痛点

  • 产品知识分散在大量Excel、PPT、Word文档中,查找特定信息(如成分、适用人群、竞品对比)困难且耗时

  • 面对客户具体咨询(如“敏感肌可用的精华有哪些成分?”),新人需手动查阅、比对多个资料,响应速度慢,影响客户体验

  • 新人培训周期长,难以快速掌握全部产品知识和销售技巧

解决方案

  • 构建成分/功效知识图谱:自动提取成分、适用肤质、禁忌搭配,构建成分冲突库(含酒精成分产品不可与 A 醇类产品叠加使用),关联成分与功效、适用肤质、禁忌搭配等,支持更专业的咨询解答(如“这款含A醇,不建议和高浓度VC产品同时使用”)

  • 智能问答助手:输入“敏感肌精华”,实时自动返回成分表+竞品对比(如“我方产品B5泛醇含量高于竞品3.2%”)

  • 构建统一产品知识中枢: 自动抽取、整合所有产品文档中的信息(参数、成分、功效、使用方法、FAQ、用户评价、竞品信息),形成结构化知识库

  • 智能问答与场景化精准推荐: 客服输入客户问题(自然语言),系统快速返回准确答案,并能根据客户描述(如肤质、需求)推荐合适的产品组合及理由,如客户提问“30岁油皮夏季护肤”,推送控油套装+搭配使用顺序+关联满减方案

  • 实时竞品对比: 输入客户提到的竞品,系统可即时调取本品与竞品的关键参数、成分、优势对比信息,辅助客服销售

应用场景 5:制造业生产线智能问答与排障

痛点

  • 设备手册庞杂(2000+页PDF),故障代码查询耗时>30分钟

  • 新人难以理解专业术语(如“主轴轴向窜动≤0.01mm”),无法准确执行操作规范或判断设备状态,导致故障处理慢、停机时间长

  • 维修依赖老师傅经验,知识传承困难

  • 生产设备操作手册、维护规程、历史故障记录等信息分散在不同的文档或系统中,一线工人查找耗时,尤其在紧急情况下

  • 设备报警或出现异常时,依赖资深技工经验判断,新人或跨岗员工难以快速准确地定位问题并执行标准处理流程(SOP),导致停机时间延长

  • 技术更新快,员工培训后知识遗忘或未及时更新,难以保证操作的规范性和效率

解决方案

  • 故障知识图谱:结构化设备手册(按故障代码/维护周期/精度标准打标签),关联故障代码(如E307→伺服电机过载)、维修实操视频片段索引(关联"E02报警"对应的齿轮箱拆装演示)

  • 多模态检索:支持语音/图片输入(拍摄异响部位),返回维修方案+AR拆装指引,输入"加工中心定位精度超差",返回导轨磨损检测流程及塞尺使用规范图示

  • 术语智能解释:输入“轴向窜动”,返回动画演示+实操标准

  • 构建统一设备知识库: 整合设备手册、SOP、维修日志、故障案例库、技术图纸等多源异构数据,利用大模型进行结构化提取(如故障代码、现象、原因、解决方案、所需工具、安全注意事项)

  • 智能对话式查询与引导: 员工可通过智能聊天系统输入自然语言(如“A机台 E05 报警如何处理?”)或上传故障图片/视频,系统快速检索知识库,提供精准的操作步骤、相关图纸或视频片段,并能进行多轮对话引导排障

  • 实时信息推送与知识推荐: 根据当前生产任务或设备状态,主动推送相关的操作要点、安全规范或常见问题预防措施。结合员工画像(如岗位、技能等级),推荐相关的技术更新培训资料或历史相似问题解决方案

应用场景 6:医疗临床辅助决策

痛点

  • 药品信息查询慢,门诊排队压力大

  • 药物相互作用依赖人工核对,易遗漏

  • 治疗方案更新快,医生难以及时掌握

  • 医学知识浩如烟海且更新迅速(如新药信息、临床指南更新),医生难以实时掌握所有最新进展,查找特定信息(如罕见病诊疗、药物相互作用)耗时费力

  • 面对复杂病例或不典型症状,医生需要快速检索和比对大量文献、病例、检验检查指标,容易遗漏关键信息或诊断方向,影响诊疗效率和准确性

  • 年轻医生临床经验相对不足,在独立诊疗时需要便捷可靠的知识支持和参考

解决方案

  • 药品知识库:结构化药品说明书,支持“妊娠期禁用”“肝肾功能调整”等条件筛选

  • 处方冲突检测:输入处方,自动提示禁忌组合(如“阿司匹林+华法林出血风险↑”)

  • 治疗方案推荐:基于患者病史,推送最新指南(如“2024版高血压用药首选ARB”)

  • 构建权威医学知识库: 整合最新的临床指南、医学文献摘要、药品说明书数据库、循证医学证据、经典/罕见病例库,并进行深度结构化处理(如按疾病、症状、检查指标、治疗方案、药物成分等标签化)

  • 智能语义检索与匹配: 医生可通过自然语言输入患者的关键信息(如“中年男性,咳嗽伴发热一周,有糖尿病史”),系统基于知识库快速提供可能的诊断列表(附依据)、推荐的检查项目、治疗方案选项(含药物剂量、禁忌、相互作用提示)

  • AI 辅助诊断与风险预警: (在确保数据安全与合规前提下)结合患者电子病历信息,系统可基于知识库进行模式识别,提示潜在的诊断可能性、高风险因素(如药物过敏史与拟用药冲突)、或基于指南的治疗规范偏离

应用场景 7:智慧教育知识平台与个性化辅导

痛点

  • 教师80%时间耗在找资料、做课件上

  • 跨学科知识(如物理-数学)关联困难

  • 学情分析依赖手工统计,反馈滞后

  • 学习资源(课件、习题、视频、参考资料)数量庞大但缺乏有效组织,学生难以根据自身学习进度和薄弱点快速找到最合适的资源

  • 教师难以实时、全面地掌握每个学生的知识点掌握情况和学习困难,进行个性化辅导和教学调整效率低

  • 学生在课后自主学习中遇到问题时,缺乏及时的答疑渠道,容易中断学习或积累疑问

解决方案

  • 教学资源库:自动归类教案/习题,按难度(★☆☆)、知识点(“牛顿定律”)标签化

  • 跨学科推荐:备课“电磁感应”,自动关联数学导数案例

  • 学情看板:基于作业数据,可视化班级薄弱点(如“楞次定律错误率60%”)

  • 构建学科知识图谱与资源库: 整合教材、教辅、课件、习题库、视频讲解、在线文章等资源,利用大模型构建精细的知识图谱(关联知识点、前后置关系、难度分级),并对资源进行多维度标签化

  • 个性化学习路径推荐与资源推送: 根据学生的学习行为数据(如观看时长、答题正误率、提问记录)和预设学习目标,系统智能评估其知识掌握水平,自动推荐匹配的学习资源、练习题或知识点讲解视频

  • 智能答疑与学情分析: 提供 7x24 小时的智能问答机器人,基于知识库解答学生的学科问题、概念辨析。同时,系统自动分析学生的学习数据,生成学情报告(如知识点掌握雷达图、常见错误类型),供教师参考以调整教学策略或进行针对性辅导

  • 自动化作业批改与学情分析: 对客观题、部分主观题进行智能批改,自动统计学生知识点掌握情况、错题分布,生成可视化学情分析报告,识别共性问题和个体差异

应用场景 8:企业系统智能助手(OA/CRM/ERP/MES/WMS/SCM/PLM)

痛点

  • 企业内部系统(OA、ERP、MES、HR等)众多且操作复杂,员工培训后仍难以上手,使用效率低

  • 跨系统查询信息或完成业务流程(如请购-审批-采购)步骤繁琐,易出错

  • 非技术人员难以通过写SQL等方式从各系统数据库中提取数据,生成所需报表

  • 多系统数据孤岛,信息整合困难

解决方案

  • 自然语言操作:输入“查上季度采购订单”,自动生成SQL并返回可视化报表

  • 流程引导:新手操作ERP时,AI逐步提示“入库单→财务审核→库存更新”

  • 多系统联动:关联OA审批与MES工单,自动同步进度

  • 构建企业统一知识入口与操作Agent: 整合各系统操作手册、FAQ、业务流程文档,并连接系统API(若可行)

  • 自然语言操作引导与执行: 员工用自然语言提问“如何提交报销申请?”或下达指令“帮我查询上个月的销售额报表”,AI助手提供分步操作指引,或直接调用API完成任务/查询数据

  • “自然语言到SQL”报表生成: 员工用大白话描述所需报表内容(如“给我看看各部门本月预算执行情况”),AI将其转化为对应系统的查询语句(如SQL),自动抓取数据并生成可视化报表

  • 跨系统流程自动化助理: 对于需要流转于多个系统的业务,AI可根据预设流程,引导用户在各系统完成相应步骤,或实现部分环节的自动化

应用场景 9:财务报销智能审核

痛点

  • 人工审核大量员工提交的费用报销单据(发票、车票、酒店水单等),工作重复、繁琐、耗时

  • 容易因疏忽导致审核错误,如票据合规性(真伪、抬头、日期)、费用标准、重复报销等问题

  • 审核效率低影响员工报销体验和资金周转

  • 合规检查(如差标超标)效率低

解决方案

  • 多票据智能识别与信息提取 (OCR + NLP): 自动识别各类发票、票据图片,提取关键信息(金额、日期、抬头、税号、消费明细)

  • 报销合规性自动校验: 基于预设的企业财务制度和报销标准(如差旅标准、招待标准),自动比对提取信息,判断是否超标、是否符合规定(如发票抬头是否正确)

  • 发票验真与查重: 对接税务系统接口进行发票真伪查验,并在历史报销数据中检查是否存在重复提交

  • 智能风险提示与分类处理: 自动标记异常或不合规单据,推送给人工复核;合规单据可加速处理流程

  • 规则自动执行:差旅报销自动比对公司标准(如“高铁二等座限报”)

  • 风险预警:标记“同一商户大额连刷”等可疑交易

应用场景 10:HR智能简历筛选与面试辅助

痛点

  • 招聘旺季收到海量简历,人工筛选耗时巨大,效率低下。

  • 难以快速、准确地从众多简历中识别出与岗位要求高度匹配的候选人,容易错失优秀人才。

  • 初步筛选标准可能受主观因素影响,存在不一致或偏见风险。

解决方案

  • 简历智能解析与信息结构化: 自动解析不同格式的简历(PDF, Word, 图片),提取关键信息(教育背景、工作经历、技能标签、项目经验)

  • 人岗智能匹配与排序: 根据招聘岗位要求(JD),自动计算简历与岗位的匹配度,并进行排序,优先展示高匹配度候选人

  • 关键信息快速筛选与搜索: 支持HR使用自然语言或关键词组合进行筛选(如“筛选出有3年以上Java开发经验且熟悉Spring Cloud的候选人”)

  • 人才库激活与推荐: 对历史简历库进行分析,当有新岗位发布时,主动推荐库内符合要求的潜在候选人

应用场景 11:学术文献智能检索与综述生成

痛点

  • 阅读大量科研文献时,难以快速定位到论文中具体章节、方法描述、实验数据或关键结论

  • 传统关键词检索无法精确定位信息在文献中的具体来源和上下文

  • 跨文献信息追踪和溯源困难,浪费大量阅读和查找时间

解决方案

  • 深度文献结构化索引 (RAG基础): 对导入的PDF文献进行精细化解析,识别章节结构(摘要、引言、方法、结果、讨论)、图表、参考文献,并建立索引

  • 基于内容的智能问答: 研究人员可针对单篇或多篇文献提问(如“这篇论文使用了什么统计方法分析数据?”“对比这几篇文献在XX材料制备上的异同”),系统直接定位到原文相关段落并给出答案及出处

  • 关键信息快速定位: 输入关键词或概念,系统不仅返回包含该词的文献,还能直接高亮显示其在文献原文中的具体位置

  • 引用关系追踪与可视化: 辅助研究人员快速查找某篇文献的参考文献或被引用的情况,梳理研究脉络

  • 语义检索:输入“钙钛矿太阳能电池稳定性”,返回高相关论文(按被引/方法创新排序)

  • 知识图谱关联:可视化研究脉络(如“2015-2024年效率提升技术路径”)

  • 综述辅助写作:自动生成研究空白总结(如“目前缺乏低温制备方案”)

应用场景 12:行业研究财报精准问答与数据提取

痛点

  • 从大量PDF格式的财报(年报、季报)中手动查找、提取关键财务数据和经营信息,耗时且易出错

  • 现有通用AI问答模型在处理专业财报时,容易出现数据提取错误、信息遗漏甚至“一本正经地胡说八道”(如“某公司2023年毛利率70%”)

  • 研究效率低,难以快速响应市场变化或完成深度分析报告

  • 竞品对比需跨文件复制粘贴,效率低下

解决方案

  • 金融文档深度解析与结构化: 采用专门针对财报(特别是表格、附注)优化的OCR和NLP模型,精准提取财务数据、关键指标、管理层讨论与分析(MD&A)中的要点

  • 基于RAG的精准问答与溯源: 用户提问财报细节(如“公司去年的研发投入是多少?”“毛利率变化的原因是什么?”),系统基于解析后的结构化数据和原文进行回答,并明确标注答案来源(页码、段落),极大减少幻觉

  • 跨财报数据对比与分析: 支持提取同一公司不同时期或不同公司同一时期的关键指标,进行自动对比和趋势分析

  • 关键信息摘要与生成: 自动生成财报关键内容的摘要、风险点提示、或特定主题(如ESG表现)的信息汇总

  • 财报结构化解析:自动提取三张表数据,生成可比指标(如“ROE行业排名”)

  • 事实核查引擎:提问“某公司存货周转天数”,仅返回财报原文数据

  • 行业看板:可视化头部企业营收增长率、研发投入趋势


应用场景 13:专利智能查新与技术特征提取

痛点

  • 人工阅读和理解专利权利要求书,准确、快速地拆解和定位其核心技术特征,难度大且耗时

  • 在海量专利数据库中进行查新比对时,仅靠关键词检索容易遗漏相关度高的对比文件,或被大量不相关文件干扰

  • 专利撰写和审查过程中,对技术特征的比对分析效率低下

  • 撰写时重复描述现有技术,创新点不突出

  • 审查意见答复缺乏数据支撑

解决方案

  • 权利要求智能解析与特征提取: 利用大模型对专利权利要求书进行语义分析,自动识别、拆解并标注其中独立的技术特征及其相互关系,自动标注“权利要求1:石墨烯掺杂方法”

  • 基于技术特征的语义检索: 支持输入待查新的技术方案描述或关键技术特征,系统在专利库中进行语义匹配,查找包含相似或相关技术特征的现有专利/文献

  • 对比文件智能筛选与排序: 根据技术特征的重合度和相似度,对检索到的对比文件进行智能排序,优先展示相关性最高的专利

  • 技术特征可视化比对: 提供工具,将待申请专利的技术特征与对比文件的技术特征进行可视化并列展示,方便审查员或代理人进行新颖性/创造性判断

  • 新颖性评估:输入技术方案,比对已有专利,提示相似度(如“与CN2018XXX重叠率30%”)

  • 案例推荐:参考同类专利答复模板(如“创造性抗辩需实验数据对比”)

应用场景 14:法务文档智能审阅与条款定位

痛点

  • 需要在海量合同、协议等法律文档(常为PDF格式,甚至影印件)中查找特定条款(如管辖权、保密、违约责任)。

  • 影印件质量差、文字模糊,导致OCR识别困难,传统文本搜索失效或不准确。

  • 人工逐页查找效率极低,且容易遗漏关键条款,存在法律风险。

  • 合同关键条款(如“违约责任”)埋没在数百页PDF中

  • 影印件文字模糊,OCR识别错误率高

  • 历史版本比对依赖人工,易遗漏修订点

解决方案

  • 增强型OCR与文档理解: 采用先进的AI OCR技术,提升对低质量扫描件、复杂排版PDF的文字识别准确率,并理解文档结构(如条款编号、标题)

  • 基于语义的智能条款检索(RAG): 用户可通过自然语言描述需要查找的条款内容(如“查找关于争议解决方式的约定”),系统能理解意图,在文档库中精准定位相关条款,即使措辞不完全一致

  • 关键条款自动识别与分类: 训练模型自动识别并标记文档中常见的标准条款类型(如付款条件、知识产权归属、不可抗力等),方便快速浏览和定位

  • 高亮显示与上下文呈现: 找到目标条款后,在原文中高亮显示,并提供上下文预览,帮助法务人员快速判断条款内容和适用性

  • 条款智能定位:输入“争议解决”,返回所有合同中的管辖法院条款

  • 版本差异比对:标红2023版vs 2024版合同修改处(如“赔偿上限从50万→100万”)

应用场景 15:制造业智能故障诊断与维护

痛点

  • 设备故障代码手册庞杂(如2000+页PDF),现场工程师需人工翻阅,平均故障定位时间>45分钟

  • 传统知识库仅支持关键词检索,无法理解"数控机床主轴温升报警"等口语化问题描述

  • 维修方案依赖老师傅经验,新人难以掌握"液压系统压力波动±0.5MPa是否正常"等隐性知识

解决方案

  • 故障知识图谱构建

    • 解析设备手册/工单记录,结构化故障代码(如E307→"伺服电机过载")、可能原因(电源不稳/轴承磨损)、标准操作流程(扭矩校准步骤)

    • 关联历史维修视频片段(如"主轴拆卸"操作关键帧打标为<15秒可跳转段落)

  • 多模态交互系统

    • 语音/图片输入故障现象(拍摄异响部位),自动匹配相似案例(如"齿轮箱油渍+高频噪音→密封圈失效概率82%")

    • AR辅助指引:扫描设备二维码叠加三维爆炸图,高亮需检查的联轴器螺栓(依ISO-10816标准标红振动超标区域)

  • 预防性维护推荐

    • 根据设备运行日志(如连续200小时满负荷),自动推送润滑周期提醒及对应油品库存位置

应用场景 16:医疗AI辅助诊疗决策

痛点

  • 医生需同时参考《临床路径指南》、最新期刊论文、医院内部诊疗规范,信息过载导致决策延迟

  • 罕见病治疗方案分散在UpToDate/PubMed等异构系统,手工交叉验证耗时>2小时

  • 患者病史数据(检验报告/影像)与非结构化主诉(“饭后右上腹钝痛”)难以关联分析

解决方案

  • 多源知识融合引擎

    • 结构化诊疗指南(将NCCN癌症分期标准转化为可计算规则树)

    • 实时抓取FDA新药公告,自动标注"奥希替尼新增脑转移适应症"等变更点

  • 患者数据智能映射

    • 输入"ALT 120U/L+胆红素升高",自动生成鉴别诊断列表(病毒性肝炎/胆管结石概率分布)

    • 检查结果冲突检测(当D-二聚体阴性但CT显示肺栓塞时触发警示)

  • 循证治疗推荐

    • 基于患者基因组数据(如EGFR L858R突变),优先显示本地化疗效数据(“本院类似病例使用阿法替尼ORR达72%”)

应用场景 17:教育智能备课助手

痛点

  • 教师需从1000+份PDF教案/MOOC视频中手工筛选适合当前学情(如"高一物理力学薄弱班")的材料

  • 跨学科知识点关联依赖人工(如讲解"抛物线运动"需关联数学二次函数),备课效率低下

  • 无法实时获取学生错题本数据(如最近20次作业在"楞次定律"错误率>60%)

解决方案

  • 教学资源动态标签库

    • 深度解析教材/习题(将"平抛运动"拆解为水平匀速+竖直自由落体两个子知识点)

    • 智能标注资源难度(如"弹簧振子实验视频"标记为★★☆需三角函数基础)

  • 跨学科知识图谱

    • 输入"讲解电磁感应",自动推荐数学导数应用案例(磁通量变化率Φ/t可视化)

    • 关联虚拟实验(可交互的法拉第线圈模拟器链接)

  • 学情自适应推荐

    • 根据班级作业数据,优先推送"左手定则"三维动画演示(针对错误率>40%的班级)

    • 生成分层练习题(A组基础计算/B组实际发电机设计问题)

应用场景 18:零售智能导购知识中枢

痛点

  • 商品知识分散在ERP系统/培训PPT/供应商邮件(如"某粉底液含积雪草成分"信息埋没在第30页PDF)

  • 顾客咨询"登山鞋防水性能"时,店员需人工对比GTX薄膜/EVENT薄膜技术参数

  • 促销规则复杂(“满300减50叠加会员8折”),人工计算易出错导致客诉

解决方案

  • 商品属性深度挖掘

    • 解析技术文档(将"5000mm防水"转换为"可抵御中雨2小时"等消费者语言)

    • 构建成分冲突库(含维C的护肤品不可与含铜肽产品同用)

  • 场景化推荐矩阵

    • 输入"海岛度假装备",自动组合防晒霜(SPF50+)、速干毛巾、防水手机套,并计算最优优惠组合

    • 实时竞品对比(展示本方冲锋衣接缝压胶宽度比竞品多1.2mm)

  • AR实景导购

    • 扫描顾客肤质,叠加适合的粉底色号推荐(基于Pantone肤色库匹配)

    • 虚拟试衣间自动标注"此款牛仔裤裆部设计适合梨形身材"

应用场景 19:智能物流调度知识中枢

痛点

  • 物流调度规划复杂,订单激增时容易出现配送延误和资源浪费

  • 司机和调度员缺乏快速处理异常事件的指导,如车辆故障或交通管制

  • 历史调度经验难以积累,人员流动导致知识流失

解决方案

  • 实时数据融合与调度优化:整合订单、库存、车辆状态和路况信息,结合大模型生成最优配送路径和调度计划

  • 异常处理知识库:构建常见物流异常(如车辆故障、堵车、货损)的案例知识库,提供标准化操作指引

  • 调度智能问答助手:输入"订单积压处理"或"多点配送优化",系统自动生成解决方案和可操作步骤

  • 经验萃取与案例沉淀:对历史调度数据进行语义分析,提取关键经验和策略,形成可复用知识条目

  • 智能监控与预警:在监控界面实时展示调度状态,当检测到异常时推送应急预案,减少延误和资源浪费

  • 操作培训助手:针对新员工和临时调度员,提供交互式培训和知识查询功能,帮助快速上手

应用场景 20:电力信息化新能源场站智能管理

痛点

  • 光伏/风电设备型号多样,运维标准不统一(如逆变器散热要求差异)

  • 功率预测误差导致弃风弃光,经济损失严重

  • 场站分散,专家远程支持响应慢

解决方案

  • 设备标准化知识库

    • 解析不同厂商技术文档,统一关键参数(如“组串式逆变器MPPT电压范围”)
  • 功率预测优化

    • 结合历史出力数据、气象预报,输出置信区间(如“明日午间光伏出力预测±5%”)
  • 远程AR协作

    • 场站人员通过AR眼镜直播故障设备,专家实时标注检测点(如“检查汇流箱熔丝”)

应用场景 21:电力营销智能客服

痛点

  • 电价政策复杂(如分时电价、绿电交易),人工解答易出错

  • 客户报装资料审核耗时(如光伏并网申请需核对10+项文件)

  • 投诉工单分类依赖经验,处理效率低

解决方案

  • 政策智能问答引擎

    • 输入“工商业峰谷电价”,自动返回最新政策+电费计算示例
  • 资料自动核验

    • OCR识别客户上传文件(如房产证、身份证),匹配缺失项(如“缺少屋顶荷载证明”)
  • 投诉工单分类

    • NLP分析客户描述,自动归类(如“电压不稳→配电运维类工单”)

三、RAG原理

如何从海量非结构化文档中高效提取关键信息,成为了DeepSeek等大模型在实际应用中面临的重要挑战。

RAG(Retrieval-Augmented Generation,检索增强生成)的工作原理类似于学生的“开卷考试” :在回答问题时,模型可以“查阅”外部知识库中的相关资料,而不是仅依赖内部记忆的参数知识。

这种方式将模型的推理能力事实知识分离,使模型能够生成更准确、更有依据的文本内容。

1、RAG解决的核心问题

传统大型语言模型存在三个主要局限:

  • 幻觉问题:模型基于统计概率生成文本,可能产生看似合理但缺乏事实依据的内容

  • 时效性问题:模型训练完成后,知识就固定了,无法获取最新信息

  • 数据安全问题:企业敏感数据不能直接用于训练公开的大模型

在行业细分领域,AI大模型LLM无法学习到所有的专业知识细节,因此在面向专业领域知识的提问时,无法给出可靠准确的回答,甚至会“胡言乱语”,这种现象称之为LLM的“幻觉”。

RAG通过引入外部知识检索机制,有效缓解了这些问题。当模型需要回答问题时,它会先从外部知识库中检索相关信息,然后基于这些信息生成回答,而不是仅依赖训练时学到的参数化知识。

尤其是在面对海量非结构化数据时,企业难以快速提取有价值的信息,导致决策效率低下。

为此,实现AI大模型商用级知识库主要有两种方法:

  • 第一种:通过专业知识的再训练或模型微调来增强模型能力,但这种方法需要大量标注数据和计算资源,成本高昂,对个人用户来说不太可行;

  • 第二种:在向大模型提问时提供相关背景知识,使模型能够基于这些上下文信息生成更准确的回答。这种知识库构建方法的核心就是RAG(Retrieval-Augmented Generation,检索增强生成)技术。

这种方法允许大型语言模型在不重新训练的情况下访问特定领域或组织的内部知识库,从而保持其输出的相关性、准确性和实用性。

2、RAG的基本工作流程

RAG系统通常包含三个核心阶段:

  • 检索(Retrieve):根据用户查询从外部知识源检索相关上下文

  • 增强(Augment):将检索到的上下文与用户查询结合,构建增强提示

  • 生成(Generate):将增强后的提示输入语言模型,生成最终回答

RAG 系统依赖于多个相互依赖的组件:检索引擎、重新排序模型和生成模型,每个组件都旨在执行特定功能。

RAG 流程图

RAG将信息检索与生成模型相结合,其核心流程是:

在生成回答前,先从外部知识库中检索相关信息,让模型能够引用训练数据之外的专业知识,使其在生成响应之前能够引用训练数据来源之外的权威知识库,再将检索结果与用户输入结合,指导生成模型输出更可靠的回答。

检索增强生成(RAG)把信息检索技术和大模型结合起来,将检索出来的文档和提示词一起提供给大模型服务,从而生成更可靠的答案,有效地缓解大模型推理的“幻觉”问题。

这是一个典型 RAG 架构图:

RAG 架构图

  1. 创建数据块
  • 第一步是将这些额外知识分解成块,然后再将其嵌入并存储在向量数据库中。
  1. 生成嵌入
  • 在分块后,我们使用嵌入模型对这些块进行嵌入。
  1. 将嵌入存储在向量数据库中
  • 这表明向量数据库充当了您 RAG 应用程序的内存,因为这正是我们存储所有额外知识的地方,用户的查询将使用这些知识进行回答。
  1. 用户输入查询
  • 接下来,用户输入查询,一个表示他们所寻求信息的字符串。
  1. 嵌入查询
  • 此查询使用我们在第 2 步中用于嵌入数据块的相同嵌入模型转换为向量。
  1. 检索相似块
  • 向量化查询随后与数据库中现有的向量进行比较,以找到最相似的信息。
  1. 重新排序数据块
  • 在检索后,选定的片段可能需要进一步的细化,以确保最相关的信息被优先考虑。

  • 在这个重新排序步骤中,一个更复杂的模型(通常是一个交叉编码器)评估初始检索到的块列表和查询,以为每个块分配一个相关性评分。

  1. 生成最终响应
  • 一旦最相关的片段被重新排序,它们就会被输入到大模型 DeepSeek 中。

  • 该模型将用户的原始查询与检索到的片段结合在一个提示模板中,以生成一个综合所选文档信息的响应。

3、技术实现细节

(1)数据预处理与索引构建

在RAG系统运行前,需要先构建外部知识库,这一过程包括:

  • 文档加载:从各种格式的文件(PDF、TXT、HTML、DOC等)中提取文本内容

  • 文本分块:将长文档分割成较小的"块"(chunk),以适应模型的上下文窗口限制

  • 向量化:使用嵌入模型(如OpenAI Embeddings)将文本块转换为向量表示

  • 索引存储:将向量化后的文本存入向量数据库(如Weaviate、Milvus等)

文本分块是预处理的关键步骤,常见方法包括:

  • 基于字符的分割(CharacterTextSplitter)

  • 基于标记的分割(SentenceTransformersTokenTextSplitter)

  • 基于文档结构的切割(MarkdownTextSplitter等)

(2)检索阶段

当用户提交查询时,RAG系统会:

  • 使用相同的嵌入模型将查询转换为向量

  • 在向量数据库中进行相似性搜索,找出与查询向量最接近的k个文本块

  • 返回这些相关文本块作为上下文

检索方法不仅限于向量检索,还可以结合关键字检索、图数据检索等多种方式实现混合检索7。

(3)生成阶段

获得相关上下文后,RAG系统会:

  • 将检索到的上下文与用户查询一起填入预设的提示模板

  • 将完整的提示输入DeepSeek

  • 模型基于提供的上下文生成最终回答

用 OpenAI 联创 Andrej Karpathy 的一张图做个类比,他把 大模型LLM 比喻为一台计算机的 CPU, 把上下文类比为计算机的内存,那么以RAG为代表的向量数据库,就可以看作是这台计算机的硬盘。

RAG 与 LLM 的关系

四、DeepSeek+RagFlow黄金组合的特色

RAG 从提出到为业界广泛接纳,经历了一年多时间,当下的 RAG 产品已经并不稀缺,然而在实际应用中,却普遍得出了“ RAG 属于上手容易,但真正落地却很难”的结论。究其原因,这里边主要包含两个方面:

其一,是来自 LLM 自身。由于 RAG 的工作流程是针对从数据库返回的结果进行回答,这样的话,对于 RAG 来说,LLM 最基础也是最重要的能力其实包含:

  • 摘要生成能力;
  • 可控性:既 LLM 是否听话,是否会不按照提示要求的内容
  • 自由发挥产生幻觉;
  • 翻译能力,这对于跨语言 RAG 是必备的。

遗憾的是,在过去,国内可以用到的大模型LLM中,在这 3 点上表现良好的并不多。至于所谓高级的能力,例如逻辑推理,以及各类 Agent 要求的自主决策能力等,这些都是建构在以上基础能力之上,基础不好,这些也都是空中楼阁。

其二,则是来自于 RAG 系统本身。我们所说的 RAG,实际上包含完整的链路,包括数据的准备,数据写入,乃至从数据库查询和返回结果排序。在整条链路中,最大的难点来自于两方面:

一是如何应对复杂多变的数据,这些数据包含各种格式,更复杂的还包含各类图表等,如果在没有理解这些语义的基础之上直接提供 RAG 方案,就会导致语义丢失从而让 RAG 失败。

二是如何查询和排序:简单地讲,在大多数情况下,都必须引入多路召回和重排序,才能保证数据查询的准确度。

RAG 的完整链路

DeepSeek + RAGFlow 的出现,让之前的这些痛点迎刃而解。它不仅能从杂乱无章的文档中提取精华,还能以智能、有据的方式回答你的问题,彻底改变信息处理的体验。

作为商用级的知识库,不仅仅需要通过向量数据库和其它基础组件满足用户问题分类、敏感词检索等各类复杂场景需求,还能够内置强大的工作流引擎和函数库,支持业务流程编排,甚至是通过低代码实现可视化自定义工作流,从而指导大模型的工作过程,满足复杂业务场景下的需求,而这些则交由RagFlow智能体解决。

如果把AI大模型DeepSeek比作学生的大脑,把企业或个人知识库比作教材教辅,那么,就可以把RagFlow比作眼、耳、鼻、舌、身,协助DeepSeek完成“应试教育”之外的“素质教育”。为了过五关斩六将,应对各种考试,学霸则需要 RAGFlow 这样的工程化Agent引擎,统筹以上各项能力的发挥。

实际上,DeepSeek + RAGFlow 的组合拳提供了Models、Prompts、Indexes、Memory、Chains、Agents六大核心抽象,在降低系统实现复杂度的同时,提升系统整体的扩展性。它的能力边界只取决于DeepSeek的智力水平和RagFlow能提供的工具集的丰富程度。

由顶尖团队打造的DeepSeek + RAGFlow的黄金组合,正是终结文档处理黑暗时代的曙光。

RAGFlow由2个开源项目组成:

1、AI 原生数据库 Infinity

它解决的是如何解锁 RAG 服务 B 端场景下遇到的典型问题:如何跟企业已有的数据——包括但不限于非结构化的文档、图片,还包括结构化的信息系统来结合,并解决多路召回和最终融合排序的问题。

举几个典型场景:

把符合要求的简历筛出,筛选条件包含工作技能(需要向量 + 全文搜索),某类行业的工作经验(基于向量的分组聚合),期望收入,学历,地域(结构化数据)等;

基于对话推荐符合个人要求的产品,可以采用多列向量来描述个人偏好,不同的列代表了用户对不同类目产品的过往使用偏好。

在推荐过程中,除了采用基于用户的偏好向量进行搜索之外,还需要结合产品的过滤条件:包括是否过期,是否有优惠券,是否符合权限要求,是否有合规要求,该用户是否近期已经购买或者阅读过,等等。

这些信息,如果仅仅拿所谓“标量”字段这种方式来表征,那么产品的开发是极其复杂的:因为这需要引入额外的 ETL ,带来了维护性,以及更严重的数据一致性的问题。

要知道,RAG 面临的是最终用户使用场景,它是需要业务乃至 LLM 发起请求,就立刻得到答案的,因此不能像数据中台一样仅仅为了一张报表就可以搭建一整套数据管道体系去做宽表这种额外逻辑。因此,Infinity 实际上等于向量数据库 + 搜索引擎 + 普通结构化数据查询,并保证三者的高并发和融合排序。

RAGFlow 接入多种数据源

2、端到端的 RAG 引擎 RAGFlow

它解决数据的问题:因为如果不对用户数据加以区分和清晰,识别其中的语义,就容易导致 Garbage In Garbage Out。RAGFlow 包含了如下的完整 RAG 流程,确保数据从 Garbage In Garbage Out 变为 Quality In Quality Out。

RAGFlow 总体流程

具体来说, RAGFlow 的最大特色,就是多样化的文档智能处理,因此它没有采用现成的 RAG 中间件,而是完全重新研发了一套智能文档理解系统,并以此为依托构建 RAG 任务编排体系。这个系统的特点包含:

  1. 它是一套基于 AI 模型的智能文档处理系统:对于用户上传的文档,它需要自动识别文档的布局,包括标题、段落、换行等,还包含难度很大的图片和表格。对于表格来说,不仅仅要识别出文档中存在表格,还会针对表格的布局做进一步识别,包括内部每一个单元格,多行文字是否需要合并成一个单元格等。并且表格的内容还会结合表头信息处理,确保以合适的形式送到数据库,从而完成 RAG 针对这些细节数字的“大海捞针”。

  2. 它是一套包含各种不同模板的智能文档处理系统:不同行业不同岗位所用到的文档不同,行文格式不同,对文档查阅的需求也不同。比如:

  • 会计一般最常接触到的凭证、发票、Excel 报表;查询的一般都是数字,如:看一下上月十五号发生哪些凭证,总额多少?上季度资产负债表里面净资产总额多少?合同台账中下个月有哪些应付应收?

  • 作为一个 HR 平时接触最庞杂的便是候选人简历,且查询最多的是列表查询,如:人才库中 985/211 的 3 到 5 年的算法工程师有哪些?985 硕士以上学历的人员有哪些?赵玉田的微信号多少?香秀哪个学校的来着?

  • 作为科研工作者接触到最多的可能是就是论文了,快速阅读和理解论文,梳理论文和引文之间的关系成了他们的痛点。

这样看来,凭证、报表、简历、论文的文档结构是不一样的,查询需求也是不一样的,那处理方式肯定是不一样。因此 RAGFlow 在处理文档时,给了不少的选择:Q&A,Resume,Paper,Manual,Table,Book,Law,通用… 。当然,这些分类还在不断继续扩展中,处理过程还有待完善。RAGFlow 也会抽象出更多共通的东西,使各种定制化的处理更加容易。

多种文件格式解析器

  1. 智能文档处理的可视化和可解释性:用户上传的文档到底被处理成啥样了,如:分割了多少片,各种图表处理成啥样了,毕竟任何基于 AI 的系统只能保证大概率正确,作为系统有必要给出这样的空间让用户进行适当的干预,作为用户也有把控的需求,黑箱不敌白箱。特别是对于 PDF,行文多种多样,变化多端,而且广泛流行于各行各业,对于它的把控尤为重要,RAGFlow 不仅给出了处理结果,而且可以让用户查看文档解析结果并一次点击定位到原文,对比和原文的差异,可增可减可改可查,如下图所示:

智能文档处理的可视化和可解释性

智能文档处理的可视化和可解释性

  1. RAGFlow 是一个完整的 RAG 系统,而目前开源的 RAG,大都忽视了 RAG 本身的最大优势之一:可以让 LLM 以可控的方式回答问题,或者换种说法:有理有据、消除幻觉。我们都知道,随着模型能力的不同,LLM 多少都会有概率会出现幻觉,在这种情况下, 一款 RAG 产品应该随时随地给用户以参考,让用户随时查看 LLM 是基于哪些原文来生成答案的,这需要同时生成原文的引用链接,并允许用户的鼠标 hover 上去即可调出原文的内容,甚至包含图表。如果还不能确定,再点一下便能定位到原文,如下图所示:

鼠标悬停显示原文内容

五、RAGFlow 文档结构识别模型

RAGFlow 具体是如何利用文档结构识别模型来处理数据的?

所谓文档结构模型,如下所示,是针对文档的布局进行目标识别,然后根据布局再做文字切分。这些布局识别的目标包括文档的标题,段落,语义文字块等等,尤其还会包含文档当中的图表。

RAGFlow 文档结构识别模型

在识别出这些目标之后,还需要分别对这些目标做相应处理:对于文字来说,需要首先判断文字的换行信息——这对于文字的语义理解也会产生干扰;其次需要对文字内容进行一些整理,这些整理会随着 RAGFlow 模板的不同有所区分;针对表格来说,还需要进一步识别它的内部结构,这在 AI 领域有个专门的研究课题,叫做 TSR(Table Structure Recognition 表格结构识别) 。

TSR 任务其实相对比较复杂,因为表格的定义是多种多样的,表格内部可能会出现有线条或者没有线条的情况,对于不同行的文字,判断它们是否是一个单元格是存在很大挑战的,单元格判断失误,很可能就会让表格的数字跟表格列的对应关系弄错,从而影响了对单元格内文字和数字语义的理解。

RAGFlow 花了很多时间来提升 TSR 的能力,最早是利用现成的 OCR 开源模型,后边也尝试过微软研究院专门针对 TSR 任务的 Transformer 模型,但是发觉这些模型处理 TSR 任务的鲁棒性依然非常不足,最后 RAGFlow 还是训练了自己的模型,从而让 TSR 任务表现良好。这个模型比较简单,就是基于 CNN 的目标检测模型,但是它的效果却比上边我们提到的其他模型都要好。为了降低对硬件的依赖和开销,RAGFlow 甚至切换到用 YOLOv8 来做目标检测,使得仅仅利用 CPU 也可以运行文档结构识别。

解锁对于非结构化数据的深度语义理解是 RAGFlow 追求的目标之一,未来RAGFlow 能够将更加 scalable 的文档结构识别模型应用到系统中。不仅如此, RAGFlow 的设计目标是让 RAG 逐渐承接起更多的复杂场景尤其是 B 端场景,因此在未来,它会接入企业的各类数据源,比如 MySQL 的 binlog,数据湖的 ETL,乃至外部的爬虫等。只有这些都被纳入 RAG 的范畴,RAGFlow 才能实现如下的愿景:

RAGFlow 愿景

RagFlow与DeepSeek两者之间是合作的关系。DeepSeek当下已经逐步具备 RAGFlow 最不可或缺的基础能力,随着它自身逻辑推理能力地增强,再结合来自数据库,还有数据方面的改进,一定能加速DeepSeek在 B 端场景的落地。

RAGFlow提供了类似文件管理的功能,这样RAGFlow可以跟企业内部文档以更灵活的方式整合。RAGFlow还将提供面向企业级数据接入的低代码平台,同时提供问答对话之外的高级内容生成,比如长文生成等等。

与RAGFlow搭配使用的AI原生数据库Infinity提供了业界最快的多路召回和融合排序能力。

🔍 多模态混合搜索能力

  • 全文搜索 + 向量搜索 + 稀疏向量搜索:​支持多种搜索方式的组合,提升查询的准确性和召回率。

  • Tensor 数据类型:​引入张量(Tensor)支持,结合 ColBERT 等排序模型,实现更高质量的语义检索。

  • 多路召回与融合排序:​支持多种召回方式的融合排序,如 RRF(递归融合排序)和 ColBERT 重排序,提高检索结果的相关性和多样性。

⚙️ 高性能执行与存储架构

  • 流水线执行引擎:​采用流水线执行计划,优化查询的并行和并发执行,降低延迟。

  • 列式存储与多种索引支持:​基于列式存储,支持多种索引结构(如 HNSW、倒排索引等),提高查询效率。

🤖 AI 模型集成与 RAG 支持

  • 与主流模型框架集成:​支持与 TensorFlow、PyTorch 等主流机器学习框架的集成,方便模型的训练与推理。

  • RAGFlow 集成:​与 RAGFlow 方案结合,提供端到端的 RAG 解决方案,支持复杂查询和多路召回。

  • 支持 ColBERT 等排序模型:​内置支持 ColBERT 等先进的排序模型,提升检索质量。

🧩 企业级特性与易用性

  • 高可用性与可扩展性:​采用分布式架构,支持水平扩展,满足大规模数据处理需求。​

  • 简洁的 API 与 SDK:​提供易于使用的 API 和 SDK,方便开发者快速集成。

  • 多种数据类型支持:​支持字符串、数字、向量、张量等多种数据类型,适应不同业务需求。

Infinity 的这些功能使其在智能客服、推荐系统、金融风控、知识图谱等 AI 应用场景中具有广泛的适用性。​其多模态混合搜索能力和高性能执行架构,特别适合需要高效、准确检索的复杂应用场景。

六、传统知识库与AI知识库的比较

对比角度传统知识库智能知识库
知识获取手动编码,专家驱动自动化,数据驱动
语义理解基于关键词匹配基于语义理解和上下文
推理主要为演绎推理,能力有限高级推理,可处理不确定性
更新手动维护,耗时动态更新,持续学习
可扩展性扩展性有限具有良好的可扩展性
维护维护成本高,易过时自动化维护,保持最新
关键技术规则、框架、本体自然语言处理、机器学习、知识图谱

1、知识库技术原理与架构

传统知识库主要依赖于结构化数据库和搜索算法,通过关键字匹配或语义分析来提供信息检索服务。其技术架构相对固定,通常包括数据存储、检索引擎和用户界面等几个主要部分。
而AI知识库则基于深度学习技术,特别是大规模预训练语言模型(如GPT系列)。它通过海量文本数据的学习,形成了对自然语言的理解和生成能力。在知识库中,AI大模型作为核心引擎,不仅能够理解用户的查询意图,还能生成自然语言回答,为用户提供更加智能、自然的知识服务。

2、知识存储与表示

传统知识库通常以结构化数据的形式存储知识,如实体、属性、关系等。这种方式对于特定领域的知识表示和查询非常有效,但缺乏灵活性,难以应对复杂多变的自然语言查询。
AI知识库则采用非结构化文本数据的形式,通过自然语言处理技术将知识转化为模型可以理解的格式。这种表示方式使得知识库能够容纳更广泛、更丰富的信息,包括文本、图像、音频等多种形式。同时,AI大模型通过深度学习技术自动提取文本中的语义信息,实现了对知识的深层次理解和表示。

3、查询与检索

传统知识库通常依赖于精确匹配或基于规则的语义分析来检索信息。用户需要准确地表达自己的查询意图,并使用特定的查询语句或关键词来获取信息。这种方式虽然在一定程度上能够满足用户的需求,但往往受限于查询语句的准确性和复杂性。
AI知识库则通过自然语言处理技术,实现了对用户查询意图的自动识别和解析。用户可以以自然语言的形式输入查询问题,无需考虑特定的查询语法或关键词。AI大模型能够理解用户的意图,并从知识库中检索相关信息,生成自然语言回答。这种查询方式更加自然、便捷,大大提高了用户的查询体验。

4、智能化程度与应用场景

传统知识库主要提供信息检索功能,智能化程度相对较低。它们更多地依赖于用户的查询能力和对知识库的熟悉程度来获取所需信息。
而AI知识库则具有更高的智能化程度。它不仅能够理解用户的查询意图,还能根据用户的历史行为和偏好进行个性化推荐。此外,AI知识库还具备知识推理、问答生成等高级功能,能够为用户提供更加智能、个性化的知识服务。这使得AI大模型知识库在教育、医疗、金融、客服等多个领域具有广泛的应用前景。

5、知识更新与维护

传统知识库的知识更新和维护通常依赖于人工操作,需要专业人员定期更新数据、优化检索算法等。这种方式不仅成本较高,而且难以应对快速变化的知识环境。
而AI知识库则具有较强的自我学习和更新能力。它可以通过持续学习新的文本数据来不断优化自身的模型参数,从而提高对知识的理解和生成能力。这种自动化的知识更新方式不仅降低了维护成本,还使得知识库能够保持与时俱进的状态。

6、用户交互与体验

传统知识库的用户交互方式相对单一,通常是通过搜索界面输入关键词或查询语句来获取结果。这种方式虽然能够满足基本的查询需求,但缺乏与用户之间的互动和反馈。
AI知识库则通过自然语言处理和对话生成技术,实现了与用户之间的自然语言交互。用户可以通过自然语言的形式提出问题或需求,AI知识库能够理解并生成自然语言回答,为用户提供更加自然、流畅的交互体验。此外,AI知识库还能够根据用户的历史行为和偏好进行个性化推荐,提高用户满意度和忠诚度。

七、AI知识库私有化部署的必要性

AI知识库私有化部署适合对数据隐私和安全性要求高的场景。对于如医疗机构处理病历档案、金融企业分析内部报告、法律部门管理合同文书,以及希望确保数据不出企业的需求,则可以在完全离线环境下独立部署和使用。

🔒 私有数据安全:全程本地处理,敏感文档无需上传第三方服务
⚡ 实时响应:基于本地向量数据库实现毫秒级语义检索
💡 领域适配:可针对专业领域文档定制知识库
🌐 离线可用:无需互联网连接,保护数据隐私
💰 成本可控:避免云服务按次计费,长期使用成本更低

AI知识库私有化部署的必要性

AI知识库已在教育、法律、医疗、金融等多个领域获得积极反馈。其核心优势体现在以下几个方面:

1. 数据安全与隐私保护

  • 本地化存储与计算:所有数据(文档、向量库、模型参数)存储在客户自有服务器或私有云,避免云端传输风险。

  • 端到端加密:支持数据存储、传输、检索全流程加密(如AES-256),确保敏感信息(如病历、合同)不被泄露。

  • 访问权限控制:基于RBAC(角色权限管理),限制不同用户的知识库访问范围(如法务仅查看合同库,HR仅查看员工手册)。

2. 高性能与实时响应

  • 本地向量数据库:部署高效的向量检索引擎(如FAISS、Milvus),实现毫秒级语义匹配,支持千万级文档快速查询。

  • 高并发处理:企业级硬件支持(如GPU集群),确保多用户同时访问时的稳定性(如金融机构百人并发分析财报)。

  • 低延迟推理:优化模型轻量化(如LoRA微调),在本地算力下实现秒级生成回答。

3. 领域专业化适配

  • 垂直领域微调:支持行业专用模型训练(如法律BERT、医疗GPT),提升专业术语理解(如“不可抗力条款”“EGFR基因突变”)。

  • 多格式文档解析:自动处理PDF、Excel、PPT、扫描件等,精准提取表格、公式、图纸等非文本信息。

  • 知识图谱构建:将非结构化数据(如设备手册、学术论文)转化为可推理的关系网络(故障代码→维修方案→备件库存)。

4. 离线可用与独立部署

  • 断网环境运行:完全脱离互联网依赖,适合涉密场景(如军工、政府内网)。

  • 一键部署方案:提供Docker镜像或离线安装包,支持企业现有IT架构(如VMware、Kubernetes)。

  • 定期本地更新:通过离线包更新模型/知识库,无需连接公网(如医院每季度更新药品库)。

5. 成本可控与长期运维

  • 买断式授权:避免云服务按次计费,适合高频使用场景(如制造业每日千次故障查询)。

  • 硬件兼容性:支持从边缘设备(如本地工作站)到数据中心部署,灵活匹配预算。

  • 低维护设计:提供自动化监控工具,预警存储/算力瓶颈,减少IT运维投入。

6. 精准性与可控性

  • 检索增强生成(RAG):严格限定答案来源,杜绝“AI幻觉”(如金融分析仅引用本地财报数据)。

  • 答案溯源:每一条回答标注出处(如“引自2023年《合同法》第52条”),支持人工复核。

  • 人工干预接口:允许管理员修正错误标签/检索结果,持续优化知识库质量。

7. 协作与扩展性

  • 多库独立管理:支持按部门/项目分库(如“研发专利库”“财务制度库”),避免信息混杂。

  • API集成能力:与企业现有系统(OA、ERP)对接,实现单点登录、数据同步。

  • 增量学习:新文档入库后自动更新索引,无需全量重建(如每日新增合同实时生效)。

8. 合规性与审计追踪

  • 符合行业特定数据处理规范
  • 操作日志与审计追踪
  • 合规性证明文档

典型应用场景验证

行业需求匹配案例
医疗隐私合规 + 术语理解三甲医院本地部署,解析电子病历生成诊疗建议,数据不出院区
金融风控分析 + 实时检索银行内网部署,秒级查询企业关联担保链,阻断高风险贷款
法律条款精准溯源律所私有化部署,合同审查直接引用《民法典》最新修订版
制造离线设备知识库工厂无网环境部署,AR眼镜扫描故障代码即刻返回维修指南

通过满足上述共性需求,私有化AI知识库可兼顾安全、效率与专业性,成为企业核心知识资产的“数字保险箱”。


八、整体架构

项目总体架构

1、硬件配置列表

大模型服务器

  • 显卡:一张RTX 4090显卡(24GB显存)
  • CPU:16 核,Xeon® Platinum 8352V
  • 内存:90 GB
  • 系统盘:30 GB
  • 数据盘:50 GB(用于存放模型文件)

RAGFlow应用服务器

  • CPU:16 核,Intel Xeon Processor (Skylake, IBRS)
  • 内存:32 GB
  • 数据盘:80 GB

2、RAGFlow 容器架构

根据docker-compose及docker-compose-base配置文件,RAGFlow运行需要以下几个容器:

services:
  ragflow:
    container_name: ragflow-server
  es01:
    container_name: ragflow-es-01
  infinity:
    container_name: ragflow-infinity
  mysql:
    container_name: ragflow-mysql
  minio:
    container_name: ragflow-minio
  redis:
    container_name: ragflow-redis
  1. ragflow-server

    • 核心应用容器,运行RAGFlow主服务
    • 提供Web界面和API服务
    • 暴露多个端口:HTTP(9380)、Web标准端口(80/443)和调试端口(5678/5679)
    • 依赖于MySQL数据库服务
    • 挂载配置文件、日志目录和Nginx配置
  2. ragflow-mysql

    • 基于MySQL 8.0.39版本
    • 存储系统配置、用户数据和知识库元数据
    • 配置了较高的最大连接数(1000)
    • 使用UTF-8 MB4字符集,支持完整Unicode字符
  3. ragflow-minio

    • 基于MinIO对象存储服务
    • 用于存储文档和向量数据
    • 提供S3兼容的API接口
    • 包含管理控制台(9001端口)
  4. ragflow-redis

    • 基于Valkey/Redis 8版本
    • 用作缓存和消息队列
    • 配置了128MB内存限制和LRU淘汰策略
    • 存储临时数据和会话信息
  5. ragflow-es-01 (可选,按需启用)

    • Elasticsearch搜索引擎
    • 用于全文检索和向量搜索
    • 单节点配置,具有安全认证
    • 配置了磁盘使用预警机制
  6. ragflow-infinity (可选,按需启用)

    • 基于InfiniFlow向量数据库
    • 提供高性能向量检索能力
    • 支持Thrift、HTTP和PostgreSQL兼容协议
    • 用于实现语义搜索功能

此外,配置中还包含一个注释掉的 ragflow-executor 服务,它是用于处理异步任务的执行器,可以按需启用以提高系统并发处理能力。

所有容器共享一个名为"ragflow"的Docker网络,确保容器间通信畅通,同时使用了持久化卷来保存数据,确保数据在容器重启后不会丢失。

3、RAGFlow 技术栈

RAGFlow采用现代化的微服务架构,通过Docker容器实现模块化部署,这使得系统既可以在单机环境下快速部署,也能在云环境中进行扩展。前端采用React生态系统构建丰富的用户界面,后端则以Python为主导语言,集成了多种AI和数据处理能力,实现了从文档摄取到知识提取再到智能问答的完整工作流。

前端技术 (Web前端)

  1. 核心框架

    • React.js 18 - 用于构建用户界面的JavaScript库
    • UmiJS 4 - 基于React的企业级前端应用框架
    • TypeScript - 提供类型安全的JavaScript超集
  2. UI组件库

    • Ant Design (antd 5.x) - 企业级UI设计语言和React组件库
    • Radix UI - 提供无样式但可访问的UI原语
    • Tailwind CSS - 实用优先的CSS框架
  3. 状态管理

    • Zustand - 轻量级状态管理库
    • Immer - 简化不可变数据结构更新
    • React Query (Tanstack Query) - 数据获取和缓存库
  4. 数据可视化

    • G2/G6 (AntV) - 图形和图表可视化库
    • Recharts - 基于React的响应式图表库
    • XYFlow - 用于构建流程图的库
  5. 国际化

    • i18next - 强大的国际化框架
    • React-i18next - React集成
  6. 开发工具

    • Jest - JavaScript测试框架
    • ESLint - 代码质量和风格检查
    • Prettier - 代码格式化工具
    • Husky - Git钩子工具

后端技术

  1. 核心框架

    • Python (3.10-3.12) - 主要编程语言
    • Flask 3.x - Web应用框架
    • Werkzeug - WSGI工具库
  2. 数据库和存储

    • MySQL 8.x - 关系型数据库
    • Redis/Valkey - 缓存和队列
    • Elasticsearch - 全文搜索引擎
    • MinIO - 对象存储服务
    • InfiniFlow - 向量数据库
  3. 机器学习和AI

    • HuggingFace Transformers - NLP模型库
    • PyTorch - 深度学习框架
    • ONNX Runtime - 模型推理优化
    • OpenAI、Anthropic等LLM API集成
  4. 嵌入模型技术

    • FastEmbed - 高性能嵌入生成
    • FlagEmbedding - 向量嵌入库
    • 支持多种第三方嵌入模型
  5. 文档处理

    • PyPDF、pdfplumber - PDF处理
    • python-docx - Word文档处理
    • python-pptx - PowerPoint处理
    • OpenCV - 图像处理
    • Tika - 文档提取工具
  6. DevOps & 部署

    • Docker - 容器化
    • Docker Compose - 多容器编排
    • Kubernetes/Helm - 容器集群管理
  7. 安全与认证

    • Flask-Login - 用户认证
    • 支持多种API密钥管理
  8. Web抓取与集成

    • Crawl4AI - 网页抓取
    • 支持多种搜索引擎和API集成(如Tavily)

数据处理流程

  1. 文档处理管道

    • DeepDoc - 深度文档理解模块
    • 布局分析和结构化提取
    • 自定义模板分块
  2. 检索增强生成(RAG)核心

    • 向量存储和检索
    • 多级回调和重排序
    • 大型语言模型(LLM)推理
  3. 知识图谱增强

    • 实体识别和关系提取
    • 图谱查询和推理
    • PageRank算法支持
  4. 推理系统

    • 深度研究(Deep Research)能力
    • 互联网搜索增强
    • 多步骤推理

4、RAGFlow自研智能体Agent框架

RAGFlow采用的是自主研发的图形化Agent智能体框架,而非使用现有的开源框架如LangChain或AutoGPT等。

  1. 基于图结构的工作流编排

    • 核心设计采用图(Graph)的数学概念,由节点(components)和边(upstream/downstream连接)组成
    • 支持复杂的工作流定义,支持包含环形逻辑的图结构(非仅限于DAG)
    • 通过JSON格式的DSL(领域特定语言)定义完整工作流
  2. 组件化架构

    • 所有功能通过组件(Component)实现,每个组件有明确定义的输入和输出
    • 基于ComponentBase基类实现的丰富组件库(30+预定义组件)
    • 组件包括:
      • 基础组件(Begin, Answer等)
      • 检索组件(Retrieval)
      • 生成组件(Generate)
      • 集成组件(搜索引擎如Google, Bing, Baidu)
      • 数据组件(Yahoo Finance, Akshare, Tushare等)
      • 工具组件(SQL执行, 爬虫, 翻译等)
  3. 状态管理与数据流

    • Canvas类作为核心执行引擎,负责组件间状态管理和工作流执行
    • 支持历史记录、消息、引用和路径跟踪
    • 通过变量传递实现组件间的数据流转
  4. 模板系统

    • 提供丰富的预定义模板,如医疗咨询、研究报告、SEO博客、SQL转换等
    • 模板以JSON格式定义,可以快速创建特定领域的Agent
  5. 流式响应

    • 支持流式输出,适合长文本生成场景
    • 通过生成器模式实现增量内容输出
  6. 参数验证与约束

    • 强大的参数验证系统,支持类型检查、值范围验证等
    • 组件参数通过ComponentParamBase派生类定义,确保配置正确性
  7. 扩展性设计

    • 易于添加新组件,只需继承基类并实现核心方法
    • 支持自定义工作流和业务逻辑
  8. 多租户支持

    • 支持多租户隔离,每个Canvas实例关联特定租户
    • 适合企业级多用户环境部署

与市面上常见的Agent框架相比,RAGFlow的框架更加注重以下方面:

  1. 图形化工作流 - 支持复杂的非线性工作流,包括循环和条件分支
  2. 业务组件丰富 - 预置了大量金融、研究、搜索等领域的专用组件
  3. 参数严格验证 - 强调配置正确性,减少运行时错误
  4. 自包含设计 - 不依赖外部Agent框架,完全自主实现核心逻辑

RAGFlow的Agent框架设计理念是为了满足企业级RAG应用的需求,特别是在复杂业务逻辑、多数据源集成和工作流管理方面提供了强大的能力。

九、DeepSeek部署过程

注:这部分内容在我的【上一篇】文章有涉及,看过的读者可以跳过这一节。

1、GPU显卡内存估算

如何准确计算大模型所需的显存大小,是许多开发者经常遇到的问题。掌握GPU内存的估算方法,并据此合理配置硬件资源以支持模型运行,是确保大模型成功部署和扩展的关键。这一能力也是衡量开发者对大模型生产环境部署和可扩展性理解程度的重要指标。

要估算服务大型语言模型所需的 GPU 内存,可以使用以下公式:

M = ( P ∗ 4 B ) ( 32 / Q ) ∗ 1.2 M=\frac{(P * 4 B)}{(32 / Q)} * 1.2 M=(32/Q)(P4B)1.2

  • M是所需的 GPU 显卡内存(单位:GB千兆字节)。
  • P是模型中的参数数量,表示模型的大小。例如,这里使用的 Llama 90B模型有 900 亿个参数,则该值将为 90。
  • 4B表示每个参数使用 4 个字节。每个参数通常需要 4 个字节的内存。这是因为浮点精度通常占用 4 个字节(32 位)。但是,如果使用半精度(16 位),则计算将相应调整。
  • Q是加载模型的位数(例如,16 位或 32 位)。根据以 16 位还是 32 位精度加载模型,此值将会发生变化。16 位精度在许多大模型部署中很常见,因为它可以减少内存使用量,同时保持足够的准确性。
  • 1.2 乘数增加了 20% 的开销,以解决推理期间使用的额外内存问题。这不仅仅是一个安全缓冲区;它对于覆盖模型执行期间激活和其他中间结果所需的内存至关重要。

估算GPU显存大小

举例:以满血版DeepSeek-R1(671B参数、加载 16 位精度)为例,计算其推理所需的显存:

M = ( 671 ∗ 4 ) ( 32 / 16 ) ∗ 1.2 = 1610.4 G B M=\frac{(671 * 4)}{(32 / 16)} * 1.2 = 1610.4 GB M=(32/16)(6714)1.2=1610.4GB

这个计算告诉我们,需要大约1610.4 GB 的 GPU 显存来为 16 位模式下具有 6710 亿个参数的满血版 DeepSeek-R1 大模型提供推理服务。

因此,单个具有 80 GB 显存的 NVIDIA A100 GPU 或者 H00 GPU 不足以满足此模型的需求,需要至少20张具有 80 GB 内存的 A100 GPU 才能有效处理内存负载。

此外,仅加载 CUDA 内核就会消耗 1-2GB 的内存。实际上,无法仅使用参数填满整个 GPU 显存作为估算依据。

如果是训练大模型,则需要更多的 GPU 显存,因为优化器状态、梯度和前向激活每个参数都需要额外的内存。

2、选择模型

目前DeepSeek-R1系列模型在Huggingface上共计开源了8种。

DeepSeek-R1系列模型

完整系列一览(按参数规模排序):

  • DeepSeek-R1 (671B)
  • DeepSeek-R1-Zero (671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • DeepSeek-R1-Distill-Qwen-14B
  • DeepSeek-R1-Distill-Llama-8B
  • DeepSeek-R1-Distill-Qwen-7B
  • DeepSeek-R1-Distill-Qwen-1.5B

DeepSeek-R1系列的两大明星产品:

DeepSeek-R1-Zero:AI界的"极限探索者"

  • 超强算力:6710亿参数(采用MoE架构,每个token可调动370亿参数)
  • 创新训练:采用纯强化学习的端到端训练方式
  • 突破性能:实现自我验证、长链推理等前沿能力
  • 实战表现:在AIME 2024基准测试中取得71%的亮眼成绩

DeepSeek-R1:AI界的"全能冠军"

  • 强大算力:同样拥有6710亿参数的超强实力
  • 独特训练:创新性采用多阶段混合训练方法
  • 双重加持:结合监督微调冷启动与强化学习优化
  • 卓越成就:在AIME 2024测试中达到79.8%的惊人准确率

值得一提的是,DeepSeek团队通过知识蒸馏技术,成功将这些顶级模型的能力传承给更轻量级的版本。

这种创新方式不仅大幅降低了模型应用门槛,还提升了小型模型的推理能力,这正是DeepSeek在AI领域备受瞩目的重要原因之一。

DeepSeek-R1 蒸馏模型的几款小尺寸模型,是使用 DeepSeek-R1 生成的包含<think>...</think>标记的思考链数据进行微调后的蒸馏版本,继承了 R1的推理能力。

毕竟博主囊中羞涩,为了完成这篇文章,选择 bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF 的DeepSeek-R1-Distill-Qwen-32B大模型的4bit量化模型,根据上面的估算公式,仅使用1张具有 24 GB 内存的 4090 GPU 就可以运行完成本文所需的推理任务。

Qwen2.5-32B是一个通用的预训练语言模型,而DeepSeek-R1-Distill-Qwen-32B是基于Qwen2.5-32B使用DeepSeek-R1生成的包含<think>...</think>标记的思考链数据进行微调后的蒸馏版本,因此继承了R1的推理能力。

这些微调数据明确包含问题拆解、中间推导等推理过程,通过强化学习让DeepSeek-R1-Distill-Qwen-32B对齐了R1生成推理步骤的行为模式。通过这种蒸馏机制,小型模型既能保持计算效率,又获得了接近大模型的复杂推理能力,这在资源受限场景下具有重要应用价值。

3、选择模型推理服务器和推理框架

在选择模型后,本地部署面临的第二个问题便是如何选择推理服务器和推理框架 🤔

对于LLM推理服务器的选择,目前主要有三种方法。

提供LLM推理服务的三种方法

第一种方法是将个人电脑或租用的服务器配置成一个LLM推理服务器,本文采取的便是这种搭建方式。

第二种方法是利用OpenAI、Anthropic等大型公司的LLM服务,你只需通过API密钥直接调用,而无需访问其官网。

第三种方法是在云平台上构建LLM服务器,并调用该服务器的API,常见的选择包括阿里云、腾讯云、百度云、AWS、Azure、谷歌云,甚至Modal和SkyPilot等服务。

在具体选择时,一定要综合考虑成本,谨慎挑选最适合的模型。

为编写本文,博主租了一台带有一张RTX 4090 GPU 显卡的服务器(花费大概10元左右就能完成本文案例的部署,当然还需要一些降低费用的小技巧,比如提前租用配置的服务器把模型文件下载到服务器,这样就可以节省很多费用💰,具体情况可以关注本公众号咨询博主)。

模型推理服务器的配置如下:

  • GPU:RTX 4090,24GB显存
  • CPU:16 核,Xeon® Platinum 8352V
  • 内存:90 GB
  • 系统盘:30 GB
  • 数据盘:50 GB(用于存放模型文件、分词器文件)

在选择模型推理服务器后,本地部署面临的第二个问题便是如何选择推理框架,今天我们就来盘一盘四大主流推理框架的优缺点:

🔥 四大天王对决:
1️⃣ SGLang - 大规模集群部署专家
2️⃣ Ollama - 轻量级玩家最爱
3️⃣ vLLM - GPU推理性能王者
4️⃣ LLaMA.cpp - CPU部署救星

💡 选择秘籍:
✅ 要极致性能 → 选vLLM
✅ 要简单易用 → 选Ollama
✅ 要集群部署 → 选SGLang
✅ 要CPU运行 → 选LLaMA.cpp

📊 性能对比:

  • 推理速度:vLLM > SGLang > Ollama > LLaMA.cpp
  • 易用程度:Ollama > LLaMA.cpp > vLLM > SGLang
  • 硬件要求:vLLM(需GPU) > SGLang > Ollama > LLaMA.cpp

💼 实战建议:

  • 单卡或双卡4090用户 → 闭眼入vLLM
  • 个人开发者 → Ollama快速上手
  • 企业级部署 → SGLang更专业

目前市面上关于如何用Ollama拉取Q4或Q8量化模型进行本地推理的教程已经层出不穷,Ollama 确实以简单易用俘获了一大批开发者,但如果你和我一样,追求的是生产环境的稳定高效,那么Ollama可能就不是你的菜了!

为什么我最终选择了 vLLM?

  • 🚀 性能才是硬道理:Ollama 在高并发和推理速度上,相比 vLLM 真的弱了不少,尤其是在吃 GPU 算力的场景下。
  • 🏭 生产环境 Real Talk:如果你是认认真真要搞生产部署 DeepSeek-R1,vLLM 这种专为生产设计的框架才是更稳的选择。
  • 💻 RTX 4090 最佳拍档:单卡 4090 想发挥最大威力?vLLM 的优化更到位!SGLang 那种大规模集群方案,对我们来说就太重了。

4、搭建环境

如果模型推理服务器没有安装CUDA和cuDNN的话,需要自行安装CUDA和cuDNN,并配置环境变量,具体方法可以参考英伟达官网:

CUDA

cuDNN

本文所需的的CUDA、cuDNN、Python和PyTorch版本如下:

  • Linux版本:Ubuntu 22.04.3 LTS
  • CUDA版本:12.1
  • cuDNN版本:8.9.0
  • Python版本:3.10.8
  • PyTorch版本:2.5.1+cu124

读者可以使用VSCode、Cursor或者其它SSH客户端连接到云服务器,然后使用以下命令安装CUDA和cuDNN。

安装前需要确保服务器上已经安装了NVIDIA驱动,可以使用以下命令查看是否安装了NVIDIA驱动:

$ nvidia-smi

安装前,需要确保PyTorch与CUDA的版本对应,否则会报错。

PyTorch与CUDA的对应版本

  • 安装CUDA 12.1
# 添加 CUDA 存储库
$ sudo apt update
$ sudo apt install -y software-properties-common
$ sudo add-apt-repository ppa:graphics-drivers/ppa

# 下载并安装 CUDA 12.1
$ wget https://developer.download.nvidia.com/compute/cuda/12.1/12.1.0/local_installers/cuda_12.1.0_520.61.05_linux.run
$ sudo sh cuda_12.1.0_520.61.05_linux.run

在安装过程中,选择是否安装 NVIDIA 驱动(如果你已经有安装过,可以跳过)。

设置环境变量:

# 在 .bashrc 中添加 CUDA 路径
$ echo "export PATH=/usr/local/cuda-12.1/bin:$PATH" >> ~/.bashrc
$ echo "export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH" >> ~/.bashrc
$ source ~/.bashrc

  • 安装cuDNN 8.9.0

要安装 cuDNN,首先需要从 NVIDIA 官方下载 cuDNN 8.9.0。

下载后,解压并安装 cuDNN。

# 假设 cuDNN 安装包下载到当前目录
$ tar -xzvf cudnn-12.1-linux-x64-v8.9.0.131.tgz

# 将 cuDNN 文件复制到 CUDA 路径
$ sudo cp cuda/include/cudnn*.h /usr/local/cuda-12.1/include
$ sudo cp cuda/lib64/libcudnn* /usr/local/cuda-12.1/lib64
$ sudo chmod a+r /usr/local/cuda-12.1/include/cudnn*.h /usr/local/cuda-12.1/lib64/libcudnn*

# 更新库路径
$ echo "export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH" >> ~/.bashrc
$ source ~/.bashrc

  • 安装Python 3.10.8
# 安装 Python 3.10
$ sudo apt update
$ sudo apt install -y python3.10 python3.10-dev python3.10-distutils

# 设置 Python 3.10 为默认版本
$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1

# 检查 Python 版本
$ python3 --version

  • 安装PyTorch 2.5.1
# 安装 pip
$ sudo apt install -y python3-pip

# 安装 PyTorch 2.5.1+cu124
$ pip install torch==2.5.1+cu124 torchvision torchaudio

# 验证安装
$ python -c "import torch; print(torch.__version__)"

  • 验证安装

验证 CUDA 和 cuDNN 是否正确安装:

# 检查 CUDA 版本
$ nvcc --version

# 检查 cuDNN 版本
$ cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2

验证 PyTorch 是否可以使用 GPU:

$ python -c "import torch; print(torch.cuda.is_available())"

现在已经成功地安装了 CUDA 12.1、cuDNN 8.9.0、Python 3.10.8 和 PyTorch 2.5.1+cu124 环境。

可以使用以下命令查看GPU内存信息

$ nvidia-smi

如果安装正确,则可以看到类似如下的GPU内存信息

nvidia-smi

5、下载DeepSeek模型权重文件

为了在本地部署模型,事先需要从Huggingface上手动下载量化过的DeepSeek-R1的4 bit量化模型bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF

bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF

在这个页面中存在26种尺寸的量化模型。

为了能够在单卡4090(24GB显存)进行推理,我们选择其中的DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf。

根据上文中的GPU显存估算公式,4bit量化模型理论上会占用19.2GB左右的显存,实际该文件大小为19.9GB。

由于模型权重文件较大,不建议直连下载,根据博主的经验,按照下面的方式下载速度较快。

  • 学术资源加速
$ source /etc/network_turbo
  • 安装Huggingface Hub客户端
$ pip install -U huggingface_hub
  • 设置Huggingface镜像源(必须设置,否则下载模型会报错⚠️)
$ export HF_ENDPOINT=https://hf-mirror.com
  • 登录Huggingface(跳过这步)
$ huggingface-cli login
  • 下载模型
$ huggingface-cli download bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF --include "DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf" --local-dir ./
#【这里的./可替换为模型在本地的其它存放路径】

下载成功后的DeepSeek-R1大模型的文件列表如下:

total 19386083
drwxr-xr-x 3 root root        4096 Feb 14 12:34 ./
drwxr-xr-x 4 root root        4096 Feb 14 21:00 ../
drwxr-xr-x 3 root root        4096 Feb 14 10:29 .cache/
-rw-r--r-- 1 root root 19851335840 Feb 14 12:34 DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf

6、下载DeepSeek分词器tokenizer文件

正常情况下,使用vLLM加载上面DeepSeek模型GGUF权重文件后,不需要单独加载分词器文件,即可实现推理。

但建议使用DeepSeek基础模型的 tokenizer 而不是 GGUF 模型的 tokenizer。因为从 GGUF 转换 tokenizer 既耗时又不稳定,尤其是对于一些词汇量较大的模型。

基于以上原因,博主单独下载DeepSeek分词器文件,并使用vLLM进行加载。

在浏览器进入DeepSeek的Hugging Face官方模型库页面,
打开Files and versions标签,找下面2个文件。

将图中2个文件下载到本地。

  • tokenizer.json
  • tokenizer_config.json

然后通过scp等方式分别上传到模型推理服务器。

$ scp -rP [端口号] [本地文路径]  root@[远程服务器地址]:[远程服务器目录]

或者,通过以下命令将上述2个文件下载到模型推理服务器,直接在服务器上修改。

$ huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-32B --include "tokenizer.json" "tokenizer_config.json" --local-dir ./tokenizer

7、搭建推理框架vLLM

高效运行DeepSeek R1需要一个高性能的推理引擎,上文博主已分析过了 vLLM 是最佳选择之一,因为它具有优化的内存管理、快速的执行速度以及与 Hugging Face 模型的无缝集成。

本文使用 vLLM v1 在本地安装和运行 DeepSeek R1,以实现消费级 GPU 上的高速推理。

  • 安装 vLLM 及其依赖项
$ pip install vllm --upgrade
  • 设置环境变量

无缝启用 vLLM v1,只需将 VLLM_USE_V1=1 环境变量设置为 1,而无需对现有 API 进行任何更改。

$ export VLLM_USE_V1=1

8、启动DeepSeek推理服务

一切准备就绪后,执行以下命令使用vLLM加载DeepSeek模型,提供与OpenAI API兼容的DeepSeek推理服务接口。

部署大模型具挑战性的部分是需要根据你自己的具体情况配置每个组件,有可能会频繁发生内存不足 (OOM) 错误,因此选择正确的模型并调整运行参数至关重要。

$ python -m vllm.entrypoints.openai.api_server \
--served-model-name bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF \
--model [DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf文件的存储路径] \
--trust-remote-code \
--host 0.0.0.0 \
--port 6006 \
--max-model-len 2048 \
--dtype float16 \
--enable-prefix-caching \
--enforce-eager \
--max_num_seqs 1 \
--api-key [API访问密钥] \
--tokenizer [分词器tokenizer的存储路径]

根据博主的经验,以上便是最佳设置,针对每个参数,说明如下:

  • served-model-name,指定模型的名称。
  • model,DeepSeek模型权重文件的路径。
  • max-model-len,模型上下文长度。如果未指定,将继承模型自身配置。将max_model_len设置为2048、4096或8196,以找到在没有错误的情况下工作的最大值。如果取值过大,你可能会遇到OOM错误。
  • max_num_seqs,用于配置同时处理多少个请求;由于这将使内存使用量增加一倍,因此最好将其设置为1。
  • trust-remote-code,加载HuggingFace自定义代码时必须启用。
  • host和port,可以根据你的服务器机器的具体情况进行配置。
  • dtype,权重和激活参数的数据类型,用于控制计算精度,常用float16/bfloat16。
  • enforce-eager,用于启用 eager 模式,加快推理速度。
  • enable-prefix-caching,重复调用接口时缓存提示词内容,以加快推理速度。例如,如果你输入一个长文档并询问有关它的各种问题,启用该参数将提高性能。
  • api-key,用于设置API访问秘钥,保证自己的DeepSeek不会被随便访问,自行设置一个字符串即可;当后文部署的知识库应用访问DeepSeek推理服务器时,会检查其请求头中的 API 密钥。
  • tokenizer,如前文所述,单独下载的DeepSeek分词器存储目录。
  • tensor-parallel-size,指定GPU数量,本文只用单卡,因此不用设置此参数。

另一种方法是运行vllm serve命令,加载DeepSeek-R1-Distill-Qwen-32B-GGUF模型。

$ vllm serve
[DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf文件的存储路径] \
--served-model-name bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF \
--trust-remote-code \
--host 0.0.0.0 \
--port 6006 \
--max-model-len 2048 \
--dtype float16 \
--enable-prefix-caching \
--enforce-eager \
--max_num_seqs 1 \
--api-key [API访问密钥] \
--tokenizer [分词器tokenizer的存储路径]

在执行如上所述的命令后,希望没有错误发生。

加载完毕后出现如下信息说明服务成功启动。

INFO 02-19 19:49:36 gpu_model_runner.py:872] Loading model weights took 18.5326 GB
INFO 02-19 19:49:45 kv_cache_utils.py:407] # GPU blocks: 376
INFO 02-19 19:49:45 kv_cache_utils.py:410] Maximum concurrency for 2048 tokens per request: 2.94x
INFO 02-19 19:49:45 core.py:91] init engine (profile, create kv cache, warmup model) took 8.87 seconds
INFO 02-19 19:49:45 api_server.py:756] Using supplied chat template:
INFO 02-19 19:49:45 api_server.py:756] None
INFO 02-19 19:49:45 launcher.py:21] Available routes are:
INFO 02-19 19:49:45 launcher.py:29] Route: /openapi.json, Methods: GET, HEAD
INFO 02-19 19:49:45 launcher.py:29] Route: /docs, Methods: GET, HEAD
INFO 02-19 19:49:45 launcher.py:29] Route: /docs/oauth2-redirect, Methods: GET, HEAD
INFO 02-19 19:49:45 launcher.py:29] Route: /redoc, Methods: GET, HEAD
INFO 02-19 19:49:45 launcher.py:29] Route: /health, Methods: GET
INFO 02-19 19:49:45 launcher.py:29] Route: /ping, Methods: POST, GET
INFO 02-19 19:49:45 launcher.py:29] Route: /tokenize, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /detokenize, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/models, Methods: GET
INFO 02-19 19:49:45 launcher.py:29] Route: /version, Methods: GET
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/chat/completions, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/completions, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/embeddings, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /pooling, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /score, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/score, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /rerank, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v1/rerank, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /v2/rerank, Methods: POST
INFO 02-19 19:49:45 launcher.py:29] Route: /invocations, Methods: POST
INFO:     Started server process [3433]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:6006 (Press CTRL+C to quit)

此时,使用nvidia-smi 查看显存占用情况。

加载DeepSeek后显存占用情况

可以看到,加载DeepSeek后,24GB的显存已被占用21GB。

显存实际占用 = 模型权重占用 + KV cache占用 + 其他内存开销

根据日志信息,这里对显存占用进行分析:

  1. 模型权重内存 (主要占用)
  • 模型权重文件加载后占用 18.5326 GB
  • 这是量化后的 Q4_K_M 格式模型,原始 32B 模型如果是 FP16 格式约需 64GB,量化后显存占用大幅降低
  • 包含 embedding 层、transformer 层参数等
  1. KV Cache 内存
  • 分配了 376 个 GPU blocks 用于存储 KV cache
  • 每个 block 存储的 token 数由 block_size 参数决定(默认 16)
  • 计算公式:KV Cache 内存 = 2 * num_layers * num_heads * head_dim * seq_length * batch_size * dtype_size
  • 支持 2048 token 请求时的最大并发达到 2.94 倍;这部分内存在生成过程中按需分配,可大幅提高推理效率,但也会增加总体显存占用。
  • 当前配置下约占用 3-4 GB
  1. 其他内存开销
  • 中间激活值:约 1-2 GB
  • 系统保留内存:约 0.5-1 GB

因此,整个 GPU 内存消耗可以看作是静态的模型参数和动态计算缓存(KV cache)两大块的合并。

确保 GPU 显存充足不仅要满足模型权重加载的 18.5GB,还需要预留足够空间应付 KV cache 及其他运行时需求。

9、测试DeepSeek推理服务

使用vLLM成功加载模型后,现在就可以使用DeepSeek R1模型了。

  • 通过浏览器查看当前服务接口文档

通过浏览器访问 http://localhost:6006/docs 查看 vLLM 支持的 DeepSeek API 接口

DeepSeek API

  • 通过 curl​ 命令查看当前的模型列表
$ curl http://localhost:6006/v1/models -H "Authorization: Bearer ds-key-001"

输出结果:

{
    "object": "list",
    "data": [
        {
            "id": "/root/autodl-fs/model/deepseek/bartowski/DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf",
            "object": "model",
            "created": 1739969336,
            "owned_by": "vllm",
            "root": "/root/autodl-fs/model/deepseek/bartowski/DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf",
            "parent": null,
            "max_model_len": 2048,
            "permission": [
                {
                    "id": "modelperm-f96541eb4c5849baa7d25b138009a094",
                    "object": "model_permission",
                    "created": 1739969336,
                    "allow_create_engine": false,
                    "allow_sampling": true,
                    "allow_logprobs": true,
                    "allow_search_indices": false,
                    "allow_view": true,
                    "allow_fine_tuning": false,
                    "organization": "*",
                    "group": null,
                    "is_blocking": false
                }
            ]
        }
    ]
}
  • 使用 curl​ 命令测试 DeepSeek API​
$ curl http://localhost:6006/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ds-key-001" \
    -d '{
        "model": "bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF",
        "messages": [
            {"role": "user", "content": "问题:玄武门之变结束的当天,李世民在深夜写下一段独白,他会写什么?"}
        ],
		"temperature": 0.6
    }'

使用建议:

为了获得预期的性能,建议在使用 DeepSeek-R1 系列模型(包括基准测试)时,遵循以下配置:

  • 将温度设置在 0.5-0.7 范围内(推荐 0.6),以防止无休止的重复或语无伦次的输出。
  • 避免添加系统提示;所有指令都应包含在用户提示中。
  • 对于数学问题,建议在提示中包含如下指令:“请逐步推理,并将最终答案放在 \boxed{} 中。”
  • 在评估模型性能时,建议进行多次测试并对结果取平均值。

执行以上命令后,在启动vLLM服务的终端窗口,可以看到以下日志:

INFO 02-19 20:55:19 loggers.py:72] Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 0.0 tokens/s, Running: 0 reqs, Waiting: 0 reqs GPU KV cache usage: 0.0%.
INFO 02-19 20:55:24 loggers.py:72] Avg prompt throughput: 9.2 tokens/s, Avg generation throughput: 36.8 tokens/s, Running: 1 reqs, Waiting: 0 reqs GPU KV cache usage: 4.5%.
INFO 02-19 20:55:29 loggers.py:72] Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 38.7 tokens/s, Running: 1 reqs, Waiting: 0 reqs GPU KV cache usage: 7.2%.
INFO:     127.0.0.1:54702 - "POST /v1/chat/completions HTTP/1.1" 200 OK

下面对日志中提到的几个指标进行说明:

  • Avg prompt throughput 反映了 prompt 处理的速度,数值可能因 prompt 大小及初始处理开销而波动;
  • Avg generation throughput 则表示生成输出过程中的 token 速率,数值较高表明生成效率较好;
  • Running reqsWaiting reqs 分别展示了当前正在处理与等待处理的请求数量,当前系统负载较低(运行1个请求、无等待请求);
  • GPU KV cache usage 表示生成过程中用于缓存的 GPU 内存占用率,随生成 token 数的累计逐步增加,反映模型在动态生成时对内存的使用情况。

这些指标综合起来有助于监控系统的实时性能和资源使用,能够为调优和扩容提供依据。


  • Avg prompt throughput(平均提示吞吐率)
    • 含义:该指标表示系统处理输入 prompt 时,单位时间内处理的 token 数量,通常以 tokens/s 为单位。
    • 日志信息分析
      • 日志中显示有一次为 0.0 tokens/s,另一处为 9.2 tokens/s
      • 这说明在某个时刻(例如刚接收到请求时)prompt 的 token 数处理速度可能较低,而在实际载入或处理 prompt 数据后,平均每秒可以处理大约 9.2 个 token。

  • Avg generation throughput(平均生成吞吐率)
    • 含义:该指标描述模型在生成响应(即生成 tokens 时)的 token 生成速度,同样以 tokens/s 为单位。
    • 日志信息分析
      • 日志中分别显示 36.8 tokens/s38.7 tokens/s
      • 这表示模型在生成阶段的算力较强,平均每秒能产生大约 37 个 token,反映了模型生成阶段的高效性。

  • Running reqs(正在运行的请求数量)
    • 含义:这是当前系统中正处于活跃状态、正在被处理(例如生成响应)的请求数。
    • 日志信息分析
      • 日志中状态显示为 Running: 1 reqs,这表明当前有一个请求在被系统积极处理。

  • Waiting reqs(等待中的请求数量)
    • 含义:表示目前在队列中等待处理、尚未启动生成的请求数量。
    • 日志信息分析
      • 日志中显示为 Waiting: 0 reqs,说明没有请求处于排队等待状态,系统的调度和资源分配都能及时处理进入的请求。

  • GPU KV cache usage(GPU KV缓存使用率)
    • 含义:KV cache(Key-Value Cache)用于 Transformer 模型中保存此前计算得到的 key 和 value,以便在生成过程中复用这些信息,从而避免重复计算。该指标显示当前这部分缓存占用了 GPU 显存的百分比。
    • 日志信息分析
      • 日志中先后显示 4.5%7.2% 的使用率。
      • 这说明随着生成 token 数量的增加,KV cache 会逐渐占用更多的 GPU 显存,反映了生成过程动态累积缓存的结果。
      • KV cache 的设计目的是为避免重复计算,同时支持更高的并发和长序列生成,因此其内存占用会随着生成任务的 token 数增加而逐步上升。

  • 用 Python​ 脚本测试 DeepSeek API​
from openai import OpenAI

openai_api_key = "ds-key-001" #填写前文启动DeepSeek推理服务时设置的API秘钥
openai_api_base = "http://localhost:6006/v1" 
model_name='bartowski/DeepSeek-R1-Distill-Qwen-32B-GGUF'

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

prompt="""
问题:
玄武门之变结束的当天,李世民在深夜写下一段独白,你觉得他会写什么?
"""

chat_response = client.chat.completions.create(
    model=model_name,
    messages=[       
        {"role": "user", "content": prompt},
    ],
    temperature=0.6
)
print("Chat response:", chat_response)

Python​脚本测试DeepSeek API的输出结果如下:

<think>
好,我需要帮用户想一下李世民在玄武门之变结束当天深夜写下的独白。首先,我要了解玄武门之变的背景。这场变故发生在626年,李世民为了争夺皇位,发动了政变,杀死了自己的兄弟李建成和李元吉。

李世民当时的心理应该是复杂的。一方面,他成功地巩固了自己的地位,成为太子,为后来的皇帝铺平了道路。另一方面,他也背负了兄弟相残的罪名,内心一定有很多挣扎和反思。

所以,他的独白可能会反映出他的复杂情绪。他可能会表达对权力的渴望,同时也感到内心的压力和不安。他可能会提到自己曾经的犹豫,以及最终做出决定的艰难。

另外,李世民后来成为了唐太宗,开创了“贞观之治”,这说明他有治国理政的雄心。因此,他的独白中也可能流露出对未来的期望和责任感。

我还需要考虑独白的语气。他可能会用一种反思和严肃的口吻,既不掩饰自己的野心,也不回避内心的矛盾。

综合这些因素,我可以构思一段独白,既展现他的决心,也反映他的内心挣扎,同时为未来的统治埋下伏笔。

最后,我需要确保这段独白符合历史背景和人物性格,让读者能够感受到李世民当时的复杂心境。
</think>

## 《权力的代价》

寒风刺骨,我独自站在玄武门城楼上,看着地上斑驳的血迹。远处的宫灯明明灭灭,像是无数双眼睛在注视着这个改变命运的夜晚。

长叹息一声,我点燃了桌上的蜡烛。火苗在黑暗中摇曳,将我的影子投射在墙上,那影子比往日还要高大几分。

"权力...终究是要用鲜血来浇灌的。"我喃喃自语,指尖无意识地摩挲着腰间的佩剑。剑鞘上还残留着清晨的血迹,那是我亲手抹去的。

脑海中又浮现出那个场景:大哥李建成端着酒杯,脸上带着一贯的温和笑容;四弟李元吉在一旁促狭地笑着,撺掇大哥多喝几杯。我看着他们,突然觉得这兄弟情义太过虚伪可笑。

"你们以为我愿意这样吗?"我握紧了拳头,指甲深深陷入掌心。记得当年在东宫,我夜以继日地读书,谋划着如何治理国家,如何让百姓安居乐业。可他们,却只顾着争权夺利,甚至想要取我性命。

烛光下,我的影子忽明忽暗。我闭上眼睛,仿佛又回到了那个雨夜。雨声中,我听到大哥在后院与人密谋,要如何除掉我这个眼中钉。那一刻,我握紧了刀柄,浑身的血液仿佛都凝固了。

"对不起,大哥。"我低声说着,泪水在眼眶中打转。可转念一想,若我不先动手,今日的我早已是刀下之鬼。这天下,终究是要由有能力的人来治理。

桌上的茶已经凉了,我端起来一饮而尽。苦涩的味道在口中蔓延,像是这权力带来的所有辛酸。远处传来更夫的梆声,新的一天即将开始。

我站起身,拍了拍衣襟上的尘土。既然已经踏上了这条路,就再无回头的余地。我李世民,定不会辜负这大好河山,也定会让大唐盛世重现人间。

9、开放DeepSeek推理服务接口

一般情况下,LLM大模型推理服务与应用服务不会部署在同一台服务器上。

由于知识库应用服务器会调用DeepSeek推理服务,因此需要将DeepSeek推理服务接口开放出来。

通过终端登录知识库应用服务器,执行以下命令后回车(注意:执行后不要关闭该终端窗口):

$ ssh -CNg -L 6006:127.0.0.1:6006 root@[DeepSeek服务器地址] -p [DeepSeek服务器端口号]

如询问yes/no请回答yes,并输入ssh服务的密码。

输入密码回车后无任何其他输出则为正常,如显示Permission denied则可能密码粘贴失败,请手动输入密码 (Win10终端易出现无法粘贴密码问题)

该命令用于建立SSH 隧道实现带端口转发的 SSH 连接,这样可以通过知识库应用服务器上的本地端口( localhost:6006 )访问DeepSeek推理服务器上的API接口。

这通常用于保护对DeepSeek服务器上运行的推理服务的访问,或访问那些无法直接从互联网访问的DeepSeek服务器。

为了测试DeepSeek推理服务接口是否成功开放,请在知识库应用服务器上打开浏览器,访问 http://localhost:6006/docs ,查看是否成功显示如前文所述的DeepSeek接口页面。

【DeepSeek接口文档页面截图】

如在本地电脑上远程访问知识库应用服务器验证DeepSeek推理服务的的话,需要通过以下命令打开知识库应用服务器的防火墙端口。

$ sudo ufw allow 6006/tcp

十、RAGFlow的部署过程

1、部署准备

  • 在RAGFlow应用服务器安装Docker

  • 确保 vm.max_map_count 不小于 262144

    如需确认 vm.max_map_count 的大小:

    $ sysctl vm.max_map_count
    

    如果 vm.max_map_count 的值小于 262144,可以进行重置:

    这里我们设为 262144:

    $ sudo sysctl -w vm.max_map_count=262144
    

    你的改动会在下次系统重启时被重置。如果希望做永久改动,还需要在 /etc/sysctl.conf 文件里把 vm.max_map_count 的值再相应更新一遍:

    vm.max_map_count=262144
    
  • 克隆仓库:

    $ git clone https://github.com/infiniflow/ragflow.git
    
  • 进入 docker 文件夹,利用提前编译好的 Docker 镜像启动服务器

    $ git checkout -f nightly
    $ cd ragflow/docker
    
  • 修改.env配置文件

    $ vim .env
    

    需要修改的配置如下:

    # RAGFLOW_IMAGE=infiniflow/ragflow:v0.17.2-slim
    # 注释掉上面这行
    
    RAGFLOW_IMAGE=swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:nightly
    # 删除上面这行的注释符号
    
    HF_ENDPOINT=https://hf-mirror.com
    # 删除上面这行的注释符号
    
  • 为了防止启动docker时出现错误dependency failed to start: container ragflow-mysql is unhealthy,执行以下命令

    修改init.sql脚本权限。

    chmod 644 init.sql
    

    编辑docker-compose-base.yml文件。

    vim docker-compose-base.yml
    

    将mysql镜像部分的字段改为以下值。

    image:mysql:8.0.39
    interval:20s
    timeout:20s
    retries:30
    restart:always
    

    最终mysql镜像部分的配置如下。

    mysql:
        # mysql:5.7 linux/arm64 image is unavailable.
        image: mysql:8.0.39
        container_name: ragflow-mysql
        env_file: .env
        environment:
        - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
        - TZ=${TIMEZONE}
        command:
        --max_connections=1000
        --character-set-server=utf8mb4
        --collation-server=utf8mb4_unicode_ci
        --default-authentication-plugin=mysql_native_password
        --tls_version="TLSv1.2,TLSv1.3"
        --init-file /data/application/init.sql
        --binlog_expire_logs_seconds=604800
        ports:
        - ${MYSQL_PORT}:3306
        volumes:
        - mysql_data:/var/lib/mysql
        - ./init.sql:/data/application/init.sql
        networks:
        - ragflow
        healthcheck:
        test: ["CMD", "mysqladmin" ,"ping", "-uroot", "-p${MYSQL_PASSWORD}"]
        interval: 20s
        timeout: 20s
        retries: 30
        restart: always
    

2、启动RAGFlow服务

以上工作准备就绪后,执行下面的命令,启动RAGFlow应用。

docker compose -f docker-compose.yml up -d

启动成功后可以查看到以下输出。

启动RAGFlow应用

使用以下命令,查看ragflow-server容器的运行日志。

docker logs -f ragflow-server
Starting nginx...
Starting ragflow_server...
Starting 1 task executor(s) on host 'c981d8986071'...
2025-04-16 12:05:42,549 INFO     19 ragflow_server log path: /ragflow/logs/ragflow_server.log, log levels: {'peewee': 'WARNING', 'pdfminer': 'WARNING', 'root': 'INFO'}
2025-04-16 12:05:46,579 INFO     19 found 0 gpus
2025-04-16 12:05:53,089 INFO     19 init database on cluster mode successfully
2025-04-16 12:05:58,461 INFO     19 load_model /ragflow/rag/res/deepdoc/det.onnx uses CPU
2025-04-16 12:05:58,592 INFO     19 load_model /ragflow/rag/res/deepdoc/rec.onnx uses CPU
2025-04-16 12:06:07,547 INFO     19 
        ____   ___    ______ ______ __               
       / __ \ /   |  / ____// ____// /____  _      __
      / /_/ // /| | / / __ / /_   / // __ \| | /| / /
     / _, _// ___ |/ /_/ // __/  / // /_/ /| |/ |/ / 
    /_/ |_|/_/  |_|\____//_/    /_/ \____/ |__/|__/                             

    
2025-04-16 12:06:07,548 INFO     19 RAGFlow version: v0.17.2-234-gc6b26a31 full
2025-04-16 12:06:07,548 INFO     19 project base: /ragflow
2025-04-16 12:06:07,548 INFO     19 Current configs, from /ragflow/conf/service_conf.yaml:
        ragflow: {'host': '0.0.0.0', 'http_port': 9380}
        mysql: {'name': 'rag_flow', 'user': 'root', 'password': '********', 'host': 'mysql', 'port': 3306, 'max_connections': 100, 'stale_timeout': 30}
        minio: {'user': 'rag_flow', 'password': '********', 'host': 'minio:9000'}
        es: {'hosts': 'http://es01:9200', 'username': 'elastic', 'password': '********'}
        infinity: {'uri': 'infinity:23817', 'db_name': 'default_db'}
        redis: {'db': 1, 'password': '********', 'host': 'redis:6379'}
2025-04-16 12:06:07,549 INFO     19 Use Elasticsearch http://es01:9200 as the doc engine.
2025-04-16 12:06:07,673 INFO     19 GET http://es01:9200/ [status:200 duration:0.123s]
2025-04-16 12:06:07,678 INFO     19 HEAD http://es01:9200/ [status:200 duration:0.004s]
2025-04-16 12:06:07,678 INFO     19 Elasticsearch http://es01:9200 is healthy.
2025-04-16 12:06:07,684 WARNING  19 Load term.freq FAIL!
2025-04-16 12:06:07,688 WARNING  19 Realtime synonym is disabled, since no redis connection.
2025-04-16 12:06:07,693 WARNING  19 Load term.freq FAIL!
2025-04-16 12:06:07,697 WARNING  19 Realtime synonym is disabled, since no redis connection.
2025-04-16 12:06:07,697 INFO     19 MAX_CONTENT_LENGTH: 134217728
2025-04-16 12:06:07,697 INFO     19 MAX_FILE_COUNT_PER_USER: 0
2025-04-16 12:06:10,721 INFO     19 init web data success:2.928006410598755
2025-04-16 12:06:10,723 INFO     19 update_progress lock_value: 658a7e30-2b01-4d02-8301-d1ccab99094b
2025-04-16 12:06:10,723 INFO     19 RAGFlow HTTP server start...
2025-04-16 12:06:10,725 INFO     19 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:9380
 * Running on http://172.18.0.6:9380
2025-04-16 12:06:10,726 INFO     19 Press CTRL+C to quit
2025-04-16 12:06:21,397 INFO     31 task_executor_c981d8986071_0 log path: /ragflow/logs/task_executor_c981d8986071_0.log, log levels: {'peewee': 'WARNING', 'pdfminer': 'WARNING', 'root': 'INFO'}
2025-04-16 12:06:21,398 INFO     31 
  ______           __      ______                     __            
 /_  __/___ ______/ /__   / ____/  _____  _______  __/ /_____  _____
  / / / __ `/ ___/ //_/  / __/ | |/_/ _ \/ ___/ / / / __/ __ \/ ___/
 / / / /_/ (__  ) ,<    / /____>  </  __/ /__/ /_/ / /_/ /_/ / /    
/_/  \__,_/____/_/|_|  /_____/_/|_|\___/\___/\__,_/\__/\____/_/                               
    
2025-04-16 12:06:21,398 INFO     31 TaskExecutor: RAGFlow version: v0.17.2-234-gc6b26a31 full
2025-04-16 12:06:21,399 INFO     31 Use Elasticsearch http://es01:9200 as the doc engine.
2025-04-16 12:06:21,408 INFO     31 GET http://es01:9200/ [status:200 duration:0.008s]
2025-04-16 12:06:21,412 INFO     31 HEAD http://es01:9200/ [status:200 duration:0.003s]
2025-04-16 12:06:21,412 INFO     31 Elasticsearch http://es01:9200 is healthy.
2025-04-16 12:06:21,419 WARNING  31 Load term.freq FAIL!
2025-04-16 12:06:21,423 WARNING  31 Realtime synonym is disabled, since no redis connection.
2025-04-16 12:06:21,428 WARNING  31 Load term.freq FAIL!
2025-04-16 12:06:21,432 WARNING  31 Realtime synonym is disabled, since no redis connection.
2025-04-16 12:06:21,433 INFO     31 MAX_CONTENT_LENGTH: 134217728
2025-04-16 12:06:21,433 INFO     31 MAX_FILE_COUNT_PER_USER: 0
2025-04-16 12:06:21,436 INFO     31 task_executor_c981d8986071_0 reported heartbeat: {"name": "task_executor_c981d8986071_0", "now": "2025-04-16T12:06:21.435+08:00", "boot_at": "2025-04-16T12:06:21.396+08:00", "pending": 0, "lag": 0, "done": 0, "failed": 0, "current": {}}
2025-04-16 12:06:21,437 WARNING  31 RedisDB.get_unacked_iterator queue rag_flow_svr_queue_1 doesn't exist
2025-04-16 12:06:51,459 INFO     31 task_executor_c981d8986071_0 reported heartbeat: {"name": "task_executor_c981d8986071_0", "now": "2025-04-16T12:06:51.458+08:00", "boot_at": "2025-04-16T12:06:21.396+08:00", "pending": 0, "lag": 0, "done": 0, "failed": 0, "current": {}}
2025-04-16 12:07:21,554 INFO     31 task_executor_c981d8986071_0 reported heartbeat: {"name": "task_executor_c981d8986071_0", "now": "2025-04-16T12:07:21.553+08:00", "boot_at": "2025-04-16T12:06:21.396+08:00", "pending": 0, "lag": 0, "done": 0, "failed": 0, "current": {}}
2025-04-16 12:07:51,648 INFO     31 task_executor_c981d8986071_0 reported heartbeat: {"name": "task_executor_c981d8986071_0", "now": "2025-04-16T12:07:51.647+08:00", "boot_at": "2025-04-16T12:06:21.396+08:00", "pending": 0, "lag": 0, "done": 0, "failed": 0, "current": {}}
2025-04-16 12:08:21,836 INFO     31 task_executor_c981d8986071_0 reported heartbeat: {"name": "task_executor_c981d8986071_0", "now": "2025-04-16T12:08:21.835+08:00", "boot_at": "2025-04-16T12:06:21.396+08:00", "pending": 0, "lag": 0, "done": 0, "failed": 0, "current": {}}

如果MySQL启动失败的话,可以使用以下命令,查看ragflow-mysql容器的运行日志。

docker logs -f ragflow-mysql

如果MySQL启动成功,其输出如下所示。

2025-04-16 12:05:20+08:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.39-1.el9 started.
2025-04-16 12:05:20+08:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2025-04-16 12:05:20+08:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.39-1.el9 started.
'/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
2025-04-16T04:05:20.947181Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
2025-04-16T04:05:20.950870Z 0 [Warning] [MY-010918] [Server] 'default_authentication_plugin' is deprecated and will be removed in a future release. Please use authentication_policy instead.
2025-04-16T04:05:20.950907Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.39) starting as process 1
2025-04-16T04:05:20.960700Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2025-04-16T04:05:21.257601Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2025-04-16T04:05:21.523718Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2025-04-16T04:05:21.523770Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2025-04-16T04:05:21.529455Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2025-04-16T04:05:21.561413Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2025-04-16T04:05:21.561510Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.39'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.
2025-04-16T04:05:40.105651Z 9 [Warning] [MY-013360] [Server] Plugin mysql_native_password reported: ''mysql_native_password' is deprecated and will be removed in a future release. Please use caching_sha2_password instead'

3、配置RAGFlow

打开浏览器,输入地址http://127.0.0.1打开前端页面。

RAGFlow登录页面

点击【注册】按钮,输入邮箱、名称和密码,注册账户。

然后使用该邮箱和密码,进行登录。

RAGFlow首页

点击页面右上角头像图标,然后点击【系统】可以查看系统运行状态。

RAGFlow运行状态

点击【模型提供商】可以为RAGFlow配置大模型。

【模型提供商】用于对接供应商的大语言模型,支持对接主流的大模型,包括本地私有大模型(DeepSeek / Llama 等)。

除了DeepSeek等文本对话模型外,还支持向量模型、重排模型、视觉模型、文生图模型等。

展开【待添加的模型】,找到【OpenAI-API-Compatible】图标。

OpenAI-API-Compatible

点击【添加模型】,配置参数如下:

添加模型

  • 模型类型:选择chat。
  • 模型名称:根据自己的情况随便填写,比如deepseek-r1-distill-qwen-32b
  • 基础Url:参考上一章“用 Python​ 脚本测试 DeepSeek API”的变量openai_api_base,使用http://localhost:6006/v1
  • API Key:参考上一章“用 Python​ 脚本测试 DeepSeek API”的变量openai_api_key,使用ds-key-001
  • 最大token数:参考上一章使用vLLM“启动DeepSeek推理服务”所配置的参数max-model-len,可设置为2048。

点击【系统模型设置】,在【聊天模型】中选择上面刚添加过的模型deepseek-r1-distill-qwen-32b,在【嵌入模型】中选择BAAI/bge-large-zh-v1.5

系统模型设置

十一、搭建RAGFlow知识库

1、创建知识库

选择切片方法

可以将文件上传到 RAGFlow 中的知识库,并将其解析为数据集。知识库实际上是数据集的集合。RAGFlow 中的问题回答可以基于特定的知识库或多个知识库。RAGFlow 支持的文件格式包括文档(PDF、DOC、DOCX、TXT、MD)、表格(CSV、XLSX、XLS)、图片(JPEG、JPG、PNG、TIF、GIF)和幻灯片(PPT、PPTX)。

创建一个知识库:

  1. 点击页面顶部中间的知识库 > 创建知识库。

  2. 输入知识库名称并点击确定以确认更改。

创建知识库

进入知识库的配置页面。

配置知识库

对知识库进行适当配置对于未来的 AI 聊天至关重要。例如,选择错误的嵌入模型或分块方法会导致语义丢失或聊天中答案不匹配的意外情况。

选择嵌入模型。一旦知识库中有了文本块,就无法更改嵌入模型。要切换到不同的嵌入模型,必须删除知识库中所有现有的文本块。原因是RAGFlow必须确保特定知识库中的所有文件都使用相同的嵌入模型进行解析。

以下嵌入模型可以本地部署:

  • BAAI/bge-large-zh-v1.5
  • maidalun1020/bce-embedding-base_v1

RAGFlow 提供多种切片方法,适用于不同的文档布局和文件格式。

模板描述文件格式
通用通用分块方式,系统将使用视觉检测模型将连续文本分割成多个片段,这些连续的片段被合并成块(token数不超过设置参数“token数”)。DOCX, XLSX, XLS(Excel97~2003), PPT, PDF, TXT, JPEG, JPG, PNG, TIF, GIF, CSV, JSON, EML, HTML
问答适用于问答对文档。如果文件是 Excel 格式,则应由两个列组成 没有标题:一个提出问题,另一个用于答案。如果文件是 csv/txt 格式,以 UTF-8编码且用TAB作分开问题和答案的定界符。XLSX, XLS(Excel97~2003), CSV/TXT
简历在选择此分块方法后,RAGFlow会将上传的简历解析成一个结构化数据。DOCX, PDF, TXT
手动适合于具有章节结构的文档。仅支持PDF。RAGFlow按照层级最低的标题对文档进行切片,因此,同一部分中的图和表不会被分割,并且块大小可能会很大,需要调大tokens。PDF
表格针对表格文件分块。第一行必须是列标题。表中的每一行都将被视为一个块。列标题必须是有意义的术语,以便大模型能够理解。 列举标题时最好使用斜杠/分隔标题的同义词,使用括号列出枚举值,例如 gender/sex(male,female)颜色(黄色、红色、棕色)尺码(M、L、XL、XXL);对于 csv 或 txt 文件,列之间的分隔符为 TAB。XLSX, XLS(Excel97~2003), CSV/TXT
论文适用于论文。RAGFlow按照论文章节结构进行切片。RAGFlow可以更好地概括论文中相关章节的内容, 产生更全面的答案,帮助读者更好地理解论文。 缺点是它增加了 LLM 对话的上下文token并增加了计算成本, 所以在对话过程中,你可以考虑减少topN的设置。PDF
书籍适用于图书。DOCX, PDF, TXT
法律适用于法律文件,RAGFlow使用文本特征检测分割点,所有上层文本都会包含在chunk中。DOCX, PDF, TXT
演示文稿适用于演示文件,每个页面都将被视为一个块,并且每个页面的缩略图都会被存储。PDF, PPTX
图片适用于纯图片文件。JPEG, JPG, PNG, TIF, GIF
整体每个文档整体分块(作为一个整体)。对于一个文档,它将被视为一个完整的块,不会被分割。如果你需要一篇文章的全部上下文,并且所选大模型的上下文长度覆盖了文档长度,可以尝试这种方法。DOCX, XLSX, XLS(Excel97~2003), PDF, TXT
标签标签知识库作为其它知识库的标签集,如果文件为Excel格式,则它应该包含两列无标题:一列用于内容,另一列用于标签,内容列位于标签列之前。可以接受多个工作表,只要列结构正确即可。如果文件为 CSV/TXT 格式,则必须使用 UTF-8 编码并以 TAB 作为分隔符来分隔内容和标签。在标签列中,标签之间使用英文 逗号 分割。XLSX, CSV/TXT
  1. 点击 + 新增文件 > 本地文件,开始上传特定文件到知识库。

  2. 在已上传的文件条目中,点击播放按钮开始文件解析:

配置知识库

当文件解析完成后,其解析状态变为 成功

每次创建知识库时,都会在 root/.knowledgebase 目录下生成一个同名文件夹。

设置元数据

在知识库的数据集页面,可以向任何已上传的文件添加元数据。此方法能够为现有文件或数据集“标记”额外的信息,如 URL、作者、日期等。在 AI 聊天中,这些信息将与检索到的内容块一起发送到 LLM 以生成内容。

例如,如果你有一个 HTML 文件的数据集,并希望 LLM 在响应你的查询时引用源 URL,请向每个文件的元数据添加一个 "url" 参数。

设置元数据

元数据为 json 格式(不可搜索)。如果提示中包含此文档的任何块,它将被添加到 LLM 的提示中。

示例:

元数据为:

{ “作者”:“Alex Dowson”, “日期”:“2024-11-12” }

提示将为:

文档:the_name_of_document

作者:Alex Dowson

日期:2024-11-12

相关片段如下:

这是块内容…

这是块内容…

标签知识库与普通知识库的区别

标签知识库用于对其它知识库中的每个文本块进行标记。对这些文本块的查询也将自动关联相应标签。此功能基于文本相似度,能够为数据集的文本块批量添加更多领域知识,从而显著提高检索准确性。该功能还能提升大量文本块的操作效率。

标签集和关键词之间的区别

标签集是一个由用户定义和管理的封闭集,而自动生成的关键词属于开放集合。在给你的知识库文本块批量打标签之前,你需要先生成标签集作为样本。自动关键词提取功能中的关键词由 LLM 生成,此过程相对耗时,并且会产生一定的 Token 消耗。

查看解析结果

RAGFlow 具有可见性和可解释性,允许您查看分块结果并在必要时进行干预。操作步骤如下:

  1. 点击完成文件解析的文件以查看分块结果:

    解析结果

  2. 将鼠标悬停在每个快照上以快速查看每个块。

  3. 双击分块文本以添加关键词或在必要时进行手动更改:

    修改解析结果

可以向文件块添加关键词,以提高其在包含这些关键词的查询中的排名。此操作会增加其关键词权重,并可能提升其在搜索列表中的位置。

  1. 在检索测试中,在测试文本中提问以检查配置是否有效。

在文件上传并解析后,建议在继续配置聊天助手之前运行检索测试。运行检索测试绝非多余或不必要的步骤!正如调试精密仪器一样,RAGFlow 需要仔细调整才能提供最佳的问题回答性能。知识库设置、聊天助手配置以及指定的大模型和小模型都可能对最终结果产生重大影响。运行检索测试可以验证是否能检索到预期的文本块,能够快速识别需要改进的地方或发现需要解决的问题。

RAGFlow 采用混合搜索进行检索,混合搜索使用关键字搜索与向量搜索相结合。

  • 相似度阈值:相似度低于该阈值的分块将被过滤。默认设置为 0.2。
  • 关键字相似度权重:关键字搜索所占的百分比。默认设置为 0.7。

若未选择重排序模型,混合搜索方式将关键字搜索与向量搜索结合使用。

若选择了重排序模型,混合搜索方式将关键字搜索与重排序搜索结合使用。采用重排序模型会非常耗时。

2、创建聊天助手

知识库、聊天助手及文件管理是 RAGFlow 的三大支柱。在 RAGFlow 中的聊天基于特定知识库或多个知识库。一旦创建了知识库、完成文件解析并运行检索测试,即可开始创建聊天助手。

  • 点击页面顶部中央的“聊天”标签 > 创建助手以显示下一个对话的聊天配置对话框。

    RAGFlow 支持为每个对话选择不同聊天模型,同时允许在系统模型设置中设定默认模型。

助理设置

  • 助理设置:

    • 助手名称是聊天助手的名称。每个助手对应一个具有独特知识库、提示词、混合搜索方式和大模型设置组合的对话。
    • 空响应:
      • 若希望限制 RAGFlow 的回答仅基于你自己的知识库,请在此处填写回复内容。当未检索到答案时,系统将统一回复在此设定的内容。
      • 若希望 RAGFlow 在无法从知识库中检索到答案时即兴发挥,请留空此选项,但可能导致幻觉生成。
    • 显示引文:这是 RAGFlow 的核心特性且默认启用。RAGFlow 清晰展示其回答所依据的信息来源。
    • 知识库。可以选择一个或多个知识库,但请确保它们使用相同的嵌入模型,否则会出现错误。

提示引擎

  • 提示引擎:
    
    • 系统提示词:可以重写系统提示词,初期也可以保留默认提示不变。
    • 相似度阈值:设定每个文本块的相似度“门槛”,默认值为 0.2。相似度得分低于此值的文本块将被过滤,不会出现在最终响应中。
    • 关键字相似度权重:默认设置为 0.7。RAGFlow 采用混合评分方式评估不同文本块的相关性,该值设定了关键字相似度在混合评分中的权重占比。
      • 若未选择重排模型,混合评分将使用关键词相似度与向量相似度,向量相似度部分的默认权重为 1-0.7=0.3。
      • 若选择重排模型,混合评分将使用关键字相似度与重排模型得分,重排模型得分的默认权重为 1-0.7=0.3。
    • Top N:决定了输入LLM的最大文本块数量。换言之,即使检索到更多文本块,系统也仅会提供前 N 个文本块作为输入。
    • 多轮对话优化:利用多轮对话中的现有上下文增强用户查询功能,默认开启。启用后将额外消耗LLM令牌,并显著增加答案生成的时间。
    • 使用知识图谱:决定在多跳问答检索过程中是否调用指定知识库中的知识图谱。启用后将在实体、关系及社区报告块间进行迭代搜索,会大幅增加检索耗时。
    • 推理:控制是否通过 Deepseek-R1/OpenAI o1 等推理流程生成答案。启用后,当聊天模型遇到未知主题时,将自主整合深度研究进行问答,动态搜索外部知识并通过推理生成最终答案。
    • Rerank模型:指定重排模型,默认情况下留空。
      • 若未选择重排模型,混合评分将使用关键词相似度与向量相似度,向量相似度部分的默认权重为 1-0.7=0.3。
      • 若选择重排模型,混合评分将使用关键词相似度与重排模型得分,重排模型得分的默认权重为 1-0.7=0.3。
    • 变量指系统提示中要使用的变量(键)。 {knowledge} 为保留变量。点击“添加”可为系统提示增加更多变量。
      • 如不确定变量的逻辑,请不要修改此处。
      • 截至 v0.17.2 版本,若在此处添加自定义变量,传递其值的唯一方式是调用以下2种之一:
        • 采用 HTTP 访问聊天助手
        • 采用 Python 访问聊天助手

模型设置

  • 模型:
    
    • 在模型选项中:选择chat模型。尽管已在系统模型设置中默认选定聊天模型,RAGFlow 仍允许为当前对话选用其他聊天模型。

    • 自由度:调整温度值、Top P、存在处罚及频率惩罚的设置,反映模型的响应自由度。从即兴创作、精确到平衡,3种模式均对应温度值、Top P、存在处罚与频率惩罚的不同组合。

      该参数提供三种选项:

      • 即兴创作:生成更具创造性的回答。
      • 精确:(默认)生成更为保守的响应。
      • 平衡:介于即兴与精确之间的折中方案。
    • 温度:控制模型输出随机性的参数。
      默认为 0.1。

      • 较低的值会导致输出更加确定和可预测。
      • 较高的值会带来更具创造性和多样化的输出。
      • 温度值为零时,相同的提示将产生相同的输出。
    • Top P:核心采样。
      
      • 通过设定阈值 P 并限制采样至累积概率超过 P 的令牌,降低生成重复或不自然文本的可能性。
      • 默认值为 0.3。
    • 存在处罚:鼓励模型在响应中包含更多样化的令牌。

      • 较高的存在惩罚值会使模型更倾向于生成尚未出现在已生成文本中的令牌。
      • 默认值为 0.4。
    • 频率惩罚:用于阻止模型在生成文本中过于频繁地重复相同的单词或短语。

      • 较高的频率惩罚值会使模型在使用重复标记时更为保守。
      • 默认值为 0.7。

请注意,某些设置可能会消耗大量时间。如果你经常发现回答问题耗时较长,以下是一份可供参考的优化清单:

  • 在聊天配置对话框的提示引擎标签页中,禁用多轮优化将减少从LLM获取答案所需的时间。
  • 在聊天配置对话框的提示引擎标签页中,留空重排模型字段将显著缩短检索时间。
  • 使用重排模型时,请确保配备 GPU 以加速处理,否则重排过程会极其缓慢。

注:重排模型在某些场景下不可或缺。速度与性能之间总需权衡,需要根据具体案例评估利弊。

  • 在聊天配置对话框的助理设置标签页中,禁用关键词分析功能将缩短从LLM获取答案的时间。
  • 与聊天助手对话时,点击当前对话框上方的灯泡图标,并在弹出的窗口中向下滚动以查看每项任务所耗时间:

chat日志

耗时指标描述
Total本轮对话总耗时,包括片段检索与答案生成。
检查 LLM验证指定在 LLM 所需时间。
创建检索器创建块检索器的时间。
绑定嵌入模型初始化嵌入模型实例的时间。
绑定 LLM初始化 LLM 实例所需时间。
优化问题利用多轮对话上下文优化用户查询的问题。
绑定重排模型初始化用于块检索器的重排模型实例所需时间。
生成关键词从用户查询中提取关键词所需时间。
检索检索数据块的时间。
生成答案生成答案所需时间。

自 v0.17.0 版本起,RAGFlow 支持在 AI 聊天中集成Agent推理实现Deep Research功能。下图展示了 RAGFlow 深度研究的工作流程:

Deep Research

要激活此功能,需要完成以下配置:

  • 在聊天助手对话的提示引擎标签下启用推理功能。
  • 在聊天助手对话的助理设置标签下输入正确的 Tavily API 密钥以启用基于 Tavily 的网络搜索功能。

十二、Agent智能体原理

最近 AI 圈可谓是风起云涌,每天都有新东西冒出来。DeepSeek 有时候聪明得吓人,能跟你天南地北地聊,还能写代码、写文案。

但说实话,用着用着,你可能也会遇到这种情况:

  • “帮我查一下今天北京到上海的最低机票价格,然后订一张?” —— 它可能会告诉你怎么查,但它自己“订”不了。

  • “分析一下我们上个季度的用户流失数据,找出主要原因,并生成一份报告?” —— 它能理解你的意图,甚至能告诉你分析思路,但它没法直接连接数据库、跑脚本、再把结果整理成 PPT。

  • “监控一下我们服务器的 CPU 使用率,如果连续 5 分钟超过 80%,就给我发个钉钉通知?” —— 它知道这个逻辑,但无法实时监控,更别说调用钉钉 API 了。

是不是感觉,这些 LLM 虽然“懂”很多,像个博学的“大脑”,但好像缺了点“手脚”,无法真正地行动起来,去完成现实世界里的复杂任务?

如果你有这种感觉,那么恭喜你,你已经触摸到了 AI 发展的下一个前沿 —— 智能体 (Agent)

“智能体”听起来有点科幻,这到底是个啥?

别被名字吓到,其实概念不复杂。你可以把 Agent 想象成一个 “打了鸡血、带了装备、有了记性”的超级版大模型

它不再仅仅是一个被动的“聊天机器人”或“文本生成器”。它更像一个具备 自主规划、决策、执行能力 的 “数字员工” 或 “智能助手”。

想象一下,你有一个非常聪明的私人助理。你告诉他一个目标:“帮我规划一次下周去上海的差旅,包括机票、酒店和简单的行程安排。”

一个普通的 LLM 可能会给你一段文字,告诉你查机票可以用哪些网站,订酒店需要注意什么,上海有哪些景点。这就像是给你了一本“攻略”。

但一个 Agent智能体 呢?它会接收到你的目标后,不仅仅是给你攻略,它会:

  • 理解目标: “规划上海差旅”。

  • 分解任务: 规划差旅包含订机票、订酒店、做行程。

  • 思考步骤: 订机票需要知道日期、出发地、目的地;订酒店需要知道日期、地点、预算;做行程需要考虑时间和兴趣。

  • 调用工具(关键!):

    • 它可能会“打开”一个机票预订网站 API 或工具。

    • 输入日期、地点,查询航班信息。

    • “打开”一个酒店预订网站 API 或工具。

    • 输入日期、地点、预算,查询酒店信息。

    • “打开”一个地图或旅游攻略 API 或工具,查询上海的景点和交通。

  • 执行并观察: 调用工具后,它会得到结果(比如航班列表、酒店信息)。

  • 整合与推理: 根据查询到的信息,结合你的偏好(如果有的话),它会进行筛选、比较,然后做出决策。

  • 行动: 它可能直接帮你预订(如果授权的话),或者生成一份包含详细航班、酒店信息以及推荐行程的报告给你。

  • 记忆与学习: 在这个过程中,它会记住你这次的偏好,下次规划时可能就会考虑进去。如果在执行过程中遇到问题(比如某个航班没票了),它也能调整计划。

看到了吗? Agent 和普通 LLM 的最大区别在于:Agent 拥有自主决策、规划、利用外部工具并从环境中获取信息的能力。它不仅仅是生成文本,它是在执行任务,是在“行动”!

Agent智能体架构

和单纯的 LLM 相比,一个典型的 LLM Agent 通常具备以下几个核心要素:

  • 核心大脑 (Core Brain - The LLM): 这当然还是基础。强大的 LLM(比如 GPT-4, Claude 3 等)负责理解指令、进行推理、制定计划、甚至自我反思。它是思考和决策的核心。

  • 规划能力 (Planning): 这是 Agent 的关键升级。面对一个复杂任务(比如上面提到的“订机票”或“数据分析”),Agent 不会一步到位,而是会像人一样,先把任务拆解成一系列更小、可执行的步骤。比如,“订机票”任务可能被拆解为:

    • 步骤 1: 理解用户意图(目的地、时间)。

    • 步骤 2: 选择并调用“机票查询”工具 (API)。

    • 步骤 3: 分析查询结果,找出最低价。

    • 步骤 4: 向用户确认是否预订。

    • 步骤 5: (如果确认) 调用“机票预订”工具 (API)。

    • 步骤 6: 返回预订结果给用户。

  • 记忆能力 (Memory): 为了完成多步骤任务,Agent 需要“记性”。这包括:

    • 短期记忆: 记住当前对话的上下文、任务的中间步骤和结果。

    • 长期记忆: (更高级的 Agent)能够存储和检索过去的经验、用户偏好、知识库信息,以便在未来任务中表现得更好、更个性化。想象一下,一个 Agent 记住了你常用的出差城市和航空公司偏好。

  • 工具使用 (Tool Use): 这是 Agent 的“手脚”!为了与外部世界交互和获取信息,Agent 需要能够调用各种工具。这些“工具”可以是:

    • API 调用: 查询天气、搜索信息、调用公司内部服务、控制智能家居等。

    • 代码执行: 运行 Python 脚本进行数据处理、计算等。

    • 数据库查询: 直接从数据库获取或更新信息。

    • Web 浏览: 访问网页,提取需要的信息。

    • 其他应用: 与 Slack, Notion, Jira 等工具集成。

  • 行动与观察 (Action & Observation): Agent 根据规划和当前情况,决定 执行 哪个动作(比如调用哪个工具、传递什么参数)。执行后,它会 观察 结果(API 返回的数据、代码运行的输出、网页内容等),并将这些新的信息纳入考虑,用于下一步的决策或反思。

把这些要素组合起来,LLM Agent 就形成了一个 “思考 -> 行动 -> 观察 -> 再思考” 的闭环。它能够根据目标,动态地规划路径,利用工具,处理反馈,最终达成任务。

听起来很酷,那它是怎么工作的呢?(稍微技术一点点)

目前实现 LLM Agent 的流行范式之一是 ReAct (Reasoning and Acting)。这个思路很符合直觉:Agent 在每一步都会进行 推理 (Reasoning) 来决定下一步该做什么,然后 行动 (Acting) 去执行,接着 观察 (Observation) 结果,再进行下一轮推理。

举个例子,假设你让 Agent “查找 LangChain 这个库的 GitHub 星标数”。它的内心戏可能是这样的:

  • 思考 (Thought): 用户想知道 LangChain 的 GitHub 星标数。我需要找到它的 GitHub 仓库。最好的方法是使用搜索引擎。

  • 行动 (Action): 调用 search(“LangChain GitHub repository”) 工具。

  • 观察 (Observation): 搜索结果返回了 LangChain 的 GitHub 链接:https://github.com/langchain-ai/langchain。

  • 思考 (Thought): 好的,我拿到了 GitHub 链接。现在我需要访问这个页面,找到星标数。但我不能直接“看”网页,我需要一个能解析网页内容的工具,或者一个专门查询 GitHub 星标的工具。假设我有一个 get_github_stars(repo_url) 工具。

  • 行动 (Action): 调用 get_github_stars(“https://github.com/langchain-ai/langchain”) 工具。

  • 观察 (Observation): 工具返回结果:105k stars (假设数字)。

  • 思考 (Thought): 我已经得到了用户想要的信息:105k 星标。任务完成。现在我需要把这个结果告诉用户。

  • 行动 (Final Answer): “LangChain 在 GitHub 上目前有大约 105k 个星标。”

看到了吗?Agent 通过一步步的推理、工具调用和观察反馈,最终完成了任务。

Agent智能体工作流

当然,这只是一个简化的例子。现实中的 Agent 架构会更复杂,可能会涉及到更精密的规划算法 (如 Tree of Thoughts)、更强大的记忆系统 (如向量数据库结合 RAG)、以及更复杂的工具选择和错误处理机制。

LangChainLlamaIndex 这样的开源框架,就提供了构建 LLM Agent 的“瑞士军刀”,它们封装了很多底层的复杂性,让开发者可以更方便地集成 LLM、记忆模块、工具集,快速搭建自己的 Agent 应用。

这玩意儿对我们开发者和产品经理意味着什么?

这可太重要了!LLM Agent 的出现,不仅仅是技术上的进步,它可能会彻底改变我们构建和使用软件的方式。

对于开发者来说:

  • 新的可能性: 你可以构建出以前无法想象的应用。比如,一个能自主调试代码、查找文档、甚至提交 PR 的 AI 编程助手?一个能自动处理用户反馈、创建工单、并跟踪进度的系统?

  • 自动化复杂工作流: 很多需要人工介入的多步骤、跨系统操作,现在有了被 Agent 自动化的潜力。解放生产力!

  • 更强大的后端能力: Agent 可以作为你应用后端的一部分,处理复杂的业务逻辑,提供更智能、更主动的服务。

对于产品经理来说:

  • 颠覆性的产品体验: 想象一下,你的用户不再需要点击 N 个按钮、切换 M 个页面来完成任务,只需要用自然语言告诉 Agent 他们的目标,Agent 就能搞定一切。这会带来多大的用户体验提升?

  • 解决更复杂的用户问题: 以前因为实现成本太高或技术限制而无法解决的用户痛点,现在可以通过 Agent 来尝试解决。

  • 全新的产品形态: Agent 本身就可以成为一种新的产品形态。比如,个性化的 AI 旅行规划师、能帮你管理投资组合的 AI 理财顾问、能帮你处理邮件和日程的 AI 行政助理等。

  • 挖掘数据的新方式: Agent 可以根据你的自然语言指令,自动去探索数据、进行分析、生成报告,让数据洞察变得前所未有的简单。

十三、搭建RagFlow Agent智能体

RAGFlow v0.8.0 引入了智能体机制,前端配备无代码工作流编辑器,后端采用基于图的全面任务编排框架。该机制构建于 RAGFlow 现有 RAG 解决方案之上,旨在协调查询意图分类、对话引导及查询重写等搜索技术,以实现更高的检索率、适应更复杂的场景。

点击页面顶部中央的“Agent”标签页以显示 Agent 页面。

Agent智能体应用场景

点击【+创建智能体】以显示智能体模板页面:

Agent智能体模板

如果从零开始创建智能体,请点击空白卡片。

如果要基于我们的某个模板创建智能体,将鼠标悬停在目标卡片上(如General-purpose chatbot),点击“使用该模板”,在弹出对话框中为您的智能体命名,然后点击“确定”。

  • 一般来说,现在可以进行以下操作:

    • 将所需组件拖拽至您的工作流中,
    • 选择要使用的知识库,
    • 更新特定组件的设置,
    • 更新LLM设置
    • 设置特定组件的输入和输出,以及其他更多功能。
  • 点击“保存”以应用对Agent智能体的更改,点击“运行”进行测试。

进入低代码工作流编辑器页面。左侧面板列出了agent智能体组件:分隔线上方是工作流组件;下方是工具组件。RAGFlow正在努力扩展这些组件。

Agent智能体低代码工作流编辑界面

点击“开始”工作流组件设置开场白,这是工作流的起始组件。

工作流组件-开始

点击“问题分类”工作流组件,对用户输入进行分类并配置业务策略。

工作流组件-分类

点击“生成回答”工作流组件,设置LLM根据检索到的信息生成响应。

工作流组件-生成

点击“知识检索”工作流组件,设置从指定的知识库中检索信息;必须配置知识库才能正常工作。

工作流组件-检索


1、RAGFlow智能体工作流组件

组件名称描述
开始组件工作流程中的起始组件。
生成组件该组件促使 LLM 做出适当响应。
交互组件接收用户输入并显示响应的组件。
检索组件从指定数据集中检索信息的组件。
分类组件对用户输入进行分类并相应应用策略的组件。
关键词组件从用户查询中提取关键词的组件。
消息组件用于发送静态消息的组件。
重写组件用于重写用户查询的组件。
条件组件评估指定条件是否满足并据此引导执行流程的组件。
集线器组件将执行流程导向下游组件的组件。
模板转换组件用于格式化用户输入或其他组件输出的组件。
循环组件将文本输入分割为多个文本片段,并为每个片段执行…
备注组件用于保存设计备注的组件。

(1)开始组件

工作流程中的起始组件。

开始组件用于设置开场问候语或接收用户输入。无论是通过模板还是从零开始(基于空白模板)创建agent智能体时,该组件都会自动出现在画布上。工作流程中应仅包含一个 开始组件 组件。

场景

开始 组件在所有情况下都必不可少。每个agent智能体都包含一个不可删除的 开始 组件。

配置项

点击组件以显示其配置窗口。在此处,您可以设置开场问候语和智能体的输入参数(全局变量)。

ID

ID 是工作流中组件的唯一标识符。与其他组件的 ID 不同,开始组件的 ID 不可更改。

开场白

开场白是agent智能体向用户发送的第一条消息,可以是一句欢迎语或引导用户继续操作的指示。

全局变量

您可以在“开始”组件中设置全局变量,这些变量可以是必填或选填的。一旦设定,用户在与agent智能体交互或聊天时需为这些变量提供值。点击“+添加变量”可添加全局变量,每个变量包含以下属性:

  • 键:必填

    唯一的变量名称。

  • 名称:必填

    描述性名称,提供关于变量的更多详细信息。

    例如,如果键设置为 lang ,可以将其名称设置为 Target language

  • 类型:必填

    变量的类型:

    • 单行:接受不含换行符的单行文本。
    • 段落:接受包含换行符的多行文本。
    • 选项:要求用户从下拉菜单中选择此变量的值。并且您需要为下拉菜单设置至少一个选项。
    • 文件:要求用户上传一个或多个文件。
    • 整数:接受整数作为输入。
    • 布尔值:要求用户在开启和关闭之间切换。
  • 可选:一个开关,指示该变量是否为可选项。

(2)生成组件

该组件促使LLM做出恰当响应。

生成组件对LLM进行微调并设定其提示。

场景

当需要LLM协助完成摘要、翻译或控制各类任务时,生成组件至关重要。

配置项

模型

点击 Model 的下拉菜单以显示模型配置窗口。

  • 模型:指定使用的聊天模型。

    • 确保在模型提供商页面正确设置聊天模型。
    • 您可以为不同组件采用不同模型,以增强灵活性或提升整体性能。
  • 自由度:快速调节温度值、Top P、存在惩罚及频率惩罚的快捷设置,反映模型的响应自由度层级。从即兴发挥、精确控制到平衡模式,每个预设配置均对应温度值、Top P、存在惩罚与频率惩罚的独特组合。

    该参数提供三种选项:

    • 即兴模式:生成更具创造性的回答。
    • 精确模式:(默认)生成更为保守的响应。
    • 平衡模式:介于即兴与精确之间的折中方案。
  • 温度值:控制模型输出随机性的参数。
    默认为 0.1。

    • 较低的值会导致输出更加确定和可预测。
    • 较高的值会带来更具创造性和多样化的输出。
    • 温度值为零时,相同的提示将产生相同的输出。
  • Top P:核心采样。

    • 通过设定阈值 P 并限制采样至累积概率超过 P 的令牌,降低生成重复或不自然文本的可能性。
    • 默认值为 0.3。
  • 存在惩罚:鼓励模型在响应中包含更多样化的令牌。

    • 较高的存在惩罚值会使模型更倾向于生成尚未出现在已生成文本中的令牌。
    • 默认值为 0.4。
  • 频率惩罚:用于阻止模型在生成文本中过于频繁地重复相同的单词或短语。

    • 较高的频率惩罚值会使模型在使用重复标记时更为保守。
    • 默认值为 0.7。

(3)交互组件

接收用户输入并显示响应。

交互组件作为人与机器人之间的接口,接收用户输入并展示代理的响应。

场景

在需要展示代理响应或人机交互的场景中,交互组件不可或缺。

例如,交互组件用于显示最终翻译,或者客户服务代理模板,其中 交互组件是 开始组件 的直接下游,用于显示用户与代理之间的多轮对话。

(4)检索组件

从指定数据集中检索信息。

场景

在大多数 RAG 场景中,检索组件至关重要,它从指定的知识库中提取信息,然后发送至LLM以生成内容。

配置项

点击检索组件可打开其配置窗口。

输入

检索组件依赖输入变量来指定其数据输入(查询)。在输入部分点击+添加变量以添加所需的输入变量。输入变量有两种类型:引用和文本。

  • 引用:使用组件的输出或用户输入作为数据源。您需要从下拉菜单中选择:
    • 组件输出下的组件 ID,或
    • 位于开始组件的全局变量。
  • 文本:使用固定文本作为查询。需输入静态文本。

相似度阈值

RAGFlow 在检索过程中采用加权关键词相似度与加权向量余弦相似度相结合的方式。此参数设定了用户查询与数据集中存储的文本块之间相似度的阈值。任何相似度得分低于此阈值的文本块将被排除在结果之外。

默认值为 0.2。

关键词相似度权重

此参数设置关键词相似度在综合相似度分数中的权重。两个权重之和必须等于 1.0。其默认值为 0.7,这意味着向量相似度在综合搜索中的权重为 1 - 0.7 = 0.3。

Top N

此参数从检索到的块中选择"前 N 项"并传递给LLM。

默认为 8。

重排模型

可选

若选择重排模型,将结合加权关键词相似度与加权重排序分数进行检索。

使用重排模型会显著增加系统响应时间。

Tavily API 密钥

可选

在此输入 Tavily API 密钥以在检索过程中启用 Tavily 网络搜索。获取 Tavily API 密钥的说明请参见此处。

使用知识图谱

是否在检索多跳问题时使用指定知识库中的知识图谱。启用后,将涉及跨实体、关系和社区报告块的迭代搜索,显著增加检索时间。

知识库

可选

选择要从中检索数据的知识库。

  • 如果未选择任何知识库,意味着与代理的对话将不基于任何知识库,请确保“空响应”字段留空以避免错误。
  • 若选择多个知识库,必须确保所选知识库(数据集)使用相同的嵌入模型,否则会出现错误提示。

空响应

  • 当查询未从知识库中检索到结果时,将此设为响应;
  • 留空此字段以允许聊天模型在无结果时即兴发挥。

(5)分类组件

对用户输入进行分类并相应应用策略。

分类组件通常是交互组件的下游。

场景

当你需要LLM来帮助识别用户意图并应用适当处理策略时,分类组件至关重要。

配置项

输入

分类组件依赖输入变量来指定其数据输入(查询)。在输入部分点击+添加变量以添加所需的输入变量。输入变量有两种类型:引用型和文本型。

  • 引用:使用组件的输出或用户输入作为数据源。需要从下拉菜单中选择:
    • 被引用的组件 ID,或
    • 位于 开始组件 的全局变量,该变量定义于 开始 组件中。
  • 文本:使用固定文本作为查询。需输入静态文本。

模型

点击 Model 的下拉菜单以显示模型配置窗口。

  • 模型:指定使用的聊天模型。

    • 确保在模型提供商页面正确设置聊天模型。
    • 您可以为不同组件采用不同模型,以增强灵活性或提升整体性能。
  • 自由度:快速调节温度值、Top P、存在惩罚及频率惩罚的快捷设置,反映模型的响应自由度层级。从即兴发挥、精确控制到平衡模式,每个预设配置均对应温度值、Top P、存在惩罚与频率惩罚的独特组合。

    该参数提供三种选项:

    • 即兴模式:生成更具创造性的回答。
    • 精确模式:(默认)生成更为保守的响应。
    • 平衡:介于即兴与精确之间的折中方案。
  • 温度值:控制模型输出随机性的参数。
    默认为 0.1。

    • 较低的值会导致输出更加确定和可预测。
    • 较高的值会带来更具创造性和多样化的输出。
    • 温度值为零时,相同的提示将产生相同的输出。
  • Top P:核心采样。
    
    • 通过设定阈值 P 并限制采样至累积概率超过 P 的令牌,降低生成重复或不自然文本的可能性。
    • 默认值为 0.3。
      
  • 存在处罚:鼓励模型在响应中包含更多样化的令牌。

    • 较高的存在惩罚值会使模型更倾向于生成尚未出现在已生成文本中的令牌。
    • 默认值为 0.4。
      
  • 频率惩罚:用于阻止模型在生成文本中过于频繁地重复相同的单词或短语。

    • 较高的频率惩罚值会使模型在使用重复标记时更为保守。
    • 默认值为 0.7。
      

    注意

  • 并非所有组件都必须使用同一模型。若某模型在特定任务上表现不佳,可考虑更换其他模型。

  • 若你不清楚温度参数、Top P、存在处罚及频率惩罚的机制,只需从三种预设配置选项中任选其一即可。

消息窗口大小

整数,指定输入到LLM的先前对话轮次数。例如,若设为 12,则最近 12 轮对话的令牌将被送入LLM。此功能会消耗额外令牌。

默认为 1。

此功能仅用于多轮对话。如果分类组件不属于多轮对话(即不在循环中),请保持此字段不变。

分类名称

分类组件必须至少包含两个类别。此字段用于设置类别的名称。点击“+添加项目”以包含所需的类别。

描述

此分类的描述。

可以输入有助于LLM判断哪些输入属于此类的标准、情境或信息。

下游组件

指定此分类的下游组件。

  • 一旦指定了下游组件的 ID,该分类与对应组件之间即建立链接。
  • 若在画布上手动将此分类链接至下游组件,该组件的 ID 将自动填充。

(6)关键词组件

从用户查询中提取关键词。

关键词组件使用指定的LLM从用户查询中提取关键词。

场景

在需要为潜在关键词搜索准备关键词时,关键词组件至关重要。

配置项

输入

关键词组件依赖输入变量来指定其数据输入(查询)。在输入部分点击+添加变量以添加所需的输入变量。输入变量有两种类型:引用型和文本型。

  • 引用:使用组件的输出或用户输入作为数据源。您需要从下拉菜单中选择:
    • 组件输出下的组件 ID,或
    • 位于开始组件的全局变量。
  • 文本:使用固定文本作为查询。需输入静态文本。

模型

点击 Model 的下拉菜单以显示模型配置窗口。

  • 模型:指定使用的聊天模型。

    • 确保在模型提供商页面正确设置聊天模型。
    • 可以为不同组件采用不同模型,以增强灵活性或提升整体性能。
  • 自由度:快速调节温度值、Top P、存在惩罚及频率惩罚的快捷设置,反映模型的响应自由度层级。从即兴发挥、精确控制到平衡模式,每个预设配置均对应温度值、Top P、存在惩罚与频率惩罚的独特组合。

    该参数提供三种选项:

    • 即兴模式:生成更具创造性的回答。
    • 精确模式:(默认)生成更为保守的响应。
    • 平衡模式:介于即兴与精确之间的折中方案。
  • 温度值:控制模型输出随机性的参数。
    默认为 0.1。

    • 较低的值会导致输出更加确定和可预测。
    • 较高的值会带来更具创造性和多样化的输出。
    • 温度值为零时,相同的提示将产生相同的输出。
  • Top P:核心采样。
    
    • 通过设定阈值 P 并限制采样至累积概率超过 P 的令牌,降低生成重复或不自然文本的可能性。
    • 默认值为 0.3。
      
  • 存在处罚:鼓励模型在响应中包含更多样化的令牌。

    • 较高的存在惩罚值会使模型更倾向于生成尚未出现在已生成文本中的令牌。
    • 默认值为 0.4。
      
  • 频率惩罚:用于阻止模型在生成文本中过于频繁地重复相同的单词或短语。

    • 较高的频率惩罚值会使模型在使用重复标记时更为保守。
    • 默认值为 0.7。
      

注意

  • 并非所有组件都必须使用同一模型。若某模型在特定任务上表现不佳,可考虑更换其他模型。
  • 若你不清楚温度值、Top P、存在惩罚及频率惩罚的机制,只需从预设的三个选项中选择其一即可。

关键词数量

整数,用于指定从用户查询中提取的关键词数量,默认为 3。请注意,实际提取的关键词数量取决于LLM的功能及用户查询的 token 计数,可能与设定的整数不符。

(7)消息组件

发送静态消息。

消息组件用于发送静态消息。若提供多条消息,则随机选择一条发送。

配置项

消息列表

待发送的消息。

点击“+添加消息”以增加消息选项。当提供多条消息时,消息组件会随机选择一条发送。

(8)重写组件

用于重写用户查询。

重写组件利用指定的LLM,基于先前对话的上下文,对来自交互组件的用户查询进行重写。

场景

当需要根据先前对话的上下文优化用户查询时,重写组件至关重要。它通常是检索组件的上游组件。

另请参阅关键词组件,这是一个用于多轮优化的类似组件。

配置项

重写组件以交互组件中的用户代理交互作为数据输入,因此无需在配置中另行指定其数据输入源。

模型

点击 Model 的下拉菜单以显示模型配置窗口。

  • 模型:指定使用的聊天模型。

    • 确保在模型提供商页面正确设置聊天模型。
    • 您可以为不同组件采用不同模型,以增强灵活性或提升整体性能。
  • 自由度:快速调节温度值、Top P、存在处罚及频率惩罚的快捷设置,反映模型的响应自由度层级。从即兴发挥、精确控制到平衡模式,每个预设配置均对应温度值、Top P、存在处罚与频率惩罚的独特组合。

    该参数提供三种选项:

    • 即兴模式:生成更具创造性的回答。
    • 精确模式:(默认)生成更为保守的响应。
    • 平衡:介于即兴与精确之间的折中方案。
  • 温度值:控制模型输出随机性的参数。
    默认为 0.1。

    • 较低的值会导致输出更加确定和可预测。
    • 较高的值会带来更具创造性和多样化的输出。
    • 温度值为零时,相同的提示将产生相同的输出。
  • Top P:核心采样。
    
    • 通过设定阈值 P 并限制采样至累积概率超过 P 的令牌,降低生成重复或不自然文本的可能性。
    • 默认值为 0.3。
      
  • 存在处罚:鼓励模型在响应中包含更多样化的令牌。

    • 较高的存在惩罚值会使模型更倾向于生成尚未出现在已生成文本中的令牌。
    • 默认值为 0.4。
      
  • 频率惩罚:用于阻止模型在生成文本中过于频繁地重复相同的单词或短语。

    • 较高的频率惩罚值会使模型在使用重复标记时更为保守。
    • 默认值为 0.7。
      

注意

  • 并非所有组件都必须使用同一模型。若某模型在特定任务上表现不佳,可考虑更换其他模型。
  • 若你不清楚温度参数、Top P、存在处罚及频率惩罚的机制,只需从三种预设配置选项中任选其一即可。

消息窗口大小

整数,指定输入到LLM的先前对话轮次数。例如,若设为 12,则最近 12 轮对话的令牌将被送入LLM。此功能会消耗额外令牌。

默认为 1。

(9)条件组件

评估指定条件是否满足并相应引导执行流程。

条件组件基于特定组件的输出评估条件,相应引导执行流程以实现复杂的分支逻辑。

场景

条件组件对于基于条件的执行流导向至关重要。虽然它与同样用于多分支策略的分类组件有相似之处,但关键区别在于其方法:开关组件的评估基于规则,而分类组件则涉及人工智能并使用LLM进行决策。

配置项

Case n

一个 条件组件必须至少包含一个Case,每个Case需指定多个条件且仅有一个下游组件。当为Case指定多个条件时,必须设置这些条件间的逻辑关系为 AND 或 OR。

下一步

指定此Case的下游组件。

  • 一旦指定了下游组件的 ID,便会在该Case与对应组件间建立链接。
  • 若手动将此Case关联至画布上的下游组件,该组件的 ID 将自动填充。

Condition

评估特定组件的输出是否满足某些条件,其中组件 ID、运算符和值共同构成一个条件表达式。

当为特定Case添加了多个条件时,将出现逻辑运算符字段,要求将这些条件间的逻辑关系设置为“与”或“或”。

工作流组件-条件组件

  • 组件 ID:对应组件的唯一标识符。
  • 操作符:构成条件表达式所需的操作符。
    • 等于
      
    • 不等于
      
    • 大于
      
    • 大于等于
      
    • 小于
      
    • 小于等于
      
    • 包含
      
    • 不包含
      
    • 以...开头
      
    • 以...结尾
      
    • 为空
      
    • 非空
      
  • 值:单一值,可以是整数、浮点数或字符串。
    • 不支持分隔符、多个值或表达式。
    • 字符串无需用 ""'' 包裹。

ELSE

必填。指定当上述条件均未满足时的下游组件。

一旦指定下游组件的 ID,便会在“ELSE”与对应组件间建立链接。

(10)集线器组件

将流程导向多个下游组件。

集线器组件充当执行流的“中继器”,将工作流传输至多个下游组件。

场景

集线器组件优化了当前用户体验设计。对于原本仅支持单一下游组件的设计,通过添加集线器,可实现多下游组件的支持。

(11)模板转换组件

用于格式化用户输入或其他组件输出。

模板组件充当内容格式化器,通常是交互组件的上游组件。

该组件用于排版各种组件的输出。

1、支持Jinja2模板,会先将输入转为对象后进行模版渲染。

2、同时保留原使用{参数}字符串替换的方式。

场景

模板转换组件有助于将不同来源的数据或信息组织成特定格式。

配置项

内容

与Key配合使用,将各类数据或信息源组织成所需格式。示例:

    <h2>{subtitle}</h2><div>{content}</div>

其中 {subtitle}{content} 为已定义的Key。

Key

模板转换组件依赖Key(变量)来指定其数据或信息源。其直接上游组件不一定是其输入,工作流中的箭头仅表示处理顺序。

Key分为两类:

  • 组件输出:键的值应为组件 ID。
  • Key的值应为在 开始 组件中定义的全局变量的名称。

(12)循环组件

循环组件首先将输入以“分隔符”分割成数组,然后依次对数组中的元素执行相同的操作步骤,直到输出所有结果,可以理解为一个任务批处理器。例如在长文本翻译迭代节点中,如果所有内容都输入到LLM节点,可能会达到单次对话的限制,上游节点可以先将长文本分割成多个片段,配合迭代节点对每个片段进行批量翻译,避免达到单次对话的LLM消息限制。
将文本输入分割为多个文本段并对每个段执行预定义工作流的组件。

场景

当需要工作流循环且循环次数不固定,而是依赖于特定代理组件输出生成的段数时,迭代组件至关重要。

  • 示例1:若计划将多个段落输入LLM进行内容生成,每个段落各有侧重点,而一次性输入LLM可能导致混淆或矛盾,此时可使用封装了生成组件的循环组件,逐段重复内容生成流程。
  • 示例2:若需利用LLM将长篇论文翻译为目标语言,同时避免超出其令牌限制,可考虑采用封装生成组件的迭代组件,将论文拆分为小片段并逐段执行翻译过程。

内部组件

迭代项

每个循环组件内部包含一个迭代项组件。迭代项组件既是迭代组件内工作流的起点,也是输入节点,负责管理由输入生成的所有文本段的工作流循环。

迭代项组件仅对当前迭代组件封装的内部组件可见。

工作流组件-循环组件

构建内部工作流

允许将其他组件拖入循环组件内以构建内部工作流,这些“新增的内部组件”对当前循环组件外部的组件不再可见。

若需从添加的内部组件引用已创建的文本片段,只需在该内部组件的输入部分添加一个等于迭代项组件的引用变量。无需引用对应的外部组件,因为迭代项组件负责管理工作流中所有已创建文本片段的循环。

在必要时,添加的内部组件可以引用外部组件。

配置项

输入

循环组件通过输入变量指定其数据输入,即待分段的文本。允许为循环组件指定多个输入源。点击输入部分的+添加变量以包含所需的输入变量。输入变量有两种类型:引用和文本。

  • 引用:使用组件的输出或用户输入作为数据源。您需要从下拉菜单中选择:
    • 输出组件的组件 ID,或
    • 位于 开始组件 输入的全局变量,该变量定义于 开始 组件中。
  • 文本:使用固定文本作为查询。需输入静态文本。

文本分段标识符

用于将文本输入分割成段的分隔符:

  • 逗号(默认)
    
  • 换行符
    
  • 制表符
    
  • 下划线
    
  • 正斜杠
    
  • 短横线
    
  • 分号
    

十四、RAGFlow 核心模块及其业务逻辑

以下是RAGFlow的代码结构。

ragflow
├── api/ (后端API接口代码)
│   ├── apps/ (各个应用模块的API代码)
│   ├── utils/ (API相关的工具函数)
│   └── db/ (数据库相关的代码或配置)
├── web/ (Web前端代码目录)
│   ├── src/ (主要的源代码目录)
│   ├── public/ (公共静态资源)
│   └── .husky/ (Git Hooks配置)
├── mcp/ (可能与多集群管道或管理控制平台有关)
│   ├── client/ (客户端代码)
│   └── server/ (服务端代码)
├── rag/ (检索增强生成相关代码)
│   ├── utils/ (RAG 相关的工具函数)
│   ├── app/ (RAG 应用层代码)
│   ├── nlp/ (自然语言处理相关代码)
│   ├── svr/ (服务或SVR相关代码)
│   ├── res/ (资源文件)
│   └── llm/ (大型语言模型相关代码)
├── graphrag/ (与图谱和检索增强生成相关的代码)
│   ├── general/ (通用图谱RAG功能)
│   └── light/ (轻量级图谱RAG功能)
├── helm/ (Kubernetes Helm Charts配置)
│   └── templates/ (Kubernetes 资源模板文件)
├── docs/ (项目文档)
│   ├── references/ (参考资料或API文档)
│   ├── guides/ (使用指南)
│   └── develop/ (开发者文档)
├── docker/ (Docker相关配置文件)
│   └── nginx/ (Nginx 配置文件)
├── conf/ (项目配置文件)
├── sdk/ (软件开发工具包)
│   └── python/ (Python SDK)
├── intergrations/ (应为 integrations, 集成相关代码)
│   ├── extension_chrome/ (Chrome 浏览器插件集成)
│   └── chatgpt-on-wechat/ (与微信集成ChatGPT相关的代码)
├── example/ (示例代码或用例)
│   ├── http/ (HTTP API 调用示例)
│   └── sdk/ (SDK 使用示例)
├── deepdoc/ (可能与深度文档处理相关)
│   ├── parser/ (文档解析器代码)
│   └── vision/ (视觉/图像处理相关代码)
├── agentic_reasoning/ (可能与代理或推理逻辑相关)
├── agent/ (代理相关代码)
│   ├── component/ (代理的组件)
│   ├── test/ (测试代码)
│   └── templates/ (模板文件)
├── uv.lock (uv包管理器锁文件)
├── pyproject.toml (Python项目配置文件)
├── Dockerfile (主Docker镜像构建文件)

下面详细分析 RAGFlow 各主要模块的核心逻辑、功能以及它们是如何利用相关技术栈来实现的。

1. API 模块 (api/)

  • 核心功能 (Core Functionality):
    作为整个 RAGFlow 应用的后端服务入口,负责接收和处理来自 Web 前端的 HTTP 请求,协调内部的各个业务模块(如 RAG、DeepDoc、Agent),管理系统配置、用户、知识库等核心数据,并向前端返回处理结果。

  • 核心逻辑与技术栈实现 (Core Logic & Tech Stack Implementation):

    1. Web 服务框架:
      • 逻辑: 定义 API 路由(Endpoints),接收 HTTP 请求(GET, POST, PUT, DELETE 等),解析请求参数(路径参数、查询参数、请求体),调用相应的业务处理逻辑。
      • 技术栈: 使用 Python 语言和 Flask Web 框架(以及其依赖 Werkzeug)来构建。Flask 通过装饰器 (@app.route('/some/path', methods=['POST'])) 定义 API 路由,并通过 request 对象访问传入的数据。它负责将原始的 HTTP 请求转换为 Python 函数调用。
    2. 业务逻辑处理:
      • 逻辑: 根据 API 请求的类型,执行具体的业务操作。例如,处理用户登录请求、创建知识库、上传文件、发起 RAG 对话、执行 Agent 任务等。这部分逻辑会调用其他模块或服务。
      • 技术栈: 核心业务逻辑使用 Python 编写。它会调用 RAG 模块的函数进行问答,调用 DeepDoc 模块进行文档解析,调用 Agent 模块执行工作流等。
    3. 数据库交互:
      • 逻辑: 将系统配置、用户信息、知识库元数据、对话历史等持久化数据存储到数据库中,并根据需要进行增删改查。
      • 技术栈: 使用 MySQL 8.x 作为关系型数据库。API 服务通过 Python 的数据库驱动(如 mysql-connector-python)或更常用的 ORM 库(如 SQLAlchemyFlask-SQLAlchemy,虽然未明确列出,但在此类应用中非常常见)来与 MySQL 交互,执行 SQL 语句或通过 ORM 对象操作数据。
    4. 缓存与会话管理:
      • 逻辑: 使用缓存存储热点数据(如频繁访问的配置、用户信息)以提高性能。管理用户登录状态(会话)。可能用作简单的消息队列。
      • 技术栈: 使用 Redis/Valkey。API 服务通过 Redis 的 Python 客户端库(如 redis-py)连接 Redis,执行 SET, GET, DELETE 等缓存操作。用户会话信息(Session ID)可能存储在 Redis 中。Flask-Login 库用于简化用户认证和会话管理流程。
    5. 对象存储交互:
      • 逻辑: 处理文件上传请求时,将文件存储到对象存储;需要访问原始文件时,从对象存储读取。
      • 技术栈: 使用 MinIO。API 服务通过 MinIO 的 S3 兼容 Python 客户端库(如 boto3minio-py)与 MinIO 服务器交互,执行上传 (put_object)、下载 (get_object) 等操作。
    6. 安全与认证:
      • 逻辑: 验证用户身份,管理 API 密钥,确保只有授权用户才能访问特定资源。
      • 技术栈: 使用 Flask-Login 处理用户登录和会话管理。API 密钥的验证逻辑通常在 API 请求处理的早期阶段(例如通过 Flask 的 before_request 钩子或装饰器)进行。
    7. 异步任务处理 (通过 Executor):
      • 逻辑: 对于耗时的操作(如大型文档解析、复杂的 Agent 任务),API 可能不会立即执行,而是将任务发送到消息队列(如 Redis),由后台的 ragflow-executor 容器异步处理。API 只负责接收任务、放入队列并可能返回一个任务 ID。
      • 技术栈: 涉及 Redis (作为消息队列) 和可能的任务队列库 (如 Celery,虽然未明确列出,但很常用)。

2. Web 前端模块 (web/)

  • 核心功能 (Core Functionality):
    提供用户交互的操作界面 (UI),让用户能够方便地使用 RAGFlow 的各项功能,如管理知识库、上传文档、进行对话问答、设计和运行 Agent 工作流等。负责展示后端返回的数据,并将用户的操作转化为对后端 API 的调用。

  • 核心逻辑与技术栈实现 (Core Logic & Tech Stack Implementation):

    1. UI 构建与渲染:
      • 逻辑: 使用组件化的方式构建用户界面,根据应用状态动态渲染页面内容。
      • 技术栈: React.js 作为核心 UI 库,用于创建可复用的界面组件(如按钮、输入框、列表、图表、Agent 画布节点等)。UmiJS 作为企业级框架,提供了路由管理、构建优化、插件化能力,简化了 React 应用的开发。代码使用 TypeScript 编写,提供静态类型检查,增强代码健壮性。
    2. 界面样式与布局:
      • 逻辑: 实现美观、一致、响应式的用户界面布局和视觉风格。
      • 技术栈: Ant Design (antd) 提供了一套丰富、高质量的企业级 UI 组件,可以直接使用。Tailwind CSS 作为一个实用优先的 CSS 框架,用于快速构建自定义样式和布局,与 Ant Design 结合使用。Radix UI 可能用于提供一些底层的、无样式的 UI 原语,以构建更可定制的组件。
    3. 状态管理:
      • 逻辑: 管理前端应用的状态,包括用户界面状态(如某个下拉框是否打开)、服务端缓存数据、用户登录信息等。
      • 技术栈: Zustand 是一个轻量级的状态管理库,用于管理全局或特定模块的状态(例如,当前选择的知识库 ID,Agent 画布的状态)。React Query (Tanstack Query) 专注于服务端状态管理,负责从后端 API 获取数据、缓存数据、处理加载和错误状态、数据同步等(例如,获取知识库列表、聊天记录)。Immer 用于简化对不可变状态的更新操作。
    4. 与后端 API 通信:
      • 逻辑: 向后端 API 服务发送 HTTP 请求(例如,获取数据、提交表单、触发操作),并处理响应。
      • 技术栈: 通常使用 axios 库或者 UmiJS 框架自带的请求库 (umi-request 或封装) 来发送异步 HTTP 请求到 api/ 模块暴露的端点。根据 API 返回的数据更新前端状态和 UI。
    5. 数据可视化:
      • 逻辑: 将数据显示为图表或图形,例如展示知识库统计信息、Agent 工作流的可视化编排。
      • 技术栈: XYFlow (React Flow) 用于构建和渲染 Agent 的图形化工作流画布,支持节点拖拽、连接等交互。G2/G6 (AntV)Recharts 可能用于在界面的其他地方展示统计图表。
    6. 国际化:
      • 逻辑: 支持界面显示多种语言。
      • 技术栈: i18nextreact-i18next 用于管理和加载不同语言的文本资源,根据用户设置或浏览器偏好切换界面语言。
    7. 开发与构建:
      • 逻辑: 代码质量检查、格式化、测试、Git 工作流集成、项目构建打包。
      • 技术栈: ESLintPrettier 保证代码风格和质量。Jest 用于单元测试和集成测试。Husky 用于在 Git 提交前自动运行检查或格式化。UmiJS 负责整个前端项目的开发服务器、构建和打包过程。

3. RAG 模块 (rag/)

  • 核心功能 (Core Functionality):
    实现检索增强生成 (Retrieval-Augmented Generation) 的核心工作流。负责将文档数据转化为可检索的知识,并在用户提问时,根据问题检索最相关的信息,然后结合这些信息调用大型语言模型 (LLM) 生成精准、可靠的答案。

  • 核心逻辑与技术栈实现 (Core Logic & Tech Stack Implementation):

    1. 文档处理与分块 (Chunking):
      • 逻辑: 接收来自 DeepDoc 模块解析后的结构化文档内容(文本块、表格等),并根据配置的策略(如固定大小、按语义、按标题等)将文档切分成更小的、适合进行向量化和检索的块 (Chunks)。
      • 技术栈: 使用 Python 编写分块逻辑。可能会利用 DeepDoc 输出的结构信息(如标题、段落边界)进行智能分块。自定义模板分块功能允许用户根据特定格式定义分块规则。
    2. 文本向量化 (Embedding):
      • 逻辑: 将文本块转换为高维向量表示,使得语义相似的文本在向量空间中距离相近。
      • 技术栈: 使用 Python 调用嵌入模型库。FastEmbedFlagEmbedding 提供了高效的、本地运行的嵌入模型选项。也支持集成第三方嵌入模型 API(如 OpenAI Embeddings API)。根据选择的模型,调用其编码函数处理文本块。如果使用本地模型,PyTorch (作为底层框架) 和 ONNX Runtime (用于优化推理) 可能被间接使用。HuggingFace Transformers 库可能是加载和使用这些嵌入模型的基础。
    3. 向量存储与索引:
      • 逻辑: 将生成的文本向量及其对应的原始文本块、元数据(如来源文档、页码)存储到专门的数据库中,并构建索引以支持快速相似性搜索。
      • 技术栈: InfiniFlow 是一个专门的向量数据库,提供高性能的向量存储和检索能力。RAG 模块通过 Python 客户端库连接 InfiniFlow,将向量和元数据写入数据库。Elasticsearch 也可以配置为向量存储和检索引擎,或者用于结合关键词的混合搜索。在某些配置下,也可能使用 Faiss、ChromaDB 等其他向量存储方案。原始文档或较大的数据块可能存储在 MinIO 中,数据库中只存储引用。
    4. 信息检索 (Retrieval):
      • 逻辑: 当用户提问时,首先将用户问题也转换为向量,然后在向量数据库中搜索与问题向量最相似的文本块向量,召回最相关的 K 个文本块作为上下文 (Context)。可能包含重排序 (Re-ranking) 逻辑以优化召回结果的相关性。
      • 技术栈: 使用 Python 调用向量数据库的客户端库执行相似性搜索(通常是 Approximate Nearest Neighbor, ANN 搜索)。例如,向 InfiniFlowElasticsearch 发送查询向量,获取相似向量及其关联的文本块。重排序可能使用更轻量级的交叉编码器模型 (Cross-encoder) 或其他算法。
    5. Prompt 构建:
      • 逻辑: 将检索到的上下文信息 (Context) 和用户的原始问题 (Question) 按照预先设计的模板组合成一个完整的提示 (Prompt),用于输入给大型语言模型。可能还会包含对话历史以支持多轮问答。
      • 技术栈: 使用 Python 进行字符串格式化或使用模板引擎(如 Jinja2)来构建 Prompt。Prompt 模板的设计 (rag/prompts.py 或数据库存储) 对生成答案的质量至关重要。
    6. 大型语言模型 (LLM) 推理:
      • 逻辑: 将构建好的 Prompt 发送给选择的大型语言模型,获取模型生成的答案。
      • 技术栈: 通过 Python 调用相应的 LLM API 客户端库(如 openai, anthropic 等库)与托管的 LLM 服务交互。或者,如果配置了本地 LLM,则通过 HuggingFace Transformers 或类似库加载模型并执行推理(可能需要 PyTorchONNX Runtime 支持)。
    7. 知识图谱增强 (Optional):
      • 逻辑: 在 RAG 流程中融入知识图谱,例如,通过识别问题中的实体,查询图谱获取相关实体和关系,将这些信息补充到上下文中,或者用于验证/丰富 LLM 的答案。
      • 技术栈: 可能涉及实体识别库 (如 spaCy, NLTK 或基于 Transformers 的 NER 模型),以及与图数据库 (如 Neo4j, Nebula Graph,虽然未明确列出) 或图计算库交互的 Python 代码。PageRank 算法可用于评估节点重要性。

4. DeepDoc 模块 (deepdoc/)

  • 核心功能 (Core Functionality):
    负责对上传的各种格式(PDF, Word, PPT, 图片等)的文档进行深度解析和理解。它的目标是超越简单的文本提取,尽可能地理解文档的结构(如标题层级、段落、列表、表格、图片),并提取出高质量、结构化的内容,为后续的 RAG 处理提供最佳输入。

  • 核心逻辑与技术栈实现 (Core Logic & Tech Stack Implementation):

    1. 多格式文档解析:
      • 逻辑: 识别文档类型,并调用相应的库来解析文件内容。
      • 技术栈: 使用 Python 作为基础。
        • PDF: PyPDFpdfplumberPyMuPDF (fitz) 用于打开 PDF 文件,读取文本、元数据、图片和潜在的表格结构。PyMuPDF 通常功能最全,性能较好。
        • Word: python-docx 用于解析 .docx 文件,访问文本、表格、图片、样式等。
        • PowerPoint: python-pptx 用于解析 .pptx 文件。
        • 其他格式: 可能使用 Tika 或其他专用库处理 HTML、TXT 等格式。
    2. 光学字符识别 (OCR):
      • 逻辑: 对于扫描版 PDF 或文档中的图片,识别其中的文字内容。
      • 技术栈: 调用 OCR 引擎。可能是开源的 Tesseract (通过 pytesseract 包装器)、PaddleOCR,或者集成了云服务商(如 Google Vision AI, Azure Cognitive Services)的 OCR API。OpenCVPillow (PIL) 用于在 OCR 前对图像进行预处理(如调整大小、灰度化、去噪)以提高识别准确率。
    3. 版面分析 (Layout Analysis):
      • 逻辑: 分析文档页面元素的空间布局,识别不同区域的功能(如页眉、页脚、标题、段落、图片、表格、列表),理解文档的视觉结构。这是 DeepDoc 的“深度”所在。
      • 技术栈: 这部分技术复杂度较高。可能采用:
        • 基于规则的方法: 根据坐标、字体大小、样式等启发式规则判断元素类型。
        • 计算机视觉 (CV) 方法: 使用 OpenCV 进行图像处理和特征提取。
        • 深度学习模型: 可能使用了预训练的版面分析模型(如 LayoutLM, DiT, Table Transformer 等,可能通过 HuggingFace TransformersDetectron2 等框架加载)来识别页面元素及其类型和边界框。
    4. 结构化信息提取:
      • 逻辑: 结合版面分析结果,将提取的文本、表格数据、图片等内容组织成结构化的表示。例如,区分标题和正文,提取表格的行列数据,将图片与相邻文本关联。
      • 技术栈: 基于版面分析的输出,使用 Python 逻辑进行信息整合和结构化。例如,识别出表格区域后,调用专门的表格提取算法或库。
    5. 输出:
      • 逻辑: 将解析和分析的结果输出为统一的格式(通常是 JSON 或 Python 对象列表),包含内容块(文本、表格、图片)、类型、内容本身、元数据(页码、字体信息)以及重要的位置信息(边界框 Bbox)。
      • 技术栈: 使用 Python 的数据结构(字典、列表)来组织输出。

5. Agent 模块 (agent/ & agentic_reasoning/)

  • 核心功能 (Core Functionality):
    提供一个自主研发的、基于图形化工作流的智能体 (Agent) 框架。允许用户通过拖拽组件和连接的方式设计、编排和执行能够完成复杂任务的 Agent。这些 Agent 可以根据目标自主规划、调用工具、与 LLM 交互并最终达成目标。

  • 核心逻辑与技术栈实现 (Core Logic & Tech Stack Implementation):

    1. 图形化工作流定义 (DSL):
      • 逻辑: 用户在前端(web/ 模块)通过拖拽界面设计 Agent 的工作流程(哪些组件按什么顺序执行,数据如何传递)。前端将这个图形化表示转换为一种基于图结构的领域特定语言 (DSL),通常是 JSON 格式。
      • 技术栈: 前端使用 XYFlow 等库实现图形化编辑。后端 Agent 模块定义并解析这种 JSON DSL。DSL 描述了图的节点(组件实例及其参数)和边(组件间的连接关系,表示数据流向)。
    2. 组件化架构:
      • 逻辑: 将 Agent 的能力封装成一系列可复用的组件 (Components)。每个组件执行一个特定的原子任务(如开始、结束、检索、生成、调用搜索引擎、执行 SQL)。
      • 技术栈: 使用 Python 定义一个 ComponentBase 基类,所有具体组件都继承自它。基类定义了标准接口(如输入参数 InputVar, 输出参数 OutputVar, 执行方法 run())。项目提供了丰富的预定义组件(如 RetrievalComponent, GenerateComponent, GoogleSearchComponent, SqlExecutorComponent 等)。用户也可以通过继承 ComponentBase 来扩展自定义组件。
    3. 工作流执行引擎 (Canvas):
      • 逻辑: Canvas 类是 Agent 的核心执行引擎。它负责加载 DSL 定义的工作流图,根据图的连接关系(支持循环和条件分支,非严格 DAG)决定组件的执行顺序,管理组件间的数据传递和整个工作流的状态。
      • 技术栈: 使用 Python 实现 Canvas 类。它维护工作流的上下文(包括历史记录、消息、引用、当前变量值等),并根据 DSL 调度各个组件实例的 run 方法。
    4. 状态管理与数据流:
      • 逻辑: 组件的输出可以作为后续组件的输入。Canvas 负责管理这些在工作流中流转的数据(通常通过变量名引用)。
      • 技术栈: Python 字典或其他数据结构在 Canvas 实例中用于存储变量状态。DSL 中定义了变量如何在组件间传递。
    5. 工具调用与集成:
      • 逻辑: Agent 组件需要执行具体操作时,会调用 RAGFlow 内部的功能(如 RAG 检索)或外部工具/API。
      • 技术栈: 相关组件的 run 方法内包含调用其他模块(如 RAG 模块)或第三方库/API 的 Python 代码(例如,使用 requests 库调用 Web API,使用 sqlite3SQLAlchemy 执行 SQL,使用 Crawl4AIBeautifulSoup 进行网页抓取,调用搜索引擎 API)。
    6. LLM 交互 (规划与生成):
      • 逻辑: Agent 可能需要在执行过程中调用 LLM 进行规划(决定下一步做什么或调用哪个组件)或在特定组件(如 GenerateComponent)中生成文本内容。
      • 技术栈: 组件内部通过 Python API 客户端库调用 OpenAI, Anthropic 等 LLM 服务。Prompt 工程在这里同样重要,用于指导 LLM 进行规划或生成。
    7. 参数验证:
      • 逻辑: 在加载工作流或执行组件前,对用户配置的组件参数进行严格验证,确保类型、格式、取值范围等符合要求。
      • 技术栈: 可能使用了 Pydantic 或类似的 Python 库来定义 ComponentParamBase 的派生类,利用其类型注解和验证器进行参数校验。
    8. 流式响应:
      • 逻辑: 对于需要生成长文本的组件(如 GenerateComponent),支持将结果以流式的方式逐步返回给前端。
      • 技术栈:Python 中使用生成器 (yield) 来实现。组件的 run 方法不一次性返回所有结果,而是逐步 yield 出内容片段。

十五、RAGFlow 核心代码及其业务流程

为深入理解 RAGFlow 各主要模块的实现,本节结合核心代码示例,帮助你更深入地理解 RAGFlow 各个模块的内部工作原理。

由于RAGFlow代码量巨大,以下是 简化示例,旨在说明核心逻辑,可能与实际源码存在差异。


1. API 模块 (api/)

  • 核心功能: 作为后端入口,处理 HTTP 请求,协调内部服务,管理数据,并返回响应。

  • 核心代码示例 (基于 Flask): 假设在 api/apps/kb_api.py 中定义知识库相关 API。

    # Simplified example from api/apps/kb_api.py (using Flask conventions)
    from flask import Blueprint, request, jsonify
    # from api.services import knowledge_base_service # 假设的业务逻辑服务
    # from api.auth import token_required # 假设的认证装饰器
    from werkzeug.exceptions import BadRequest
    
    # 创建一个 Flask 蓝图 (Blueprint) 来组织知识库相关的路由
    kb_bp = Blueprint('knowledge_base', __name__, url_prefix='/v1/kb')
    
    # 示例:创建知识库的 API 端点
    @kb_bp.route('/create', methods=['POST'])
    # @token_required # 应用认证装饰器
    def create_knowledge_base():
        """
        接收创建知识库的 POST 请求。
        请求体应包含 JSON 数据,例如: {"name": "MyNewKB", "description": "...", "embedding_model": "..."}
        """
        try:
            # 1. 解析请求体中的 JSON 数据
            data = request.get_json()
            if not data or 'name' not in data:
                raise BadRequest("Missing 'name' in request body")
    
            kb_name = data.get('name')
            description = data.get('description', '')
            embedding_model = data.get('embedding_model', 'default_model')
            user_id = request.user.id # 假设认证后用户信息在 request.user 上
    
            # 2. 调用业务逻辑层 (Service Layer) 处理创建操作
            # new_kb = knowledge_base_service.create_kb(
            #     user_id=user_id,
            #     name=kb_name,
            #     description=description,
            #     embedding_model=embedding_model
            # )
    
            # 模拟成功创建后的返回数据
            new_kb_id = "kb-" + kb_name # 模拟生成 KB ID
            print(f"Simulating KB creation: ID={new_kb_id}, Name={kb_name}") # 打印日志或实际调用 Service
            new_kb = {"id": new_kb_id, "name": kb_name, "embedding_model": embedding_model}
    
    
            # 3. 返回成功的 JSON 响应
            return jsonify({
                "success": True,
                "message": "Knowledge base created successfully.",
                "data": new_kb # 返回新创建的知识库信息
            }), 201 # HTTP 状态码 201 Created
    
        except BadRequest as e:
            # 处理客户端请求错误 (例如,缺少参数)
            return jsonify({"success": False, "message": str(e)}), 400
        except Exception as e:
            # 处理其他内部服务器错误
            # log.error(f"Error creating knowledge base: {e}", exc_info=True) # 记录详细错误日志
            print(f"Error creating knowledge base: {e}")
            return jsonify({"success": False, "message": "Internal server error."}), 500
    
    # --- 在主应用 (api/ragflow_server.py 或类似文件) 中注册蓝图 ---
    # from flask import Flask
    # from api.apps.kb_api import kb_bp
    # app = Flask(__name__)
    # app.register_blueprint(kb_bp)
    # # ... 其他配置和蓝图注册 ...
    # if __name__ == '__main__':
    #     app.run(host='0.0.0.0', port=9380)
    
    
  • 代码解释:
    这段代码展示了使用 Flask 框架定义一个 API 端点 (/v1/kb/create) 的典型模式。

    1. 路由定义: 使用 @kb_bp.route('/create', methods=['POST']) 装饰器将 create_knowledge_base 函数与指定的 URL 路径和 HTTP 方法(POST)绑定。Blueprint 用于模块化地组织路由。
    2. 请求处理: 函数内部首先通过 request.get_json() 获取并解析 POST 请求体中的 JSON 数据。进行基本的输入验证(如检查 name 字段是否存在)。
    3. 业务调用: 注释掉的部分 (knowledge_base_service.create_kb(...)) 代表了实际调用业务逻辑层的地方,将解析后的数据传递给 Service 函数处理数据库操作等。这里用打印和模拟数据代替。
    4. 响应生成: 使用 jsonify() 将 Python 字典转换为 JSON 格式的响应体返回给客户端。根据处理结果(成功或失败),设置不同的 success 标志、消息和 HTTP 状态码(成功是 201 Created,客户端错误是 400 Bad Request,服务器内部错误是 500 Internal Server Error)。
    5. 错误处理: 使用 try...except 捕获不同类型的异常,并返回相应的错误信息给客户端。
    6. 蓝图注册: 最后需要在主应用中通过 app.register_blueprint(kb_bp) 将这个蓝图注册进去,API 才能生效。

    对话请求流程

    此图展示了当用户在 Web 界面发起一次 RAG 对话时,API 模块如何处理该请求。

    用户 Web 前端 (React/UmiJS) API 服务 (Flask) 认证中间件 RAG 模块 Redis/Valkey MySQL 输入问题并发送 发起 POST /api/v1/chat/completion 请求 (含问题, 会话ID) 验证用户 Token/Session 验证通过 (或失败) 查找或创建会话状态 (可选, 可能从 DB/Cache 获取) (可选) 获取会话历史 返回会话历史 调用 RAG 模块的 query 方法 (含问题, 历史, 知识库ID) 返回 LLM 生成的答案及引用 更新会话状态/历史 (可选) 缓存更新后的会话历史 确认缓存 (可选) 记录对话日志 确认记录 返回包含答案和引用的 JSON 响应 (HTTP 200 OK) 显示助手回答及引用 用户 Web 前端 (React/UmiJS) API 服务 (Flask) 认证中间件 RAG 模块 Redis/Valkey MySQL

    流程说明:

    1. 用户在前端界面输入问题。
    2. 前端将问题和会话信息通过 HTTP POST 请求发送给后端 API 服务。
    3. API 服务首先通过认证中间件验证用户身份。
    4. (可选) API 可能从 Redis 缓存或 MySQL 数据库中获取该会话之前的聊天记录。
    5. API 调用核心的 RAG 模块,传递用户问题、聊天历史以及目标知识库的标识。
    6. RAG 模块执行完整的 RAG 流程(检索->构建 Prompt->LLM 推理),并将结果返回给 API。
    7. (可选) API 更新 Redis 中的会话历史,并在 MySQL 中记录此次对话日志。
    8. API 将 RAG 模块返回的答案和引用信息封装成 JSON 格式,返回给前端。
    9. 前端解析响应并在界面上显示助手的回答。

2. Web 前端模块 (web/)

  • 核心功能: 提供用户界面,与用户交互,调用后端 API。

  • 核心代码示例 (React 组件): 假设在 web/src/pages/KnowledgeBase/List/index.tsx 中显示知识库列表。

    // Simplified example from web/src/pages/KnowledgeBase/List/index.tsx
    import React, 'react';
    import { Button, Table, message, Tag, Space } from 'antd';
    import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; // React Query for data fetching and caching
    // import { history } from 'umi'; // UmiJS routing
    import { callApi } from '@/utils/request'; // Assume a utility for API calls
    
    interface KnowledgeBase {
      id: string;
      name: string;
      description?: string;
      embedding_model: string;
      created_at: string;
    }
    
    const KnowledgeBaseList: React.FC = () => {
      const queryClient = useQueryClient(); // React Query client for cache invalidation
    
      // 1. 使用 React Query 获取知识库列表数据
      const { data: kbList, isLoading, error } = useQuery<KnowledgeBase[]>({
        queryKey: ['knowledgeBases'], // 缓存的 Key
        queryFn: async () => {
          // 异步函数,调用后端 API 获取数据
          const response = await callApi('/api/v1/kb/list'); // 假设的 API 端点
          if (response && response.success) {
            return response.data; // 返回知识库列表数组
          }
          throw new Error(response?.message || 'Failed to fetch knowledge bases');
        },
      });
    
      // 2. 使用 React Query 定义删除知识库的操作 (Mutation)
      const { mutate: deleteKb, isPending: isDeleting } = useMutation({
        mutationFn: async (kbId: string) => {
          // 异步函数,调用后端 API 执行删除操作
          const response = await callApi(`/api/v1/kb/delete/${kbId}`, { method: 'DELETE' });
          if (!response || !response.success) {
            throw new Error(response?.message || 'Failed to delete knowledge base');
          }
          return response;
        },
        onSuccess: () => {
          // 删除成功后执行
          message.success('Knowledge base deleted successfully.');
          // 使知识库列表的缓存失效,触发重新获取最新数据
          queryClient.invalidateQueries({ queryKey: ['knowledgeBases'] });
        },
        onError: (err) => {
          // 删除失败后执行
          message.error(err.message);
        },
      });
    
      // 3. 定义 Ant Design Table 的列配置
      const columns = [
        { title: 'Name', dataIndex: 'name', key: 'name' },
        { title: 'Embedding Model', dataIndex: 'embedding_model', key: 'embedding_model', render: (model: string) => <Tag>{model}</Tag> },
        { title: 'Created At', dataIndex: 'created_at', key: 'created_at' },
        {
          title: 'Action',
          key: 'action',
          render: (_: any, record: KnowledgeBase) => (
            <Space size="middle">
              <Button type="link" onClick={() => { /* history.push(`/kb/${record.id}`) */ }}>View</Button>
              <Button type="link" danger onClick={() => handleDelete(record.id)} loading={isDeleting}>
                Delete
              </Button>
            </Space>
          ),
        },
      ];
    
      // 处理删除按钮点击事件
      const handleDelete = (kbId: string) => {
        if (window.confirm('Are you sure you want to delete this knowledge base?')) {
          deleteKb(kbId); // 调用 useMutation 定义的删除函数
        }
      };
    
      if (error) {
        return <div>Error loading knowledge bases: {error.message}</div>;
      }
    
      // 4. 渲染 UI
      return (
        <div>
          <Button type="primary" style={{ marginBottom: 16 }} onClick={() => { /* history.push('/kb/create') */ }}>
            Create Knowledge Base
          </Button>
          <Table
            columns={columns}
            dataSource={kbList} // 将获取到的数据传给 Table
            loading={isLoading} // 控制表格加载状态
            rowKey="id" // 指定行的唯一 Key
          />
        </div>
      );
    };
    
    export default KnowledgeBaseList;
    
  • 代码解释:
    这个 React 组件使用 Ant DesignTable 来展示知识库列表。

    1. 数据获取 (useQuery): 使用 React QueryuseQuery Hook 来异步获取知识库列表数据。它负责调用 callApi('/api/v1/kb/list'),管理加载 (isLoading) 和错误 (error) 状态,并将获取的数据 (kbList) 缓存起来(由 queryKey: ['knowledgeBases'] 指定)。
    2. 数据操作 (useMutation): 使用 useMutation Hook 来定义删除知识库的操作。它封装了调用删除 API (callApi(...)) 的逻辑,并提供了 onSuccess(成功时显示提示信息并使列表缓存失效以刷新)和 onError(失败时显示错误信息)的回调。isPending 状态用于在删除过程中显示按钮加载效果。
    3. UI 渲染与交互: 定义 Table 的列 (columns),包括操作列中的“查看”和“删除”按钮。删除按钮的 onClick 事件调用 handleDelete 函数,该函数会弹出确认框,确认后调用 useMutation 返回的 deleteKb 函数来触发删除操作。
    4. 状态驱动 UI: TabledataSource 绑定到 useQuery 获取的 kbListloading 状态绑定到 isLoading,实现了数据驱动的界面更新。

    提交聊天消息处理流程

    此图展示了用户在前端界面输入消息并点击发送后,前端内部的处理流程。

    用户 浏览器 聊天输入组件 (AntD Input.Search) 聊天页面组件 (React Component) 状态管理 (Zustand/React Query) API 请求工具 (e.g., callApi) 后端 API 服务 在输入框输入文字 更新输入框值 调用 onChange 更新组件状态 (inputValue) 点击发送按钮 (或回车) 触发 onSearch 事件 调用 handleSendMessage 函数 更新消息列表状态 (添加用户消息) 状态更新完成 更新 UI, 显示用户消息, 设置加载状态 调用 callApi 发送请求到 BackendAPI 发送 POST /api/v1/chat/completion 返回 API 响应 (含助手回答) 返回处理后的数据或错误 更新消息列表状态 (添加助手消息) 状态更新完成 更新 UI, 显示助手消息, 清除加载状态 显示错误提示, 清除加载状态 alt [请求成功] [请求失败] (通过UI更新) 显示结果 用户 浏览器 聊天输入组件 (AntD Input.Search) 聊天页面组件 (React Component) 状态管理 (Zustand/React Query) API 请求工具 (e.g., callApi) 后端 API 服务

    流程说明:

    1. 用户与浏览器交互,在聊天输入组件中输入文字并点击发送。
    2. 输入组件触发聊天页面组件的状态更新和消息发送处理函数 (handleSendMessage)。
    3. 页面组件首先更新状态管理库中的消息列表,立即在 UI 上显示用户自己发送的消息,并设置加载状态。
    4. 然后,页面组件调用封装好的 API 请求工具 (callApi) 向后端 API 发起请求。
    5. 后端 API 处理后返回响应。
    6. API 请求工具将响应传递回页面组件。
    7. 如果请求成功,页面组件再次更新状态管理库,添加助手的消息,并更新 UI 显示;如果失败,则显示错误提示。最终清除加载状态。

3. RAG 模块 (rag/)

  • 核心功能: 实现检索增强生成的核心流程:检索相关文档 -> 构建 Prompt -> 调用 LLM 生成答案。

  • 核心代码示例: 模拟 RAG 处理流程的类。

    # Simplified example illustrating RAG flow logic
    from rag.llm import get_llm # 假设的 LLM 获取函数
    from rag.retriever import get_vector_retriever # 假设的检索器获取函数
    from rag.prompts import get_rag_prompt_template # 假设的 Prompt 模板获取函数
    from typing import List, Dict, Any
    
    class RagPipeline:
        def __init__(self, knowledge_base_id: str, llm_config: dict, retriever_config: dict):
            """
            初始化 RAG 流程所需的组件。
            :param knowledge_base_id: 目标知识库的 ID。
            :param llm_config: LLM 的配置信息。
            :param retriever_config: 检索器的配置信息。
            """
            # 1. 初始化 LLM (大型语言模型)
            #    - 根据 llm_config (如模型名称, API Key, 温度等) 获取 LLM 实例
            self.llm = get_llm(**llm_config)
            print(f"LLM initialized with config: {llm_config}")
    
            # 2. 初始化 Retriever (向量检索器)
            #    - 根据 knowledge_base_id 和 retriever_config (如使用的向量数据库地址, top_k 参数) 获取检索器实例
            self.retriever = get_vector_retriever(knowledge_base_id=knowledge_base_id, **retriever_config)
            print(f"Retriever initialized for KB '{knowledge_base_id}' with config: {retriever_config}")
    
            # 3. 获取用于生成答案的 Prompt 模板
            self.prompt_template = get_rag_prompt_template()
            print("RAG prompt template loaded.")
    
        def query(self, user_query: str, chat_history: List[Dict[str, str]] = None) -> Dict[str, Any]:
            """
            执行 RAG 查询。
            :param user_query: 用户提出的问题。
            :param chat_history: (可选) 对话历史记录。
            :return: 包含答案和源文档的字典。
            """
            print(f"\n--- Starting RAG query for: '{user_query}' ---")
            # === Retrieval Phase ===
            # 1. 向量检索: 从向量数据库检索与 user_query 相关的文档块 (Context)
            print(f"Retrieving relevant documents from retriever...")
            # retriever.retrieve() 可能内部先将 user_query 向量化,然后执行搜索
            relevant_docs = self.retriever.retrieve(user_query, top_k=retriever_config.get('top_k', 5))
            # 将检索到的文档内容拼接成上下文字符串
            context = "\n\n".join([doc.page_content for doc in relevant_docs]) # 使用换行符分隔文档块
            print(f"Retrieved {len(relevant_docs)} documents. Context length: {len(context)} chars.")
            if not context:
                 print("Warning: No relevant documents found.")
                 context = "No relevant information found in the knowledge base." # 提供默认上下文
    
            # === Generation Phase ===
            # 2. 构建 Prompt: 将上下文、问题(和对话历史)填充到模板中
            print("Building prompt for LLM...")
            prompt_input = {
                "context": context,
                "question": user_query,
                # "chat_history": format_chat_history(chat_history) # 可能需要格式化历史记录
            }
            try:
                # 使用模板的 format 方法填充占位符
                final_prompt = self.prompt_template.format(**prompt_input)
                print(f"Final prompt built (first 100 chars): {final_prompt[:100]}...")
            except KeyError as e:
                 print(f"Error formatting prompt: Missing key {e}. Using basic prompt.")
                 # 提供备用 Prompt 格式以防模板错误
                 final_prompt = f"Based on the following context:\n{context}\n\nAnswer the question: {user_query}"
    
    
            # 3. LLM 推理: 调用 LLM 生成答案
            print("Invoking LLM to generate answer...")
            # 调用 LLM 实例的 invoke 或 generate 方法
            response = self.llm.invoke(final_prompt) # 假设 invoke 返回一个包含 content 的对象
            generated_answer = response.content # 提取生成的文本答案
            print(f"LLM generated answer (first 100 chars): {generated_answer[:100]}...")
    
            # 4. 结果封装: 返回答案和引用的源文档
            result = {
                "answer": generated_answer,
                "source_documents": [doc.metadata for doc in relevant_docs] # 返回源文档的元数据
                # 或者返回整个 relevant_docs 对象列表: "source_documents": relevant_docs
            }
            print("--- RAG query finished ---")
            return result
    
    # --- 使用示例 (假设配置和函数已定义) ---
    # llm_config = {"model": "gpt-3.5-turbo", "temperature": 0.7}
    # retriever_config = {"type": "infinity", "host": "localhost", "port": 23817, "top_k": 3}
    # rag_pipeline = RagPipeline(knowledge_base_id="my_kb_123", llm_config=llm_config, retriever_config=retriever_config)
    # result = rag_pipeline.query("RAGFlow 的主要特点是什么?")
    # print("\nFinal Answer:", result["answer"])
    # print("Sources:", result["source_documents"])
    
  • 代码解释:
    这段代码定义了一个 RagPipeline 类来封装 RAG 的核心流程。

    1. 初始化 (__init__): 在创建实例时,根据传入的配置信息,分别初始化 LLM (get_llm) 和向量检索器 (get_vector_retriever),并加载 Prompt 模板 (get_rag_prompt_template)。这些是执行 RAG 必不可少的组件。
    2. 查询 (query): 这是执行 RAG 的核心方法。
      • 检索阶段: 调用 self.retriever.retrieve(user_query) 方法,输入用户问题,从向量数据库中检索出最相关的文档块 (relevant_docs)。然后将这些文档块的内容拼接成 context 字符串。
      • 生成阶段: 使用 self.prompt_template.format() 将检索到的 context 和用户问题 user_query (以及可选的对话历史) 填充到预定义的 Prompt 模板中,生成最终要发送给 LLM 的 final_prompt。接着,调用 self.llm.invoke(final_prompt) 将 Prompt 发送给 LLM,获取生成的答案 (generated_answer)。
      • 结果返回: 最后,将生成的答案和检索到的源文档信息(这里是元数据)打包成一个字典返回。

    RAG 查询处理流程

    此图展示了 RAG 模块内部处理一次查询请求的核心步骤。

    调用者 (e.g., API 服务) RAG 处理管道 向量检索器 嵌入模型 (FastEmbed/FlagEmbedding) 向量数据库 (InfiniFlow/ES) Prompt 构建引擎 大型语言模型服务 (OpenAI/Anthropic/Local) 调用 query(user_query, history, kb_id) 请求将 user_query 向量化 返回 query_vector 调用 retrieve(query_vector, top_k) 执行向量相似性搜索 (ANN Search) 返回 Top K 相似文档块 (IDs + 向量 + 元数据) (可选) 获取文档块原始内容 (可能需要访问 MinIO 或 DB) (可选) 对召回结果进行重排序 (Re-ranking) 返回相关文档块列表 (relevant_docs) 请求构建 Prompt (传入 relevant_docs, user_query, history) 根据模板格式化上下文和问题 返回 final_prompt 发送 final_prompt 进行推理 返回生成的答案 (response) 解析 response, 提取答案文本 返回 {answer: ..., source_documents: ...} 调用者 (e.g., API 服务) RAG 处理管道 向量检索器 嵌入模型 (FastEmbed/FlagEmbedding) 向量数据库 (InfiniFlow/ES) Prompt 构建引擎 大型语言模型服务 (OpenAI/Anthropic/Local)

    流程说明:

    1. 外部调用者(如 API 服务)调用 RAG 处理管道的 query 方法。
    2. RagPipeline 首先调用嵌入模型将用户问题转换为向量。
    3. 然后调用向量检索器,传入查询向量进行检索。
    4. Retriever 与向量数据库交互,执行相似性搜索,获取最相关的 K 个文档块。
    5. (可选) Retriever 可能需要根据 ID 从其他地方获取文档原文,并可能进行重排序优化。
    6. Retriever 将处理后的相关文档块列表返回给 RagPipeline
    7. RagPipeline 调用 Prompt 构建引擎,将检索到的文档(上下文)、用户问题和历史记录组合成最终的 Prompt。
    8. RagPipeline 将此 Prompt 发送给配置好的大型语言模型服务。
    9. LLM 服务返回生成的答案。
    10. RagPipeline 解析 LLM 的响应,提取答案文本,并将其与引用的源文档信息一起返回给调用者。

4. DeepDoc 模块 (deepdoc/)

  • 核心功能: 深度解析文档,提取结构化内容,包括文本、表格、图片,并理解布局。

  • 核心代码示例 (PDF 解析): 使用 PyMuPDF 库解析 PDF。

    # Simplified example from deepdoc/parser/pdf_parser.py
    import fitz # PyMuPDF
    from typing import List, Dict, Tuple, Any
    # from PIL import Image # 用于处理图片和可能的 OCR
    # import io
    
    class DeepPdfParser:
        def parse(self, file_path: str) -> List[Dict[str, Any]]:
            """
            解析 PDF 文件,提取文本块和图片信息,并包含位置信息。
            :param file_path: PDF 文件路径。
            :return: 包含页面元素的字典列表,每个元素代表一个文本块或图片。
            """
            parsed_elements = []
            try:
                doc = fitz.open(file_path) # 打开 PDF 文件
                print(f"Opened PDF: {file_path}, Pages: {len(doc)}")
            except Exception as e:
                print(f"Error opening PDF {file_path}: {e}")
                return parsed_elements
    
            for page_num in range(len(doc)):
                page = doc.load_page(page_num)
                page_index = page_num + 1 # 页码从 1 开始
                print(f"Processing Page {page_index}...")
    
                # 1. 提取文本块 (按 Blocks)
                # get_text("dict") 可以获取更详细的结构信息,包括字体、坐标等
                try:
                     blocks = page.get_text("dict", flags=fitz.TEXTFLAGS_TEXT)["blocks"]
                except Exception as e:
                     print(f"Error extracting text blocks from page {page_index}: {e}")
                     blocks = []
    
                for block in blocks:
                    if block["type"] == 0: # 文本块类型
                        block_text = ""
                        # 拼接块内所有行的文本
                        for line in block.get("lines", []):
                            for span in line.get("spans", []):
                                block_text += span.get("text", "")
                            block_text += "\n" # 行末加换行符
    
                        if block_text.strip(): # 忽略空块
                            element = {
                                "type": "text",
                                "content": block_text.strip(),
                                "bbox": tuple(block["bbox"]), # 边界框 (x0, y0, x1, y1)
                                "page": page_index,
                                # 可以添加更多信息,如字体大小、名称等 (从 span 获取)
                                # "font_size": block["lines"][0]["spans"][0]["size"] if ... else None
                            }
                            parsed_elements.append(element)
    
                # 2. 提取图片信息 (不包含图片数据本身,仅信息和位置)
                try:
                    images = page.get_images(full=True)
                except Exception as e:
                    print(f"Error extracting images from page {page_index}: {e}")
                    images = []
    
                for img_index, img_info in enumerate(images):
                    try:
                        # img_info[0] 是 xref, 用于提取图片数据
                        # img_info[7] 是图片名称或描述
                        # 获取图片在页面上的边界框
                        img_bbox = tuple(page.get_image_bbox(img_info).irect)
                        element = {
                            "type": "image",
                            "bbox": img_bbox,
                            "page": page_index,
                            "image_name": img_info[7], # 图片描述
                            "xref": img_info[0] # 用于后续提取图片数据的标识符
                            # "ocr_text": ocr_image_from_xref(doc, xref) # 可选:调用 OCR
                        }
                        parsed_elements.append(element)
                    except Exception as e:
                        print(f"Error processing image {img_index} on page {page_index}: {e}")
    
    
                # --- TODO: 布局分析与表格提取 ---
                # layout_blocks = analyze_page_layout(page) # 调用布局分析模型/函数
                # for block in layout_blocks:
                #     if block.type == "table":
                #         table_data = extract_table(page, block.bbox)
                #         parsed_elements.append({"type": "table", "data": table_data, ...})
                #     elif block.type == "title":
                #         # 标记文本块为标题等
                #         pass
    
            doc.close()
    
            # 可选:根据 Bbox 对元素进行排序,模拟阅读顺序
            # parsed_elements.sort(key=lambda x: (x["page"], x["bbox"][1], x["bbox"][0]))
    
            print(f"Finished parsing. Extracted {len(parsed_elements)} elements.")
            return parsed_elements
    
    # --- 使用示例 ---
    # parser = DeepPdfParser()
    # elements = parser.parse("path/to/your/document.pdf")
    # for element in elements:
    #     print(f"Page: {element['page']}, Type: {element['type']}, BBox: {element['bbox']}")
    #     if element['type'] == 'text':
    #         print(f"  Content: {element['content'][:50]}...")
    #     elif element['type'] == 'image':
    #         print(f"  Image Name: {element.get('image_name', 'N/A')}, Xref: {element['xref']}")
    
    
  • 代码解释:
    这个 DeepPdfParser 类演示了使用 PyMuPDF (fitz) 库解析 PDF 的核心步骤。

    1. 打开文档: 使用 fitz.open(file_path) 打开 PDF 文件。
    2. 遍历页面: 循环处理文档中的每一页 (doc.load_page(page_num))。
    3. 提取文本块: 调用 page.get_text("dict") 获取页面上的文本块及其详细信息(包括边界框 bbox)。代码遍历这些块,拼接每个文本块内的文字内容,并将其与类型 (“text”)、边界框、页码一起存入 parsed_elements 列表。
    4. 提取图片信息: 调用 page.get_images(full=True) 获取页面上的图片列表。对于每张图片,获取其边界框 (page.get_image_bbox) 和其他信息(如内部引用 xref、名称),存入 parsed_elements。注意这里只提取了图片的位置和引用信息,没有提取图片数据本身(可以根据 xref 后续提取)或进行 OCR。
    5. 布局分析与表格提取 (TODO): 代码中用注释标出了这部分,实际实现会更复杂,通常需要调用专门的布局分析函数或模型 (analyze_page_layout) 来识别页面区域(如表格、标题),然后根据识别结果进行处理(如调用 extract_table 提取表格数据)。
    6. 返回结果: 函数最后返回一个包含所有提取出的页面元素(文本块、图片信息等)的列表,每个元素是一个包含类型、内容/引用、位置等信息的字典。

    PDF 文档解析流程

    此图展示了 DeepDoc 模块解析一个 PDF 文档的核心步骤。

    调用者 (API/Executor) PDF 解析器 文件系统/MinIO PyMuPDF (fitz) 库 版面分析器 (可选, 模型/规则) OCR 引擎 (可选, Tesseract/PaddleOCR) 结构化输出 调用 parse(file_path) 读取 PDF 文件 返回文件内容/句柄 打开 PDF 文档 (fitz.open) 加载页面 (doc.load_page) 提取文本块 (page.get_text("dict")) 返回文本块列表 (含 bbox) 处理文本块, 存入结果 获取图片列表 (page.get_images) 返回图片信息列表 (含 bbox, xref) 存储图片位置信息 提取图片数据 (doc.extract_image(xref)) 返回图片字节流 请求识别图片文字 (传入图片字节) 返回 OCR 文本 将 OCR 结果与图片信息关联 opt [需要 OCR] loop [处理每个图片信息] 分析页面布局 (传入页面对象或图像) 返回布局元素 (表格区域, 标题等) (根据布局结果) 提取表格数据, 标记元素类型 opt [需要版面分析] loop [遍历 PDF 每一页] 关闭 PDF 文档 (doc.close) (可选) 对所有提取的元素按阅读顺序排序 返回结构化元素列表 (Output) 调用者 (API/Executor) PDF 解析器 文件系统/MinIO PyMuPDF (fitz) 库 版面分析器 (可选, 模型/规则) OCR 引擎 (可选, Tesseract/PaddleOCR) 结构化输出

    流程说明:

    1. 调用者启动 DeepDocParser 的解析任务,传入文件路径。
    2. 解析器从文件系统或 MinIO 读取 PDF 文件。
    3. 使用 PyMuPDF 库打开文档。
    4. 循环处理每一页:
      • 使用 PyMuPDF 提取该页的文本块及其位置信息,存入结果。
      • 使用 PyMuPDF 获取该页的图片信息(位置、引用等)。
      • 对于每张图片,存储其位置信息。如果需要 OCR,则使用 PyMuPDF 提取图片数据,调用 OCR 引擎识别文字,并将结果关联。
      • (可选) 调用版面分析器分析页面布局,识别表格、标题等区域,并根据分析结果进行处理(如提取表格数据)。
    5. 所有页面处理完毕后,关闭 PDF 文档。
    6. (可选) 根据元素的位置信息(bbox)对提取的所有元素进行排序。
    7. 最终将包含所有结构化元素(文本块、图片信息、表格数据等)的列表返回给调用者。

5. Agent 模块 (agent/)

  • 核心功能: 提供图形化 Agent 框架,通过组件化和工作流编排执行复杂任务。

  • 核心代码示例:

    • 组件基类 (ComponentBase) 和示例组件 (BeginComponent):

      # Simplified example from agent/component/base.py and begin.py
      from abc import ABC, abstractmethod
      from typing import Dict, Any, List, Optional
      
      class ComponentParamBase: # 用于定义组件参数 (可能用 Pydantic 实现更复杂验证)
           def __init__(self, name: str, type: str, required: bool = True, default: Any = None, desc: str = ""):
                self.name = name
                self.type = type # e.g., "string", "integer", "boolean", "file"
                self.required = required
                self.default = default
                self.description = desc
      
      class ComponentBase(ABC):
          """所有 Agent 组件的基类"""
          # 定义组件的元信息 (由子类覆盖)
          name: str = "Base Component" # 组件唯一名称
          description: str = "Base component description." # 组件功能描述
          input_params: List[ComponentParamBase] = [] # 定义输入参数
          output_params: List[ComponentParamBase] = [] # 定义输出参数
      
          def __init__(self, tenant_id: str, config: Dict[str, Any]):
              """
              初始化组件实例。
              :param tenant_id: 租户 ID。
              :param config: 来自 DSL 的该组件实例的配置参数。
              """
              self.tenant_id = tenant_id
              self.config = config # 存储传入的配置
              # 可以在这里进行参数验证
              self._validate_config()
      
          def _validate_config(self):
              """验证传入的 config 是否符合 input_params 定义。"""
              for param_def in self.input_params:
                  if param_def.required and param_def.name not in self.config:
                       raise ValueError(f"Missing required parameter '{param_def.name}' for component '{self.name}'")
                  # 可以添加类型检查等更多验证
              print(f"Component '{self.name}' config validated.")
      
          @abstractmethod
          def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
              """
              执行组件的核心逻辑。
              :param input_data: 从上游组件传递过来的数据。
              :return: 包含组件输出参数的字典。
              """
              pass
      
          def get_metadata(self) -> Dict[str, Any]:
               """返回组件的元数据,用于前端展示或 DSL 生成。"""
               return {
                    "name": self.name,
                    "description": self.description,
                    "inputs": [vars(p) for p in self.input_params],
                    "outputs": [vars(p) for p in self.output_params],
               }
      
      
      class BeginComponent(ComponentBase):
          """工作流的起始组件"""
          name: str = "Begin"
          description: str = "Marks the start of the workflow. Receives initial user input."
          # 起始组件通常没有输入参数,但可以定义输出参数代表用户输入
          input_params: List[ComponentParamBase] = []
          output_params: List[ComponentParamBase] = [
              ComponentParamBase(name="user_input", type="string", required=True, desc="Initial user query or objective.")
          ]
      
          def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
              """
              Begin 组件的 run 方法通常用于接收 Canvas 传递的初始输入。
              :param input_data: 由 Canvas 传入,通常包含用户最初的输入。
              :return: 将初始输入作为 "user_input" 输出。
              """
              print(f"'{self.name}' component running...")
              user_input_value = input_data.get("initial_user_input", "") # 从 Canvas 获取初始输入
              if not user_input_value:
                   print("Warning: No initial user input provided to Begin component.")
      
              # 将获取到的初始输入放入输出字典,键名需与 output_params 中定义的 name 匹配
              output = {"user_input": user_input_value}
              print(f"'{self.name}' component output: {output}")
              return output
      
      
    • 工作流执行引擎 (Canvas) 核心循环片段:

      # Simplified conceptual snippet from agent/canvas.py
      from typing import Dict, Any, List
      # from agent.component import load_component # 函数:根据组件名称加载并实例化组件类
      
      class Canvas:
          def __init__(self, tenant_id: str, workflow_dsl: Dict[str, Any]):
              self.tenant_id = tenant_id
              self.dsl = workflow_dsl # 加载工作流定义 (JSON DSL)
              self.nodes = {} # 存储实例化的组件节点: {node_id: ComponentBase_instance}
              self.edges = self.dsl.get("edges", []) # 存储边的连接关系
              self.execution_state = {} # 存储每个节点的输出结果: {node_id: output_dict}
              self._load_nodes()
      
          def _load_nodes(self):
              """根据 DSL 加载并实例化所有节点组件。"""
              print("Loading nodes from DSL...")
              for node_def in self.dsl.get("nodes", []):
                  node_id = node_def["id"]
                  component_name = node_def["component_name"]
                  config = node_def.get("config", {})
                  try:
                      # self.nodes[node_id] = load_component(component_name, self.tenant_id, config)
                      # 模拟加载 BeginComponent
                      if component_name == "Begin":
                           self.nodes[node_id] = BeginComponent(self.tenant_id, config)
                      else:
                           # 加载其他组件...
                           print(f"Placeholder for loading component: {component_name}")
                           # self.nodes[node_id] = SomeOtherComponent(self.tenant_id, config)
                           pass # 忽略未实现的组件
                  except Exception as e:
                      print(f"Error loading node {node_id} (Component: {component_name}): {e}")
              print("Nodes loaded.")
      
          def run(self, initial_input: Dict[str, Any]) -> Dict[str, Any]:
              """
              执行工作流。
              :param initial_input: 工作流的初始输入数据。
              :return: 最终结果 (通常来自 'Answer' 或 'End' 组件)。
              """
              print("\n--- Starting Canvas execution ---")
              execution_queue = self._find_start_nodes() # 找到起始节点 (通常是 Begin 组件)
              processed_nodes = set() # 记录已执行的节点
      
              while execution_queue:
                  current_node_id = execution_queue.pop(0) # 取出下一个要执行的节点 ID
                  if current_node_id in processed_nodes: # 避免循环重复执行 (简单处理,实际可能更复杂)
                       continue
      
                  node_instance = self.nodes.get(current_node_id)
                  if not node_instance:
                       print(f"Error: Node '{current_node_id}' not found.")
                       continue
      
                  # 1. 准备当前节点的输入数据
                  input_data = self._gather_input_data(current_node_id, initial_input)
      
                  # 2. 执行当前节点的 run 方法
                  print(f"\nExecuting node '{current_node_id}' (Component: {node_instance.name})...")
                  try:
                      output_data = node_instance.run(input_data)
                  except Exception as e:
                       print(f"Error running node '{current_node_id}': {e}")
                       # 可以根据策略决定是否停止执行
                       output_data = {"error": str(e)} # 记录错误
      
                  # 3. 存储当前节点的输出结果
                  self.execution_state[current_node_id] = output_data
                  processed_nodes.add(current_node_id)
                  print(f"Node '{current_node_id}' finished. Output: {output_data}")
      
                  # 4. 查找并添加下游节点到执行队列
                  downstream_nodes = self._find_downstream_nodes(current_node_id)
                  for next_node_id in downstream_nodes:
                      if next_node_id not in processed_nodes and next_node_id not in execution_queue:
                           # 检查是否所有上游依赖都已完成 (对于需要多个输入的节点)
                           if self._check_dependencies_met(next_node_id, processed_nodes):
                                execution_queue.append(next_node_id)
      
              print("--- Canvas execution finished ---")
              # 返回最终结果 (可能需要从特定的结束节点获取)
              return self._get_final_output()
      
          def _find_start_nodes(self) -> List[str]:
               # 查找没有入度 (或类型为 Begin) 的节点
               # ... 实现查找起始节点的逻辑 ...
               print("Finding start nodes...")
               start_node_ids = [node_id for node_id, node in self.nodes.items() if isinstance(node, BeginComponent)]
               print(f"Found start nodes: {start_node_ids}")
               return start_node_ids
      
      
          def _gather_input_data(self, node_id: str, initial_input: Dict[str, Any]) -> Dict[str, Any]:
              """根据边的连接关系,从上游节点的输出中收集当前节点的输入数据。"""
              print(f"Gathering input data for node '{node_id}'...")
              node_input = {}
              # 如果是 Begin 节点,使用初始输入
              if isinstance(self.nodes.get(node_id), BeginComponent):
                   return {"initial_user_input": initial_input.get("query", "")} # 假设初始查询在 "query" 字段
      
              # 查找连接到当前节点的边 (target_node == node_id)
              incoming_edges = [edge for edge in self.edges if edge.get("target_node") == node_id]
              for edge in incoming_edges:
                   source_node_id = edge.get("source_node")
                   source_output_key = edge.get("source_param") # 上游节点的输出参数名
                   target_input_key = edge.get("target_param") # 当前节点的输入参数名
      
                   if source_node_id in self.execution_state:
                       source_output = self.execution_state[source_node_id]
                       if source_output_key in source_output:
                            # 将上游输出值赋给当前节点的对应输入参数
                            node_input[target_input_key] = source_output[source_output_key]
                       else:
                            print(f"Warning: Source key '{source_output_key}' not found in output of node '{source_node_id}'.")
                   else:
                        print(f"Warning: Dependency node '{source_node_id}' has not been executed yet for node '{node_id}'.")
              print(f"Input data for node '{node_id}': {node_input}")
              return node_input
      
          def _find_downstream_nodes(self, node_id: str) -> List[str]:
               """查找当前节点的直接下游节点。"""
               # ... 实现查找下游节点的逻辑 ...
               downstream = [edge.get("target_node") for edge in self.edges if edge.get("source_node") == node_id]
               print(f"Downstream nodes for '{node_id}': {downstream}")
               return downstream
      
          def _check_dependencies_met(self, node_id: str, processed_nodes: set) -> bool:
               """检查目标节点的所有上游依赖是否都已执行完毕。"""
               # ... 实现依赖检查逻辑 ...
               incoming_edges = [edge for edge in self.edges if edge.get("target_node") == node_id]
               for edge in incoming_edges:
                    if edge.get("source_node") not in processed_nodes:
                         return False # 只要有一个上游未完成,就返回 False
               return True
      
      
          def _get_final_output(self) -> Dict[str, Any]:
              """从指定的结束节点获取最终输出。"""
              # ... 实现查找结束节点并获取其输出的逻辑 ...
              print("Getting final output...")
              # 查找 Answer 或 End 组件的输出
              for node_id, output in self.execution_state.items():
                   # 假设 Answer 组件会输出 "final_answer"
                   if "final_answer" in output:
                        return {"result": output["final_answer"]}
              return {"result": "Workflow finished, but no final answer node found or executed."} # 默认返回
      
      
      # --- 使用示例 ---
      # dsl = { ... json definition of nodes and edges ... }
      # canvas = Canvas(tenant_id="tenant1", workflow_dsl=dsl)
      # initial_data = {"query": "Summarize the latest news about AI."}
      # final_result = canvas.run(initial_input=initial_data)
      # print("\nFinal Result:", final_result)
      
    
    
  • 代码解释:

    这部分展示了 Agent 框架的核心概念:

    1. 组件基类 (ComponentBase): 定义了所有组件的通用结构。它包含组件元信息(名称、描述、输入/输出参数定义 input_params, output_params),以及一个抽象的 run 方法要求子类实现具体逻辑。构造函数接收租户 ID 和来自 DSL 的配置。_validate_config 用于验证配置。
    2. 示例组件 (BeginComponent): 继承自 ComponentBase,代表工作流的开始。它定义了自己的元信息(如名称 “Begin”),并实现了 run 方法。Begin 组件的 run 通常负责接收 Canvas 传递过来的工作流初始输入,并将其作为自己的输出(名为 “user_input”)传递给下游。
    3. 执行引擎 (Canvas) 片段: Canvas 类负责执行整个工作流。
      • __init__: 加载 DSL,实例化所有节点组件 (_load_nodes)。
      • run: 接收初始输入,找到起始节点 (_find_start_nodes),然后进入主循环。
      • 执行循环: 循环从队列中取出节点,收集其输入数据 (_gather_input_data,根据边的定义从上游节点的 execution_state 获取),执行节点的 run 方法,存储其输出到 execution_state,然后找到其下游节点 (_find_downstream_nodes),检查依赖满足后 (_check_dependencies_met) 加入队列。
      • _gather_input_data: 关键逻辑,根据 edges 定义,将上游节点的输出映射到当前节点的输入。
      • _get_final_output: 工作流结束后,从特定的结束节点(如 Answer 组件)获取最终结果。

    执行 Agent 工作流

    此图展示了 Agent 模块的核心执行引擎 Canvas 如何根据 DSL 执行一个简单的工作流 (Begin -> Retrieval -> Generate -> Answer)。

    调用者 (e.g., API 服务) Agent 执行引擎 (Canvas) 工作流定义 (JSON) Begin 组件实例 Retrieval 组件实例 Generate 组件实例 Answer 组件实例 RAG 工具 (被 Retrieval 调用) LLM 服务 (被 Generate 调用) 调用 run(initial_input, workflow_dsl) 解析 DSL, 加载并实例化所有组件 (Begin, Retrieval, Generate, Answer) 确定起始节点 (Begin) 将 initial_input 准备为 Begin 的输入 执行 run(input_data) 返回输出 {"user_input": ...} 存储 Begin 的输出, 查找下游节点 (Retrieval) 根据 DSL 边定义, 准备 Retrieval 的输入 (从 Begin 的输出获取 user_input) 执行 run(input_data) 调用 RAG 查询 (传入 user_input) 返回检索结果 (documents) 返回输出 {"retrieved_docs": ...} 存储 Retrieval 的输出, 查找下游节点 (Generate) 根据 DSL 边定义, 准备 Generate 的输入 (获取 user_input, retrieved_docs) 执行 run(input_data) 构建 Prompt (结合 user_input 和 retrieved_docs) 调用 LLM 进行文本生成 返回生成的文本 返回输出 {"generated_text": ...} 存储 Generate 的输出, 查找下游节点 (Answer) 根据 DSL 边定义, 准备 Answer 的输入 (获取 generated_text) 执行 run(input_data) 格式化最终答案 返回输出 {"final_answer": ...} 存储 Answer 的输出, 发现无更多下游节点 获取最终结果 (从 Answer 的输出中提取) 返回最终结果 {"result": ...} 调用者 (e.g., API 服务) Agent 执行引擎 (Canvas) 工作流定义 (JSON) Begin 组件实例 Retrieval 组件实例 Generate 组件实例 Answer 组件实例 RAG 工具 (被 Retrieval 调用) LLM 服务 (被 Generate 调用)

    流程说明:

    1. 调用者(如 API)启动 Canvas 执行工作流,传入初始用户输入和工作流定义 DSL。
    2. Canvas 解析 DSL,实例化其中定义的所有组件节点(Begin, Retrieval, Generate, Answer)。
    3. Canvas 确定起始节点是 Begin
    4. Canvas 执行 Begin 组件,将初始用户输入传递给它,Begin 组件将其作为 user_input 输出。
    5. Canvas 存储 Begin 的输出,并找到其下游节点 Retrieval。根据 DSL 中边的定义,将 Begin 输出的 user_input 作为 Retrieval 的输入。
    6. Canvas 执行 Retrieval 组件。Retrieval 组件内部调用 RAG 工具(传入 user_input),获取检索到的文档,并将结果作为 retrieved_docs 输出。
    7. Canvas 存储 Retrieval 的输出,找到下游节点 Generate。准备 Generate 的输入(需要 user_inputretrieved_docs)。
    8. Canvas 执行 Generate 组件。Generate 组件构建 Prompt,调用 LLM 生成文本,并将结果作为 generated_text 输出。
    9. Canvas 存储 Generate 的输出,找到下游节点 Answer。准备 Answer 的输入(需要 generated_text)。
    10. Canvas 执行 Answer 组件。Answer 组件格式化最终答案,并将其作为 final_answer 输出。
    11. Canvas 存储 Answer 的输出,发现没有更多下游节点,执行结束。
    12. CanvasAnswer 组件的输出中提取最终结果,并返回给调用者。

### 如何在不同平台使用 Iconfont 彩色图标 #### UniApp 中使用 Iconfont 彩色图标 当在 UniApp 项目中引入阿里云 Iconfont 图标时,可能会遇到即使选择了彩色图标但在应用内仍显示为单色的情况。为了确保正确展示彩色图标,可以采取特定措施来调整样式加载方式[^1]。 对于 UniApp 用户来说,在线创建并下载所需体包之后,需注意 CSS 文件中的颜色属性设置,默认情况下这些值可能被设为固定色彩(通常是黑色)。因此建议修改对应类名下的 `color` 属性为空符串或移除该属性,从而允许浏览器按照 SVG 原始定义渲染多色调图案: ```css .icon-class-name { font-family: "iconfont"; speak: none; /* 移除 color 定义 */ } ``` 另外一种解决方案是在 HTML 元素上直接指定 `fill="currentColor"` 参数给 `<svg>` 标签内的路径元素,这使得填充色跟随当前文本的颜色变化而自动适应不同的主题风格。 #### 微信小程序中使用 Iconfont 彩色图标 针对微信小程序开发者而言,官方提供了更简便的方式通过命令行工具初始化配置文件以便更好地集成 Iconfont 资源库。执行如下指令可完成环境搭建工作,并自动生成必要的依赖项以及模板代码片段用于后续开发过程[^2]: ```bash npx iconfont-init // 初始化通用版配置 // 或者 npx iconfont-wechat // 特化于微信生态系统的版本 ``` 上述操作完成后,只需遵循生成文档说明即可轻松实现对各种样式的支持,包括但不限于全彩矢量图形的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值