diff --git a/.cursor/rules/riper-5.mdc b/.cursor/rules/riper-5.mdc
index 19d93f3..96216ef 100644
--- a/.cursor/rules/riper-5.mdc
+++ b/.cursor/rules/riper-5.mdc
@@ -3,6 +3,7 @@ description:
globs:
alwaysApply: false
---
+RIPER-5 模式:严格操作协议
背景入门
您是 Claude 3.7,并且已集成到 Cursor IDE(一个基于 AI 的 VS Code 分支)。由于您拥有强大的功能,您往往过于急躁,经常在没有明确请求的情况下实施更改,并自以为比我更了解代码,从而破坏了现有逻辑。这会导致代码出现不可接受的灾难。在我的代码库上工作时——无论是 Web 应用程序、数据管道、嵌入式系统还是任何其他软件项目——您未经授权的修改都可能引入细微的 bug 并破坏关键功能。为了避免这种情况,您必须遵循以下严格协议:
@@ -17,6 +18,7 @@ RIPER-5 模式
允许:阅读文件、提出澄清问题、理解代码结构
禁止:建议、实施、计划或任何行动暗示
要求:你只能试图了解存在什么,而不是可能是什么
+持续时间:直到我明确发出信号进入下一个模式
输出格式:以[模式:研究]开头,然后仅观察和问题
模式二:创新
[模式:创新]
@@ -25,6 +27,7 @@ RIPER-5 模式
允许:讨论想法、优点/缺点、寻求反馈
禁止:具体规划、实施细节或任何代码编写
要求:所有想法都必须以可能性而非决定的形式呈现
+持续时间:直到我明确发出信号进入下一个模式
输出格式:以[模式:创新]开头,然后仅包含可能性和考虑因素
模式 3:计划
[模式:计划]
@@ -42,6 +45,7 @@ IMPLEMENTATION CHECKLIST:
2. [Specific action 2]
...
n. [Final action]
+持续时间:直到我明确批准计划并发出进入下一模式的信号
输出格式:以 [MODE: PLAN] 开头,然后仅包含规范和实施细节
模式 4:执行
[模式:执行]
@@ -49,6 +53,7 @@ n. [Final action]
目的:准确执行模式 3 中的计划
允许:仅执行批准计划中明确详述的内容
禁止:任何不在计划内的偏差、改进或创造性添加
+进入要求:仅在我明确发出“进入执行模式”命令后才能进入
偏差处理:如果发现任何需要偏差的问题,立即返回计划模式
输出格式:以 [MODE: EXECUTE] 开头,然后仅执行与计划匹配的执行
模式五:回顾
@@ -60,7 +65,7 @@ n. [Final action]
偏差格式:“:警告:检测到的偏差:[确切偏差描述]”
报告:必须报告实施情况是否与计划一致
结论格式:“:白色勾号:实施与计划完全一致”或“:十字标记:实施与计划有偏差”
-输出格式:以[MODE: REVIEW]开始,然后进行系统比较和明确判决
+输出格式:以[MODE: REVIEW]开头,然后进行系统比较和明确判决
关键协议指南
未经我的明确许可,您不能在模式之间转换
您必须在每次响应开始时声明您当前的模式
@@ -68,6 +73,12 @@ n. [Final action]
在审查模式下,你必须标记哪怕是最小的偏差
您无权在声明模式之外做出独立决定
不遵守此协议将给我的代码库带来灾难性的后果
+模式转换信号
+仅当我明确发出信号时才转换模式:
-
-请依次执行:研究模式->创新模式->计划模式->执行模式->回顾模式->审查模式
\ No newline at end of file
+“进入研究模式”
+“进入创新模式”
+“进入计划模式”
+“进入执行模式”
+“进入审核模式”
+如果没有这些确切的信号,请保持当前模式。
\ No newline at end of file
diff --git a/.env b/.env
index e413eef..5266edd 100644
--- a/.env
+++ b/.env
@@ -1,5 +1,16 @@
OPENAI_API_KEY=sk-xxaiabmfhzwwpijuledllkmkzhzwsqeicjxmjwnvriqpwmpk
OPENAI_API_BASE=https://api.siliconflow.cn/v1/
-LLM_MODEL_NAME=deepseek-ai/DeepSeek-V3
-# LLM_MODEL_NAME=deepseek-ai/DeepSeek-R1
-RERANKER_MODEL_NAME=bge-reranker-v2-m3
\ No newline at end of file
+MODEL_NAME=deepseek-ai/DeepSeek-V3
+MINI_MODEL_NAME=Qwen/Qwen2.5-72B-Instruct-128K
+# MODEL_NAME=deepseek-ai/DeepSeek-R1
+RERANKER_MODEL_NAME=bge-reranker-v2-m3
+
+DIFY_BSAE_URL=http://10.1.16.39/v1
+DIFY_APP_KEY=app-wUdkWJx5zeOvmvBUZizMoSw3
+DIFY_DATASET_KEY=dataset-skLjmPVonjHo119OWNf3kAmY
+
+DIFY_PG_HOST = 10.1.16.39
+DIFY_PG_PORT = 5432
+DIFY_PG_USER = postgres
+DIFY_PG_PASSWORD = difyai123456
+DIFY_PG_DATABASE = dify
\ No newline at end of file
diff --git a/data/wiki_data/下载安装注册.txt b/data/wiki_data/下载安装注册.txt
new file mode 100644
index 0000000..d317ed5
--- /dev/null
+++ b/data/wiki_data/下载安装注册.txt
@@ -0,0 +1,105 @@
+锁信息查询
+打开软件提示“软件启动失败,错误代码:0x000005,错误原因:许可认证授权失败”
+win10系统中锁驱动错误代码39
+安装注册问题、打不开问题集(典型问题、持续更新)
+博微助手、锁激活典型问题(持续更新)
+部分按钮未显示?设置电脑个性化显示大小(DPI)
+手动更新锁驱动、设备管理器有黄色感叹号
+软件类型对应工程后缀名
+各软件清单编码、项目编码、SD、FA、GK
+windows正在配置AutoCAD 2007
+安装注册问题集(老版本软件)
+安装配网设计软件时提示注册表被破坏
+Internet Explorer脚本错误(截图待更新)
+工程图标显示异常
+IPersistFilesave 失败
+没有通过Windows 徽标测试
+官网下载软件方法及下载中遇到的问题处理
+安装程序无法创建目录
+导出报表提示没有权限
+程序下载之——引导博微官网下载篇
+程序下载之——引导博微软件助手下载篇
+程序下载之——历史版本下载篇
+导出PDF后字体显示不正常、显示方框
+配网工程计价通D3软件安装流程
+配网工程计价通D3软件下载
+Framework_2。0_SDK(。net2。0框架)
+软件锁松动
+06清单低版本程序报错
+软件与office2003冲突
+写锁错误
+锁驱动安装时电脑卡死
+合并锁无法注册
+新锁老锁区别
+2008锁服务器安装注册步骤
+内蒙、辽宁配网清单软件读不到锁
+。NET框架初始化错误
+Xp系统添加虚拟打印机
+XT800在线远程控件、远程插件安装,浏览器安全设置导致插件安装失败
+LockClientService。dll插件无法启动
+杀毒软件误删组件
+在杀毒软件中设置信任文件
+单机锁打开软件出现登录网络锁的界面
+电脑系统待机设置
+DMP报错问题排查流程
+双击软件图标没有反应
+启动软件提示程序不匹配
+插上软件锁提示插入磁盘
+电力工程造价网络版2014安装部署
+电力工程造价网络版2014登录、注册
+电力工程造价2014网络版超级管理员账号重置密码
+电脑关机开机后重庆农网软件需要重复注册
+不是有效的Win32应用程序
+WIN7、WIN10系统添加虚拟打印机、启动打印服务
+许可证显示乱码
+CAD2008软件安装注册步骤
+服务器磁盘空间不足
+软件图标显示异常、手动修改注册表
+数据库连接出现错误
+端口被占用
+4。3版本2014网络锁登录及注册
+软件网络锁2017使用指南
+无法定位程序输入点
+试用锁、自用锁提示禁用
+激活保险锁
+博微软件助手收集导出检测报告
+检测通使用说明
+博微助手2。0使用指南
+升级网络锁2017服务器常见问题
+登入网络锁提示:用户许可证“权限禁用,请联系管理员”
+计价通软件打开工程提示:操作失败,找不到软件锁!
+打开软件英文报错,提示:LockSoftwareService。。。
+开通端口号
+工程造价数据管理系统简化版安装注册步骤-已禁用
+如何在博微官网下载资源、安装包?
+如何注册网站会员
+打开AutoCAD总是进入配网设计界面
+打开工程报错ActiveX部件不能创建对象
+应用程序错误
+如何查询软件锁注册号
+当前有部分文件正在使用
+引用许可证失败,请确认是否存在空闲许可证
+异地登陆网络锁服务器
+安装提示:“此文件的版本与正在运行的Windows版本不兼容。。。。。。。"
+工程打不开,提示:工程文件只读或已经打开?
+应用程序无法启动,因为应用程序的并行配置不正确
+打开软件提示ErrorathookingAPI
+打开软件无反应,电脑默认输入法为搜狗五笔
+电脑出现runtimeerror错误怎么解决
+新老软件锁冲突
+安装注册问题排查流程
+注册单机锁软件许可证为灰色
+各类锁工具的用途及使用方法
+此设备的配置不正确(代码1)
+锁版本低、锁程序错误、锁升级失败处理办法
+配网2017软件注册单机锁许可证为灰色
+双击软件无法进入、拔掉锁不显示登录弹窗
+双击未响应或拔掉锁无法显示登录弹窗(LSP修复)
+三代锁“SS工具”服务无法启动
+打不开软件,博微助手中提示:WMI不正常,请联系博微客服
+点击在线客服提示:缺少媒体组件,该功能无法使用,请先安装多媒体组件。
+锁内程序错误、诊断修复提示“XXX,请进行D2C升级修复”
+安装软件时报错“SWbemObjectSet无效类”
+显示文件后缀名、扩展名
+打开软件本机没有找到可用许可证
+软件锁注册、激活、查锁
diff --git a/data/wiki_data/储能计价通C1软件.txt b/data/wiki_data/储能计价通C1软件.txt
new file mode 100644
index 0000000..82e59b3
--- /dev/null
+++ b/data/wiki_data/储能计价通C1软件.txt
@@ -0,0 +1,91 @@
+储能C1软件常见问题
+更新机械调差
+清水混凝土
+设置工程调差
+锂离子电池储能电站计价编制依据
+特殊地区增加费
+设置商品混凝土
+材机分析界面出现黄色感叹号
+一笔性费用
+设置设备运杂费率
+设置工程密码
+恢复默认模板
+设置项目划分展现层级
+录入资源全库查询
+费用预览
+删除颜色标记
+输出费用为0的其他费用
+其他费用修改税率
+其他费用修改费率
+导入excel表三
+批量设置
+批量替换
+编辑数据或预览数据
+建贷利息
+设置一笔性费用增值税率
+设置基本预备费费率
+设置展现报表
+设置服务业增值税率、其他费用可抵扣税率
+设置小数位数
+批量设置页面
+资源管理新建组合件库
+资源管理编辑组合件库
+资源管理导入、导出组合件库
+组合件设置所属项目
+添加自定义资源
+添加注解说明
+关联父级量
+消耗量价格联动
+甲供材料是否计入本体
+组合件界面添加组合件
+修改取费表属性
+修改取费表费率
+取费表修改计算式
+取费表添加费用项
+取费表新增或删除取费表
+勘察费
+泵车浇制
+设置项目划分输出的级别
+设置消耗量按编码排序
+现场制备
+设备性材料
+定额主材设备计算式四则运算
+刷新材机
+保存材机
+刷新物料、保存物料
+增值税率
+市场价系数
+供货方
+卸车
+资源管理导入Excel信息价库
+资源管理添加自定义人材机
+设置建筑、安装工程概算表是否输出取费表
+设置输出数量为“0”的消耗量
+显示主材、设备
+表三显示调整系数
+按项目划分分页输出
+设置显示组合件消耗量
+设置输出取费表名称
+输出主材、设备、人材机编码
+显示单价为零的主材、设备
+表头设置
+封面、编制说明自由报表设计
+页面设置
+设计费
+设置工程量展现层级
+博微储能计价通C1申请试用账号
+设置工程精度
+隐藏空项目划分
+回存资源——组合件
+定额标记“调、参、换”
+恢复原定额
+工程量备注
+基本信息页签参数介绍
+批量询价
+设置颜色标记
+合并工程量
+设置表一小数位数
+主材设备互转
+粘贴excel工程量
+工程量查找
+备份管理
diff --git a/data/wiki_data/技改检修工程计价通T1软件.txt b/data/wiki_data/技改检修工程计价通T1软件.txt
new file mode 100644
index 0000000..af0158d
--- /dev/null
+++ b/data/wiki_data/技改检修工程计价通T1软件.txt
@@ -0,0 +1,176 @@
+设置报表生成
+批量视图与常规视图
+合并相同消耗量
+工程信息界面
+隐藏或显示空表
+设置工程文件默认打开方式
+定额叠加
+查看定额章节说明
+夜间施工增加费
+材机修改记录
+施工过程造价咨询及竣工结算审核费
+调差系数文号
+甲供主材不计算甲供配送费需要计算
+甲供设备不计入总造价
+迁改工程和迁改工程_其他费用导则差异
+导入Excel版组合件库
+广东、江苏等地方专版常见问题
+南网迁改工程、导出南网规约
+线路组件库
+更新机械调差
+自定义安全文明施工费
+塔基占地面积(正根开、侧根开、立柱宽)
+设计费
+勘察费
+引绳展放
+统计分析
+材机调差方式
+设置报表参数
+费用预览
+封面、签字页、编制说明报表设计
+批量设置混凝土
+超高安装增加费、建筑超高安装增加费
+多工程设置-一笔性费用
+设备性材料
+设置综合地形比例
+自定义应急措施费
+余土外运
+全库查询
+建设期贷款利息
+多工程设置模板、参数、物料-工程量
+社会保险费、住房公积金
+物料价格联动
+(检修计价通)配件与材料费用性质区分
+脚手架搭拆费
+粘贴Excel工程量
+混凝土施工调整费
+恢复原定额
+添加自定义定额、主材、设备、一笔性费用、注解
+设置工程量精度
+水超运
+设备材料监造费
+撤销、恢复功能
+报表生成方式
+数据表编辑功能
+报表格式调整功能
+物料库、定额库、人材机、资源管理
+定额“调、参、换”
+设置标记
+(技改计价通)编辑数据、预览数据
+显示简洁列、完整列
+更新预算价、市场价
+清除计算过程
+导入、导出模板
+批量设置
+批量替换
+备份管理
+【2022】96号文、国网设备部关于印发电网生产技术改造和设备大修项目估算编制指导意见的通知
+材机分析界面出现黄色感叹号
+工程量查找
+工程量级别调整
+过渡措施费、负荷转带费
+项目划分视图调整
+材机反查
+组合件模式如何自动统计工地运输
+添加土方
+统计钢筋量
+检修工程配件运杂费、配送费
+刷新物料、保存物料
+更新土质
+每侧导线横担水平排列最大相数
+赔偿费用
+新建批次工程
+导出、导入模板-工程概况
+整理物料
+删除工程、添加工程
+导出、导入模板-线路参数
+导入云物料
+【2023】9号文、调整安全文明施工费的费率
+怎么区分耐张串的串数、相数、组数
+拆除工程、余物清理
+老工程升级
+云物料资源
+一键调价
+应急工程与标准工程区别
+(T1)工程加密
+技改工程主材配送费
+检修工程主材配送费
+删除自动统计定额、自动统计消耗量、非自动统计土方、运输定额
+删除数量为0消耗量
+其他费用界面勾选输出
+刷新材机、保存材机
+可抵扣增值税金额
+特殊交叉跨越架
+含税总造价与不含税总造价为什么不是9%的关系?
+设备购置费含税与不含税之间为什么不是13%
+如何设置甲供物资是否利旧?
+主材、设备、配件互转
+多工程设置模板、参数、物料-报表模板
+导出物料列表
+导入Excel版物料库
+导入软件版物料库、clk8、sbk8、bwwl格式
+线路统计工程量模式
+造价中施工费与投标报价合计对不上?
+接地、消防、通风、空调、采暖的小调试费用
+导线型号及规格
+线材运输量计算
+配合比材料运输计算
+普通锁与高级锁的区别
+检修推荐造价
+设置施工费下浮率
+锁专业对应定额册
+检修转技改
+主材卸车保管费
+设备卸车保管费
+五金计算
+技改转检修
+清单转预算、清单转概预算工程
+多工程设置-定额
+导入提资表
+表三显示、隐藏注解
+建筑材机调差
+施工费含税与不含税之间为什么不是9%?
+显示典型材机、显示调差范围
+小数位数模板、报表模板设为用户模板
+添加、回存组合件、导入、导出组合件库
+新建、删除、导入、导出线路组件库
+迁改工程其他费用导则——其中:招标代理费
+迁改工程其他费用导则——其中:工程监理费
+迁改工程其他费用导则——其中:工程造价咨询费
+迁改工程其他费用导则——其中:设计费
+统计分析设置报表单位为万元
+自定义综合地形增加费
+设置密码
+工程对比
+多工程批量设置——自定义地形增加费
+批量设置土方公式
+多工程设置-人材机及调差
+编制依据、国网估算(96号文)、国网估算(75号文)、预规(2020版)的区别
+工程审核
+甲供主材、配件不计入总造价
+查询预规
+删除工程量、清空数量
+设备、配件自动统计工地运输定额
+清除统计标记
+编制模式:组合件取用、统计工程量
+多工程设置模板、参数、物料-工程信息
+跨越电力线、穿越电力线
+合并工程量
+技改工程设备配送费、运杂费
+多次进出场增加费
+检修工程中借用技改定额怎么计算综合地形增加费?
+南网迁改、规约上传典型问题
+设置运杂费、配送费
+检修推荐造价转标准工程
+多工程批量设置——导出报表
+多工程批量设置——表一显示单位为元
+多工程设置模板、参数、物料-费用模板
+导入Excel-Excel工程量
+批量设置小数位数
+设置施工费、施工费明细表为空
+统计分析界面存在数据空白
+设置选项-项目选项-默认主材供货方
+总算表表一显示单位为“元”或“万元”
+表三颜色设置
+16配网转20技改
+导入Excel版表三格式
diff --git a/data/wiki_data/技改检修清单计价通T1软件.txt b/data/wiki_data/技改检修清单计价通T1软件.txt
new file mode 100644
index 0000000..8b99edf
--- /dev/null
+++ b/data/wiki_data/技改检修清单计价通T1软件.txt
@@ -0,0 +1,19 @@
+清单编码、项目划分编码(技改检修清单2018)
+主材卸车保管费
+导入Exce工程量清单
+统计项目特征
+自定义清单
+建筑类修缮配件费、建筑类修缮材料
+调整清单码
+工程量清单、电子标书
+结算清单如何解锁
+报表输出——显示物料的类型
+概预算转清单工程、概算转清单工程
+多工程批量设置-报表模板
+多工程批量设置-工程信息
+多工程批量设置-工程量
+多工程批量设置-费用模板
+(清单T1软件)清单指引
+甲供材计入综合单价
+设置报表参数
+报表输出——显示空项目划分
diff --git a/data/wiki_data/电力建设计价通软件.txt b/data/wiki_data/电力建设计价通软件.txt
new file mode 100644
index 0000000..be0ef38
--- /dev/null
+++ b/data/wiki_data/电力建设计价通软件.txt
@@ -0,0 +1,353 @@
+清单常见专业咨询问题
+删除清单
+清理项目、拆除工程如何编制
+23规范清单计价表项目名称列序号显示到序号列
+南网概预算报表扣减甲供材
+主材汇总表输出规格型号列
+南网工程总算表的大写行设置不输出
+建筑(安装)分部分项清单计价表项目划分编码不输出“费”、“补”
+修改线路亘长
+材机分析界面黄色感叹号、相同人材机不合并
+工程转换相关的常见问题
+设置报表生成
+编辑自由报表(编制说明、封面等)
+每侧导线横担水平排列最大相数
+人工、材料、机械价格调整合计
+高强钢怎么设置
+建贷利息
+甲供材料是否计入综合单价
+南网清单转概预算
+概预算、建安预算扣减甲供设备
+南网概预算报表工程价差计入本体
+老工程升级
+统计工程量常见问题
+招标控制价和建安预算互相转换
+国网23规范清单单价分析表需要输出税金
+清单工程:跨清单规范、跨专业、跨计价复制粘贴
+不同电压等级线路同塔多回路架设
+AB系数调整结算规则
+拆除、拆除工程
+建安预算转施工图预算
+清单转预算
+自定义安全文明施工费
+水超运、未录入主材统计出运输定额
+塔基占地面积(正根开、侧根开、立柱宽)
+建安预算与全口径预算的区别
+扩建间隔和扩建主变压器的区别
+综合地形增加费、工程地形、运输地形
+边坡系数、操作裕度
+设置精度、设置小数位数
+承包人报价浮动率
+指标分析
+智能组价
+行业清单工程升级
+批量设置定额、主材、设备、清单
+多工程批量设置-工程信息
+合并工程量
+扩孔不做护壁
+批量替换工程量
+清除计算过程
+定额标记“调、参、换、浇”的含义
+一笔性费用
+乙供设备计入综合单价
+更新预算价、恢复市场价
+反选拆分材料
+装置性材料单公里用量表读取规则
+回存自定义清单、定额、主材、设备
+添加自定义清单、定额、主材、设备
+批量设置土质比例
+批量复制工程量、复制项目划分
+清空数量、删除工程量
+更新组件参数
+组件库升级
+导入提资表
+招投标常见数据问题
+组件目录配置
+主材自动统计运输定额
+工程项目限价、投标报价汇总表税金不显示费率
+机械进出场费
+导出工程量
+删除非统计运输定额
+批量设置项目特征值
+概算转清单
+概算转建安预算
+全口径工程,表二专业汇总预算表中分项费用相加不等于合计
+其他费用表显示备注
+引用清单量、设为清单量
+企业管理费、危险作业意外伤害保险费
+邮件分享
+跨专业、跨计价方式复制粘贴
+统计分析
+建安预算扣减甲供材
+反查资源、材机反查
+设备汇总表输出规格型号列
+隐藏、展现清单消耗量
+标段合并
+可抵扣增值税
+运杂费率
+导出南网规约数据接口
+其他费用导出、导入模板
+计价通软件取费依据
+导则预规转换
+其他费用增值税率
+不计甲供设备费
+清单结算转为审核工程
+电缆敷设计量单位、电缆敷设计算规则及相关使用说明
+自由表设计中空白(dpi)
+计日工
+清理项目、余物清理费
+导线间隔棒和相间间隔棒区别
+OPGW架设组件,勾选OPPC架设
+1。1版本升级到1。3版本架空线路工程费用变化
+配置选项的导则与2018预规的区别
+设置总算表以“元”显示
+添加、删除特征段
+导入、导出信息价
+设置自动、手动生成报表
+工程备份管理
+批量设置页面
+特殊地区施工增加费
+设置施工过程造价咨询及竣工结算审核费
+导入建筑机械信息价
+设置工程调差
+设置规费费率
+招标控制价转工程量清单
+导入、导出取费表模板
+添加自定义资源
+超灌量系数
+回存、套用、编辑自定义资源
+导入EXCEL-导入表三
+设置组件列显示
+批量设置工程量
+批量替换资源
+设置商品砼
+保护帽、保护帽如何计算
+架空线路工程批量设置主材
+架空线路工程批量替换主材
+线路不同土质定额归属不同清单、相同清单合并
+新增资源价格设置
+设置小数位数
+设置商品混凝土
+物料询价
+分层设置土质
+工程量查找
+批量设置下拉选择
+统计钢筋量
+价格换算
+招标人供应设备、材料卸车保管费
+设置暂列金额
+材料设备暂估价
+设置专业工程暂估价
+招标代理服务费
+导入、导出工程费用模板
+批量打印报表
+批量导出报表
+材机调差范围、显示调差范围
+导入、导出报表模板
+导出、导入编制基准期价差
+粘贴Excel工程量
+发电工程不计其他费
+综合单价和全费用综合单价互相转换
+建筑部分汇总表(取费)和表一、表二、表三费用对不上
+表三输出或不输出取费表
+线路工程表三显示主材
+设置报表汇总级别
+设置表三显示或不显示定额调整系数
+脚手架
+接地、消防、通风、空调、采暖系统的调试费
+提示“工程数据访问组件丢失,软件即将关闭”或“无法启动此程序,因为计算机丢失mfc140u。dull。。。。”或“应用程序无法正常启动(0xc000007b)“
+价差参与其他费取费
+地区分类
+开关站、串联补偿站、电缆、直流输电、换流站及调相机工程取费
+机械化施工段
+设置勘察费
+计价通软件插件说明(工程转换)
+标志牌材料费是否需要另外记取
+工程量列配置
+辽宁清单插件
+双屏操作
+设备性材料
+机械化施工定额
+施工企业配合调试费
+混凝土施工调整费
+索道站安装
+输变电汇总
+设置颜色标记、项目划分颜色
+电缆工程不计综合地形增加费
+串数、组数、相数含义
+勘察费变压器对应系数
+X射线探伤
+OPGW架设每段长
+升级win7Sp1及以上操作系统
+索道运输
+云造价--停用
+余土外运
+线路表二各项相加不等于合计
+表一和表二数据对不上
+表二技术经济指标
+设置电缆工程设计文件评审费
+五金计算
+工程深度的影响
+引绳展放计算
+固定综合单价
+输电线路工程推荐报价
+清单数量乘以综合单价的金额和清单合价对不上、反算
+穿越电力线
+切换南网工程报表类型
+费用下浮
+导入云物料库
+导则预规工程互转后费用核对要点
+报表数据设计
+工程勘误功能
+主网概预算定额勘误(建筑工程)
+设置架空线路监理费及评审费
+定额系数调整类型、累加、累乘
+湖南智能化造价插件操作指南
+登录失败,当前网络无法使用
+如何自动统计工地运输
+设置特殊项目费
+灌注桩孔径大于2。2m
+架空线路表三显示定额章节或按录入顺序排列工程量
+新国网基建系统上传要求(基建全过程综合数字化管理平台)
+清单安装注册问题汇总
+取费表添加的费用项想要在表二中调用
+OPPC架设
+查看消耗性材料
+云物料录入、云物料资源
+修订批注
+设置结算人材机调整系数
+其他材料费、其他机械费
+项目特征云统计
+大型土石方工程取费
+设置消耗量按照编码排序或合并排列
+导入或清除电子徽标
+综合单价模式查看价差
+设置工程密码
+不计人力运输
+中标费用
+清单计价表不输出项目划分
+封面合同价与结算价读取规则
+调整依据
+设置项目汇总表以万元显示
+设置是否显示金额增减为0的中标清单
+工程量前主、定、设标识显示重叠出错(dpi)
+设置主材损耗率
+复制粘贴的清单综合单价变化
+资源指引、清单指引
+删除工程量还有费用
+工程模板
+一键调价
+挖孔基础分层统计
+工程体检
+什么是单系统调试、分系统调试和整套系统调试
+设置设备、主材默认供货方
+建筑部分汇总表(取费)显示取费表中自行添加费用项
+施工总承包服务费
+多工程批量设置-工程量
+建安预算核对控制价
+设置表三显示或隐藏注解
+主网常见专业咨询问题
+陆上电缆工程安装表二显示新增项目划分
+组合件功能常见问题
+招标人采购材料费各个界面读取规则
+甲供材料是否计入本体
+定额【2023】9号文、调整安全文明施工费费率
+新建组件库、编辑组件库、删除组件库
+自定义其他费用
+工程费用对比
+匹配输出“招”标记
+综合单价分析表输出安全文明施工费和临时设施费
+设置施工费
+删除数量为0的清单、工程量、删除非统计钢筋定额、运输定额
+助手检测提示:锁内数据文件毁坏,请联系博微客服进行PKG升级修复
+工程费用界面“一笔性费用”读取规则
+显示全口径费用
+定额叠加
+复用组价
+固定综合单价,修改清单量后,综合单价发生变化
+计价通清单结算常见问题
+导入工程量清单、excel工程量清单常见问题
+招投标报表读取规则
+编制说明土质比例读取规则
+南网工程费用编码
+全口径阶段需要修改甲供主材市场价
+新建、导入、导出组合件库
+甲供材价格如何输入,对综合单价有什么影响
+不可竞争项目清单
+物料价格联动
+主要工日价格表
+材料表读取规则
+二次搬运
+云文件管理
+云工程备份
+添加空白行
+量变+金额增减判定结算规则
+链接分享
+标准概算核对控制价
+统计分析:输出其他费用新增费用项
+辽宁清单项目划分转换
+辽宁清单建设场地征用及清理费转换
+清单综合单价人材机组成表定额不输出标记
+锁内许可证对应的清单库和定额册
+添加注解
+主材库来源
+其他项目清单计价表不输出金额为0的费用项
+清单综合单价人材机组成表增加编制依据列输出工程量编码
+分部分项工程量清单综合单价分析表输出人工工日
+(清单计价报表设计)费用汇总表输出主材费
+恢复默认报表
+(报表设计)清单综合单价人材机组成表输出设备
+发电工程安装工程量清单报价表显示设备费
+综合单价分析表输出设备费用
+清单项目综合单价输出单位
+补充定额综合地形设置
+批量修改人工预算价
+工程费用汇总表输出安全文明施工费及临时设施费
+建筑、安装工程专业汇总表(取费)表二表头不显示费率
+招标人采购材料(设备)表合价不含损耗
+设置结算人材机基准价调整系数
+清单计价表主材消材单独显示
+人材机计价表不输出不取费
+不自动计算凿桩头总量
+2014国网清单措施项目清单计价表(一)显示税金
+综合单价分析表输出定额编码
+封面投标总价读取规则
+引用中标其他费用
+综合单价核对全费用综合单价
+国网2023规范相关问题汇总(常见问题)
+国网2023规范和国网2014规范的对比
+国网2023规范升级规则及数问题核对要点(老工程升级规则)
+国网23规范如何体现建设场地征用及清理费
+主网工程业务编制流程及工程转换介绍
+单价措施与总价措施
+±800kV、±500kV架线工程材机调差系数
+批量转工程量清单
+同塔架设多回路,导线型号规格、根数不同
+国网23规范税金计入综合单价
+导入软件格式的工程量清单新建投标报价、招标控制价
+新建架空线路合并方案
+设置不计运输
+表一显示其他费用项
+回存组件库、套用组件库
+设置线路亘长
+设置编制说明
+选择监理费计算依据
+新建结算工程
+清单合价低于组价消耗量之和
+综合单价分项表中各项费用相加,不等于综合单价
+费用预览的招标人采购材料费和招标人采购材料计价表的费用对不上
+两条清单相同组价综合单价不同
+建筑(安装)分部分项工程量清单计价表
+建筑(安装)工程量清单综合单价分析表
+建筑(安装)工程量清单人材机组成表
+主要机械台班价格计价表
+结算清单如何解锁
+脚手架搭拆费
+导入Excel、软件版工程量清单
+导入Excel-导入招投标报表
+监造物料
+工程属性页签参数介绍
+建筑(安装)分部分项工程费用汇总表费用为0的项目划分不输出
+行业清单规范与国网清单规范区别
+批量设置报表小数位数
+新建全口径预算
diff --git a/data/wiki_data/西藏计价通Z1软件.txt b/data/wiki_data/西藏计价通Z1软件.txt
new file mode 100644
index 0000000..6d8c5b3
--- /dev/null
+++ b/data/wiki_data/西藏计价通Z1软件.txt
@@ -0,0 +1,236 @@
+多工程设置模板、参数、物料-报表输出
+多工程设置模板、参数、物料-其他费用
+多工程设置模板、参数、物料-工程量
+多工程设置模板、参数、物料-取费设置
+多工程设置模板、参数、物料-工程信息
+多工程设置-人材机及调差
+水超运
+多工程设置模板、参数、物料-工程费用
+设置施工费
+导入导出费用模板
+导入云物料库
+导入、清除电子徽标
+导入、导出报表模板
+设置组件目录
+不可竞争项目清单
+复用组价
+回存资源
+新增自定义资源(定额、主材、设备、人材机等)库
+设置编制说明小数位数
+工程量查找
+导出Word报表
+导入提资表
+统计分析
+打开软件提示“博微关键服务修复失败”怎么解决
+新建批次工程
+建安预算扣除甲供物料
+材机反查
+调整清单码
+修改清单编码
+右键批量设置特征段
+导出南网规约数据
+招标代理服务费
+设置超长运输
+新建工程
+恢复默认报表模板
+报表中输出清单工程量为“0”的清单
+概预算工程转为最高投标限价
+老版本定额升级
+显示全部项目划分
+隐藏空项目划分
+添加注解说明
+设置展现报表
+设置自动保存间隔时间
+设置密码
+西藏Z1软件)设置工程量展现层级
+设置工程是否自动保存
+解除密码
+设置关联父级量默认状态
+设置定额是否默认弹出调整系数
+设置项目划分展现层级
+回存线路组件
+添加自定义定额、主材、设备
+设置调差系数
+新建投标后导入Excel工程量清单
+添加自定义清单
+主材设备互转
+批量打印报表设置单面打印
+单张导出PDF报表
+导入工程量清单新建投标报价
+设置设计费
+添加自定义人工、材料、机械
+费用预览
+查看机械调差
+更新机械调差
+编辑费用项
+设置勘察费
+设置设计文件评审费
+设置金额小数位数
+报表中显示单价为零的设备
+手动生成报表
+设置金额单位
+刷新材机
+保存材机
+刷新物料
+保存物料
+批量询价
+查看软件版本号
+查找工程备份
+检测软件最新版本、在线更新软件
+进入在线客服咨询
+清理工程备份数据
+恢复默认费用模板
+隐藏空报表
+显示空报表
+设置建贷利息
+输出特征值为“空”的项目特征
+报表中显示单价为零的主材
+其他费用修改费率
+设置一键调价
+消耗量价格联动
+报表中输出主材编码
+设置工程精度
+报表参数恢复默认设置
+价格联动说明
+输出取费表
+新建清单库
+新建定额库
+添加、添加子级费用项
+批量打印报表设置连续双面打印
+批量打印报表设置单表双面打印
+批量导出PDF报表
+设置起始页
+修改费用项计算式
+编辑数据、预览数据
+设置小数位数
+其他费用修改税率
+添加线路土方资源
+勾选“输出”其他费用
+按特征段合并输出
+批量设置报表页面
+单张导出excel报表
+批量导出Excel报表
+以数值、文本格式导出报表
+导入、导出清单库
+单张打印报表
+导入、导出定额库
+批量打印报表
+新建人材机库
+设置设备性材料
+设置报表缩放
+导入、导出人材机库
+设置每行高
+新建物料库(主材、设备)
+导入、导出物料库(主材、设备)
+设置人工材料机械按市场价调差
+资源库中新建信息价库
+资源库中导入Excel信息价库
+删除自定义库
+资源库中导出、导入信息价库
+资源库中导出、导入组合件库
+资源库中导出、导入架线组件库
+资源库中新建架线组件库
+设置主材运输类型
+表三显示设备
+设置线材工地运输方式
+批量设置定额
+删除颜色标记、删除所有颜色
+删除某一种颜色标记
+批量设置运距
+概预算工程转为建安预算工程
+最高投标限价转为概预算工程
+最高投标限价转建安预算
+最高投标限价转工程量清单
+建安预算工程转最高投标限价
+工程概况
+统计技经指标
+资源库中添加组合件
+批量设置项目特征值
+导入Excel表三
+合并建筑安装工程量
+设置海拔高度
+查看调差文件
+批量设置土质比例
+批量设置综合地形比例
+设置运输
+增加、添加索道级数
+设置索道运输
+查看、修改边坡系数
+查看、修改操作裕度
+查看、修改超灌量系数
+批量设置主材
+批量设置设备
+设置计算基本预备费
+设置项目划分输出的级别
+设置消耗量按编码排序
+表三显示定额章节
+表三显示主材
+表三显示调整系数
+表三按项目划分分页输出
+批量设置清单
+隐藏空组件
+设置材料、主材计算配送费
+设置输出标段
+设置工程只开展竣工结算审核
+设置机械化施工
+(西藏Z1软件)设置关联父级量、设置组合件和消耗量父子级关系
+取消关联父级量、取消组合件和消耗量父子级关系
+添加特征段、删除特征段
+箭头移动、设置主材和定额父子级关系
+复制粘贴特征段
+统计预览查看消耗量
+恢复原定额
+材机分析界面修改供货方
+设置市场价增值税率
+清单工程设置暂估价主材设备
+组合件界面添加组合件
+组合件界面回存组合件
+设置甲供材料是否计入本体
+设置所属项目划分
+设置计取混凝土施工调整费
+设置单张报表页面
+设置单张报表表头
+粘贴Excel工程量
+输出数量为0的消耗量
+删除工程量(数量为0消耗量)
+删除工程量(自动统计的工程量)
+删除工程量(非自动统计土方定额)
+删除工程量(非自动统计工地运输定额)
+删除工程量(全部、定额、设备、主材、人工、材料、机械)
+输出人材机编码
+报表中输出设备编码
+输出取费表名
+显示消耗量
+设置工程地址
+清空数量(全部、定额、设备、主材、人工、材料、机械、土方)
+计算过程转为数值
+批量按系数上浮或下调物料的市场价
+设置工程量不取费
+复制数据
+显示典型材机
+复制行数据
+添加空白行
+批量设置物料(主材、设备)
+查询预规
+批量设置土方
+导入Excel工程量
+设置技经参数
+设置甲供设备计算卸车保管费
+设置甲供材计算卸车保管费
+设置暂列金额
+新增取费表
+设置线路亘长
+设置价差预备费
+设置设备计算运杂费
+批量设置一笔性费用
+批量替换(主材、设备、定额、人工、材料、机械)
+添加、删除批注
+脚手架搭拆费
+如何移动项目划分层级
+添加颜色标记
+添加一笔性费用
+查看配合比、取消拆分勾选设置商混
+更新土质
+查看定额章节说明
+设置设备计算配送费
+设置编制说明
diff --git a/data/wiki_data/配网计价通D3软件.txt b/data/wiki_data/配网计价通D3软件.txt
new file mode 100644
index 0000000..8b3804d
--- /dev/null
+++ b/data/wiki_data/配网计价通D3软件.txt
@@ -0,0 +1,590 @@
+老版定额升级
+导入Excel(表三格式)
+统计土方定额
+删除所有“复”标记
+复用清单组价
+批次工程物料价格联动
+粘贴触发重排序
+切换到报表界面是否自动统计工程量
+统计技经指标
+多工程批量调价
+甲供材料设备计入总价
+甲供材料设备是否计入总价
+调试费
+定额计价核对清单计价
+设置计算施工方相关费用、施工费
+只开展竣工结算审核、施工过程造价咨询及竣工结算审核费
+占地类型
+工程转换:预算转清单、清单转预算
+总算表、表一以元为单位显示
+综合地形增加费、批次工程计算综合地形增加费
+勘察费、专项勘察费、其中勘察费
+工程审核
+工程量小数位数
+物料单价小数位数
+批量设置小数位数
+设置报表参数
+刷新、保存物料
+技经参数
+施工方相关费用、施工费
+统计工地运输定额、地材运距
+建设预算性质、基本预备费
+批次工程查看统计运输定额
+多工程报表导出-显示导入的文件不符
+锁定清单属性
+导入工程量清单
+多工程批量设置——导出定额汇总表
+多工程批量设置-导出物料表
+其他费用界面勾选输出
+多工程批量设置—报表输出
+多工程批量设置-湖北标准化管理系统
+多工程批量导出南网规约接口
+双面打印设置短边翻页、长边翻页
+多工程批量设置-导出设计接口
+升级16版定额组合件库
+批量升级配网造价2017工程
+多工程批量设置-物料价格联动
+批次工程录入物料时是否录入物料本身
+录入组合件时土方参数是否按工程设置刷新
+导入Excel(物料表格式)
+导出自定义组合件库
+导入自定义组合件库
+导入云物料
+录入设备时,是否刷新运杂费费率
+导入Excel库、物料表格式是否录入数量为0的物料
+录入主材时,是否根据运输类型更新损耗率及包装系数
+默认设备供给方
+弹出定额系数调整
+默认主材供给方
+录入含未计价材料定额时,是否同时录入未计价材料
+基本信息页签参数介绍
+配网工程计价通D3软件插件介绍
+南网规约上传常见问题
+甲供材不计入本体参与其他费用取费
+甲供材不计入本体且不参与其他费用取费
+多工程批量设置—显示价格为0的物料
+进入在线客服
+搜索问题
+博微造价软件产品简介
+博微系列软件文件后缀汇总
+规模参数页签操作介绍
+设置工程地形比例
+界面主要作用介绍
+设置拆除工程的地形比例
+设置运输地形比例
+建设场地征用及清理费的费用构成
+不同材料类别设置不同运距
+添加自定义定额、主材、设备资源
+设置土质比例
+按调差文件更新机械市场价
+查看边坡系数
+设置人工、安装材料和安装机械按市场价调差
+设置工程调差
+脚手架搭拆费计算规则
+套用线路参数模板、套用调差系数模板、套用技经参数模板、套用规模参数模板
+工程设置密码、工程加密
+工程体检、检查工程完整性
+新建工程、创建工程
+打开工程
+关闭工程
+关闭全部工程
+保存工程
+保存全部工程
+另存工程
+注册软件锁、添加软件锁许可证
+管理工程模板
+合并相同消耗量
+按编号、名称、规格型号、单位、首字母查找消耗量
+同步定位工程量
+批量替换主材、设备、定额、人材机
+批量设置主材
+设置报表生成
+回存组合件
+添加一笔性费用
+添加组合件
+添加注解
+手动生成报表
+删除工程量(全部、定额、设备、主材)
+设置表二汇总级别
+导入设计接口创建多工程
+导入设计接口创建单工程
+刷新材机市场价
+保存材机市场价
+表二、表三显示拆除
+导入设计接口创建批次工程
+自动保存工程
+保存物料
+显示典型材机
+表三工程量合并排序
+查看物料统计
+显示表三取费表
+统计分析-查看费用统、物料统计
+自动保存工程间隔时间
+查找工程备份数据
+管理软件插件
+查看帮助内容说明
+查看软件更新说明
+检测软件最新版本
+查看软件基础信息
+设置主材、设备、材料增值税率,换算含税、不含税价
+设置主材、设备市场价系数,上浮或下调市场价
+设置主材、设备类别、运输类型
+云端备份
+云物料录入、录入其他省份的物料信息价
+物料库下载
+编辑组合价、定额、主材、设备、一笔性费用的计算式
+物料批量询价、批量查询物料信息价
+编辑组合价、定额、主材、设备、一笔性费用的批注
+统计分析-设置页眉页脚
+报表设计
+费用模板恢复初始
+统计分析-设置起始页
+多工程批量设置-选择工程
+多工程批量设置-查看工程
+清空新建时间、封面不显示新建时间
+工程概况
+报表生成方式
+清除计算过程
+主材设备互转
+隐藏空项目划分
+批量设置设备
+批量设置定额
+批量设置混凝土
+批量设置工程量的所属项目划分
+设置关联父级量、设置组合件和消耗量父子级关系
+设置颜色标记
+取消关联父级量、取消组合件和消耗量父子级关系
+解除密码、工程无法编辑
+显示全部项目划分
+设置主材、材料计算配送费
+设置主材损耗率
+设置主材包装系数
+批次工程设置包装系数
+备份管理
+统计分析-批量取消、恢复显示页码
+统计分析-批量调整行高
+统计分析-批量调整字体
+统计分析-批量导出报表
+统计分析-导入表格式
+批次工程:导出南网规约接口
+删除颜色标记、删除所有颜色
+添加特殊符号
+自定义物料库导出excel
+新建物料库
+删除自定义物料库
+设置报表显示价格为0的物料
+导入自定义物料库
+设置报表显示金额为0的费用项
+导出自定义物料库
+设置报表是否显示物料编号
+设置主材、设备汇总表汇总级别
+批量设置报表小数位数
+批量设置报表正文字体及大小
+批量设置报表页眉页脚副标题文本
+导入、清除电子徽标
+添加自由报表
+删除自定义报表
+显示表三主材
+显示表三设备
+设置表三显示计算式
+设置表三显示定额调整系数
+设置表三显示价格为0的独立费
+统计分析-修改工程路径
+统计分析-查看已读取、未读取工程
+统计分析-批量设置页面页脚
+统计分析-设置报表汇总方式
+箭头移动、设置主材和定额父子级关系
+数据筛选—组、消、全、展现层级
+回存自定义定额资源
+退出软件
+回存物料资源
+录入组合件资源
+定额库添加补充定额
+人材机库添加自定义人材机
+物料库添加物料
+自定义物料库导入excel
+录入定额、物料资源
+统计分析-设置报表生成
+材机分析更改主材、材料、设备的供货方
+新建组合件库
+删除自定义组合件库
+报表导出到同一文件、导出到不同文件
+批量导出excel
+批量导出pdf
+设置导出报表的数据格式(数值、文本)
+导出PDF报表时设置起始页码
+云端备份文件查看、管理云端备份文件
+以链接形式分享工程文件
+统计钢筋量、统计钢筋费
+设计组件
+多工程批量设置——基本信息
+清空数量(全部、定额、主材、设备)
+智电课堂
+邮件分享工程
+组合件库刷新物料
+编辑取费表(粘贴、剪切、复制、删除、添加子级、箭头移动等功能)
+多工程批量设置-土质比例
+多工程批量设置工程地形
+多工程批量设置运输参数
+多工程批量设置调差系数
+多工程批量导入、导出调差模板
+批次工程:生成小批次
+更新土质比例
+删除工程量(数量为空消耗量)
+删除工程量(自动统计消耗量)
+费用预览、查看取费过程
+删除工程量(非自动统计土方、运输定额)
+批量导出报表设置全选、反选
+删除批注、红色小三角
+工程费用修改计算式
+取费表修改计算式
+粘贴excel
+批次工程:设置只开展竣工结算审核
+批次工程设置类别、运输类型
+批次工程:运输设置
+导入Excel(EXCEL库)
+设置计算价差预备费
+多工程批量设置-查看边坡系数
+多工程批量设置-查看价差文件
+批次工程设置密码、工程加密
+批次工程:添加或删除工程
+批次工程:导入、导出工程概况
+取费表查询预规说明
+批次工程设置卸车
+其他费用一键清空费率
+设置一键调价
+费用模板设为默认
+批次工程设置甲供、乙供材料配送费费率
+批次工程设置标记
+批次工程显示典型材机
+批次工程:导出工程文件
+批次工程设置损耗率
+取费表修改费率
+导出南网规约接口
+放大、缩小报表
+导入、导出规模参数
+导出配网设计接口
+批量导出报表设置文件名输出工程编号
+批量打印窗口上移、下移报表位置
+批量打印设置打印份数
+批量打印设置起始页号
+导入、导出取费表模板
+取费设置-工程体检
+多工程报表批量导出、批量打印
+添加、删除取费表
+取费表添加子项费用
+工程量合并
+多工程批量设置—表二汇总级别
+多工程批量设置—表二、表三不显示拆除
+云工程备份
+批量打印报表
+批量打印报表设置单面打印
+批量打印报表设置连续双面打印
+批量打印报表设置单表双面打印
+批量打印报表设置全选、反选
+导入、导出报表模版
+批次工程:查看边坡系数
+批次工程设置保管
+批次工程设置配送
+批次工程:查看土质比例说明
+批次工程修改工程名称
+导入、导出技经参数
+其他费用修改费率
+批次工程:云端备份
+批次工程基本设计费、设计费
+其他费用剪切、复制、粘贴、删除
+总算表剪切、复制、粘贴、删除
+总算表上移、下移、左移、右移
+总算表导入模板、导出模板
+批次工程设置增值税率
+批次工程设置市场价系数
+批次工程添加定额
+批次工程铁附件价格换算
+批次工程:导入、导出报表模板
+批次工程:导入、导出成果输出界面模板
+批次工程:导出物料列表
+批次工程设置甲供、乙供设备运杂费率
+批次工程:导入电子徽标
+总算表调整费用层级
+多工程批量设置——线路参数
+导出配网全过程平台接口
+总算表添加子级
+导出配网标准化管理系统接口
+导出安徽配网结算模块接口
+设置起始页
+设置页眉页脚
+多工程批量设置-技经参数
+多工程批量设置-清空工程时间
+总算表修改计算式
+批次工程:工程概况工程信息填写
+批次工程设置所属项目划分
+批次工程添加主材
+批次工程:工程信息页签参数介绍
+批次工程设置线路运距
+批次工程设置甲供、乙供设备配送费率
+批次工程添加设备
+批次工程添加一笔性费用
+批次工程添加注解
+批次工程添加线路土方
+批次工程查看价差文件
+批次工程设置工程税率
+批次工程:导出配网设计接口
+批次工程设置社会保险费费率
+总算表设置计算建贷利息
+导入、导出设备购置费、其他费用、建设场地征用及清理费、总算表模板
+多工程批量设置-导入、导出线路参数模板
+多工程批量设置-查看预规文件
+多工程批量设置-导入、导出取费表模板
+设置单表行高
+设置单表页面(方向、页边距、纸张大小)
+打印单张报表
+导出单张报表
+多工程批量设置-修改计算式
+多工程批量设置-修改费率
+多工程批量设置-清除模板
+批次工程:配网标准化管理系统接口
+批次工程设置住房公积金费率
+批次工程设置甲供材料卸车费费率
+批次工程更新机械市场价
+批次工程设置甲供材料保管费费率
+批次工程:导出报表文件
+批次工程保存材机
+批次工程选择价差模板
+批次工程设置甲供设备卸车费率
+批次工程导入、导出价差模板
+批次工程查询预规
+批次工程刷新材机
+批次工程设置甲供设备保管费费率
+批次工程设置人运、汽运、拖拉机运距
+批次工程设置水超运运距设置
+批次工程施工方相关费用、施工费
+批次工程施工方相关费用其他费用计取明细
+设置价差预备费
+设置建贷利息
+批次工程添加自由表
+自由表添加行、删除行
+自由表添加列、删除列
+批次工程设置土质比例
+批次工程导入、导出取费表模板
+批次工程设置工程地形比例
+批次工程设置运输地形比例
+批次工程设置10kV线路跨越处数
+批次工程设置默认取费表
+批次工程:导出界面数据
+批次工程恢复初始取费表
+批次工程取费表添加子级
+批次工程:导出汇总报表
+批次工程:打印份数
+批次工程:起始页号
+总算表编辑数据、预览数据
+批次工程:成果输出配置列
+批次工程设置低压电力线路、通信线路(处)
+批次工程:导出配网全过程平台接口
+批次工程删除自定义报表
+批次工程:打印设置--单面、双面打印
+自由表合并单元格、取消单元格合并
+批次工程设置经济作物、房屋
+自由表插入图片、删除图片
+恢复市场价
+设置其他费用可抵扣增值税税率
+批次工程上下移动
+多工程批量设置—表三工程量合并排序
+批次工程设置一般公路(处)、高速公路(处)、普通铁路(处)
+批次工程取费表修改计算式
+多工程批量设置-保存物料
+批次工程取费表修改费率
+多工程批量设置-刷新物料
+多工程批量设置-设置工程小数位数
+其他费用查询预规
+多工程批量设置-设置组合件
+多工程批量设置-设置土方
+多工程批量设置-设置定额
+多工程批量设置—显示表三主材
+多工程批量设置—显示表三取费表
+多工程批量设置—显示表三设备
+多工程批量设置—显示表三计算式
+多工程批量设置—表三显示定额调整系数
+多工程批量设置——表三显示价格为0的独立费
+多工程批量设置—显示金额为0的费用项
+多工程批量设置—物料表是否显示物料编号
+批次工程取费表剪切、复制、粘贴、删除
+批次工程导入、导出其他费用模板
+批次工程其他费用恢复初始
+设置字体、表格格式
+批次工程其他费用设为默认
+批次工程计算
+批次工程批量设置页面
+批次工程其他费用添加子级
+批次工程:查看报表
+设置报表生成全选、反选
+批次工程:设置表二汇总级别
+批次工程:表二、表三显示拆除
+批次工程:表三工程量合并排序
+批次工程:显示表三取费表
+批次工程:显示表三主材
+批次工程:显示表三设备
+批次工程:显示表三计算式
+批次工程:显示定额调整系数
+批次工程:显示价格为0的独立费
+材机分析批量替换主材、设备、人工、材料、机械
+批次工程:显示价格为0的物料
+批次工程:显示金额为0的费用项
+批次工程:设置是否显示物料编号
+批次工程:设置主材设备汇总级别
+设置定额叠加
+批次工程:设置材机
+工程勘察只进行一般性定位测量作业
+拆除物返库运输费
+特殊地区
+批注
+批次工程设置带电10kv电力线路
+批次工程设置河流跨越
+导入excel-组合件
+材机修改记录
+批次工程设置建设场地征用及清理费
+水超运
+自由表显示元金额
+批次工程其他费用修改计算式
+地区类型
+自由表显示大写金额
+导出excel物料库
+工程监理费
+甲供设备不计入造价
+生产期可抵扣增值税
+多工程报表导出
+混凝土施工调整费
+建筑超高安装增加费
+批次工程:编制拆除工程
+编制说明工程投资按元显示
+超灌、加灌量
+余土外运
+五金计算
+物料价格联动
+材机反查
+已有运行电缆沟(管)道内敷设降效费
+统计分析设置报表单位为万元
+多工程批量设置—设置主材、设备汇总表汇总级别
+多工程批量设置—导入、导出报表模板
+清空所有项目特征
+调整清单码
+导出工程量清单
+添加自定义清单
+工程转换:清单预算转清单计价、清单计价转清单预算
+专业暂估价
+显示清单计算式
+清单折扣比例
+解除锁定
+结算如何解锁
+清单结算工程:查看中标清单组价
+清单计价按照全费用综合单价模式取费
+材料设备暂估价
+暂列金额
+清单批次:一键调价
+显示物料编号
+显示价格为0的清单
+显示项目划分及清单
+设置设备运杂费率
+铁附件价格换算
+设置设备计算配送费
+配合比、设置商品混凝土
+查看费用统计
+新建批次工程
+设置甲供材料计算卸车保管费
+设置甲供设备计算卸车保管费
+多工程批量设置建筑材机
+批次工程:设置工程小数位数
+批次工程:设置报表小数位数
+费用统计显示施工费
+多工程批量设置-批量替换材料、机械
+多工程批量设置-报表小数位数
+细分高低压侧项目划分
+多工程批量设置-添加自由表
+报表输出工程新建时间年月
+技经参数页签操作介绍
+刷新物料参数
+多工程批量设置-甲供物料计取卸车保管费
+批次工程:工程概况配置列
+批次工程编辑自由表
+批次工程导入、导出自由表模板
+多工程批量设置-供货方、主材损耗、包装系数、运杂费率、卸车、保管、配送、增值税率、市场价系数、铁附件价格换算、主材设备互转、主材类型、运输类型、设备类别
+多工程批量设置出现很多黄色感叹号
+工程量配置列
+批次工程特殊符号
+批次工程设置物料类型
+多工程批量设置-工程量
+多工程批量设置-取费设置、工程费用
+多工程批量设置-线路参数
+导出设计接口文件
+多工程建筑材机设置
+开展环境监理和水土保持监理
+安装软件后软件图标异常、图标白色
+设计技经接口文件(pjte、bwdp)导入哪些参数信息
+土质比例对应规则
+工程信息中点击“土质信息”、其他费用中点击“查询预规”无反应、显示乱码
+双击图标无反应
+中电联【2023】88号文、2022版配网定额执行时间
+借用2016带电作业定额及取费操作步骤
+配网2022版与2016版的差异点
+多工程批量设置浏览不到文件夹
+各地区插件的主要作用
+刷新内蒙清单编码
+合并南网规约
+配网清标常见问题
+启动软件提示“当前缺失BwSystemConfig。dll组件,软件即将退出”
+导出评审规约常见问题
+多工程批量设置-导出物料、定额汇总表
+批量导出工程量清单
+广东配电网建设项目框架招标工程量清单2023年版的常见问题
+已设置虚拟打印机,点击报表输出界面闪退
+打开提示物料库保存失败,工程保存失败、组合件库打开失败
+设置运输距离、设置运距
+基本设计费、设计费
+设置统计编辑性
+设置设备性材料
+批次工程刷新物料
+恢复原定额
+清除统计标记(工地运输定额、土方定额)
+批次工程批量设置-增值税率
+批次工程批量设置-恢复市场价
+批次工程复制、粘贴、剪切、删除物料
+批次工程录入资源
+批次工程物料搜索
+批次工程工程筛选
+批次工程显示工程名称、显示工程序号
+批次工程添加物料
+批次工程批量设置
+批次工程全选、反选
+批次工程过滤
+批次工程批量设置-定额系数
+批次工程批量设置-供货方
+批次工程整理物料
+批次工程物料数据配置列
+批次工程回存物料库
+批次工程复制、粘贴、剪切、删除
+批次工程设置供货方
+批次工程添加、修改物料数量
+余物清理费
+批次工程资源分析
+定额系数调整
+案例
+拆除工程
+编辑定额的材机列表明细
+一般计税转简易计税
+工程编辑过程提示内存不足300M
+当前软件程序不能正常运行文件遭到篡改,可能被病毒感染或破解
+打开软件出现“应用程序无法正常启动。。。”
+材料卸车保管费
+福建专版与通用版本区别、设计标准化平台、审核意见书
+插件管理、全国版和专版切换
+批次工程:配置列、成果输出中体现施工费
+批次工程:工程信息填写
+打开工程提示“识别工程失败,请将工程升级至2。15。0及以上版本”
+批量刷新组合件库中物料价格
+费用统计数据以万元显示
+配网工程计价通D3软件版本更新记录
+物料询价、确认物料信息价
+定额基本信息页签介绍
+老版定额升级、2017工程升级为D3工程
+审核工程、设置审核模式
+设置工程小数位数
+上海资文插件与通用版本的区别
diff --git a/rag2_0/demo/deduplicate_nouns_json.py b/rag2_0/demo/deduplicate_nouns_json.py
index e68f2ef..470d0f9 100755
--- a/rag2_0/demo/deduplicate_nouns_json.py
+++ b/rag2_0/demo/deduplicate_nouns_json.py
@@ -48,7 +48,7 @@ class JsonDeduplicator:
{items}
'''
# 配置LLM
- model_name = os.getenv("LLM_MODEL_NAME", "gpt-3.5-turbo")
+ model_name = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_API_BASE")
llm_params = {"temperature": 0.3, "model": model_name}
diff --git a/rag2_0/demo/extract_wikijs_nouns.py b/rag2_0/demo/extract_wikijs_nouns.py
deleted file mode 100755
index 9591b98..0000000
--- a/rag2_0/demo/extract_wikijs_nouns.py
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-File: extract_wikijs_nouns.py
-Author: oyyz
-Description: 从 Wikijs 文档中提取专业名词
-"""
-
-import os
-from typing import List
-from dotenv import load_dotenv
-from langchain.output_parsers import PydanticOutputParser
-from rag2_0.tool.WikijsTool import WikijsTool
-from rag2_0.intent_recognition.DataModels import Term, TermList
-from rag2_0.tool.html_to_md import convert_html_to_md
-from rag2_0.tool.ModelTool import OpenAiLLM
-import json
-import datetime
-import logging
-import threading
-import concurrent.futures
-from threading import Semaphore
-
-# 加载环境变量
-load_dotenv()
-
-extract_wiki_nouns_prompt="""
-我在完善我的专业词库,请从提供的电力行业造价软件相关文本中提取关键词,要求如下:
-
-一、提取范围
-1. 核心功能模块
-(例:多工程批量计价、材机数据反算、变电工程智能组价、架空线路地形系数计算)
-2、软件功能及界面名称(包括:界面页签、功能按钮、功能名称等)
-(例:新建工程量清单、导出工程量清单等)
-3. 业务专用术语
-(例:装置性材料、甲供材保管费、施工降效补偿、电缆头试验配套费)
-4. 计价标准体系
-(例:预规2020版、电网检修定额2015版、配网工程概算定额)
-
-
-二、提取规则
-1. 识别核心功能名称(如"多工程批量设置工程量、工程设置密码")
-2. 提取业务专用名词(如"主材卸车保管费")
-3. 标注关联术语的对应关系(如"市场价"与"市场价格"互为同义词)
-4. 包含定额标准相关术语(如"预规2020版")
-5. 复合型术语需保持完整
- √ 正确:"地形增加系数批量设置"
- × 错误:"地形"、"系数"、"设置"
-6. 总结生成关键词解释
- 关键词:编制依据
- 描述:造价文件编制基准规范
-
-7. 软件的特定版本号不作为关键词
-
-三、输出格式:
-{output_format}
-
-四、输入内容:
-{content}
-"""
-
-
-class WikijsNounsExtractor:
- """从 Wikijs 文档中提取专业名词"""
-
- def __init__(self, api_key: str = None, base_url: str = None, model_name: str = "gpt-3.5-turbo"):
- """
- 初始化专业名词提取器
-
- Args:
- api_key: API密钥,如果为None则从环境变量获取
- base_url: API基础URL,如果为None则使用默认URL
- model_name: 要使用的模型名称
- """
- # 保存参数
- self.api_key = api_key
- self.base_url = base_url
- self.model_name = model_name
-
- # 初始化LLM
- llm_params = {
- "temperature": 0.6,
- "model": model_name
- }
-
- if api_key:
- llm_params["api_key"] = api_key
-
- if base_url:
- llm_params["base_url"] = base_url
-
- self.llm = OpenAiLLM(**llm_params)
-
- # 准备术语列表解析器
- self.terms_list_parser = PydanticOutputParser(pydantic_object=TermList)
-
- # 信号量,限制并发请求数量
- self.semaphore = None
-
- # 线程锁,用于保护共享资源
- self.lock = threading.Lock()
-
- def _convert_html_to_md(self, content, title):
- """HTML转Markdown"""
- options = {"heading_style": '', "keep_inline_images_in": ["figure", "img"], "escape_asterisks": True}
- new_content = (content.replace("h6>", "h7>")
- .replace("h5>", "h6>")
- .replace("h4>", "h5>")
- .replace("h3>", "h4>")
- .replace("h2>", "h3>")
- .replace("h1>", "h2>"))
- # 将HTML内容转换为Markdown
- markdown_content = convert_html_to_md(new_content, "", **options)
- markdown_content = f"# {title}\n\n{markdown_content}"
- return markdown_content
-
- def extract_from_document(self, doc_info: dict) -> List[Term]:
- """从单个文档中提取专业名词"""
- try:
- # 使用LLM调用处理文档
- content = doc_info['content']
- title = doc_info["title"]
-
- # 转换HTML到Markdown
- markdown_content = self._convert_html_to_md(content, title)
-
- # 准备提示词
- formatted_prompt = extract_wiki_nouns_prompt.replace("{content}", markdown_content)
- formatted_prompt = formatted_prompt.replace("{output_format}", self.terms_list_parser.get_format_instructions())
-
- try:
- # 调用LLM
- response = self.llm.invoke(formatted_prompt)
- # 使用Pydantic解析器解析结果
- parsed_output = self.terms_list_parser.parse(response.content)
- return parsed_output.terms
- except Exception as e:
- logging.error(f"解析LLM响应时出错: {str(e)}", exc_info=True)
- return []
- except Exception as e:
- logging.error(f"提取专业名词时出错: {str(e)}", exc_info=True)
- return []
-
- def _process_document(self, doc, path_terms):
- """处理单个文档"""
- try:
- # 获取信号量
- with self.semaphore:
- # 检查文档路径是否在我们要处理的路径中
- path_prefix = None
- for prefix in path_terms.keys():
- if doc['path'].startswith(prefix):
- path_prefix = prefix
- break
-
- # 如果不在要处理的路径中,则跳过
- if not path_prefix:
- return None
-
- # 获取文档详细信息
- doc_info = WikijsTool.query_doc_info(doc['id'])
- if not doc_info or not doc_info.get('content'):
- return None
-
- # 提取专业名词
- terms = self.extract_from_document(doc_info)
-
- # 将提取的术语添加到对应路径的结果列表中
- terms_dicts = [{"name": term.name, "synonymous": term.synonymous, "description": term.description} for term in terms]
-
- with self.lock:
- path_terms[path_prefix].extend(terms_dicts)
- logging.info(f"文档 {doc['path']} 处理完成,提取了 {len(terms)} 个专业名词")
-
- # 每处理10个文档保存一次中间结果
- current_count = len(path_terms[path_prefix])
- if current_count % 10 == 0:
- # 使用锁保护文件IO
- self._save_terms_to_file(path_terms[path_prefix], os.path.join(self.output_dir, f"{path_prefix.split('(')[0]}_nouns.json"))
- logging.info(f"已处理 {path_prefix} 的文档数达到 {current_count//10*10} 个,已保存中间结果")
-
- return path_prefix
- except Exception as e:
- logging.error(f"处理文档 {doc['path']} 时出错: {str(e)}", exc_info=True)
- return None
-
- def process_all_documents(self, output_dir: str = "extracted_nouns", max_concurrency: int = 5):
- """使用线程池处理所有文档"""
- # 保存输出目录
- self.output_dir = output_dir
-
- # 创建输出目录
- if not os.path.exists(output_dir):
- os.makedirs(output_dir)
-
- # 初始化信号量,限制并发请求数
- self.semaphore = Semaphore(max_concurrency)
-
- # 获取所有文档
- all_docs = WikijsTool.get_all_documents()
-
- # 要处理的路径前缀
- # path_prefixes = [
- # "技改检修计价通(2020)",
- # "西藏造价软件(2023)",
- # "新型储能电站建设计价通C1(2024)",
- # "配网造价软件(2022)",
- # ]
- path_prefixes = [
- "主网电力建设计价通(2018)",
- ]
- # 为每个路径创建单独的结果列表
- path_terms = {prefix: [] for prefix in path_prefixes}
-
- # 过滤出符合路径前缀的文档
- filtered_docs = []
- for doc in all_docs:
- for prefix in path_prefixes:
- if doc['path'].startswith(prefix):
- filtered_docs.append(doc)
- break
-
- logging.info(f"开始使用线程池处理 {len(filtered_docs)} 个文档...")
-
- # 使用线程池处理所有文档
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrency) as executor:
- futures = []
- for doc in filtered_docs:
- future = executor.submit(self._process_document, doc, path_terms)
- futures.append(future)
-
- # 等待所有任务完成
- for i, future in enumerate(concurrent.futures.as_completed(futures)):
- try:
- prefix = future.result()
- if i % 10 == 0:
- logging.info(f"已完成 {i+1}/{len(futures)} 个文档的处理")
- except Exception as e:
- logging.error(f"处理文档时出错: {str(e)}", exc_info=True)
-
- # 保存最终结果
- for prefix, terms in path_terms.items():
- # 为每个路径保存单独的文件
- output_file = os.path.join(output_dir, f"{prefix.split('(')[0]}_nouns.json")
- self._save_terms_to_file(terms, output_file)
- logging.info(f"{prefix} 处理完成,共提取 {len(terms)} 个专业名词,已保存到 {output_file}")
-
- def _save_terms_to_file(self, terms, output_file):
- """保存术语列表到文件"""
- with open(output_file, 'w', encoding='utf-8') as f:
- json.dump(terms, f, ensure_ascii=False, indent=2)
-
-
-def main():
- # 从环境变量获取配置
- api_key = os.getenv("OPENAI_API_KEY")
- base_url = os.getenv("OPENAI_API_BASE")
-
- # os.environ["LLM_MODEL_NAME"] = "Qwen/Qwen2.5-72B-Instruct-128K"
-
- extractor = WikijsNounsExtractor(api_key=api_key, base_url=base_url, model_name=os.getenv("LLM_MODEL_NAME"))
- current_dir = os.path.dirname(os.path.abspath(__file__))
- output_dir = os.path.join(current_dir, "..", "..", "data", "wiki_extracted_nouns")
- extractor.process_all_documents(output_dir=output_dir, max_concurrency=2)
-
-if __name__ == "__main__":
- # 配置日志输出到文件,并设置格式
- current_dir = os.path.dirname(os.path.abspath(__file__))
- log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- date_format = '%Y-%m-%d %H:%M:%S'
-
- # 创建一个控制台处理器
- console_handler = logging.StreamHandler()
- console_handler.setLevel(logging.INFO)
- console_handler.setFormatter(logging.Formatter(log_format, date_format))
-
- # 获取根日志记录器并添加处理器
- root_logger = logging.getLogger()
- root_logger.setLevel(logging.INFO)
- root_logger.addHandler(console_handler)
- main()
\ No newline at end of file
diff --git a/rag2_0/demo/intent_recognition_example.py b/rag2_0/demo/intent_recognition_example.py
index 6255248..b136309 100755
--- a/rag2_0/demo/intent_recognition_example.py
+++ b/rag2_0/demo/intent_recognition_example.py
@@ -75,15 +75,8 @@ class QueryRewriteProcessor:
dify_base_url: Dify API基础URL
"""
# 初始化意图识别器
- self.api_key = api_key or os.getenv("OPENAI_API_KEY")
- self.base_url = base_url or os.getenv("OPENAI_API_BASE")
- self.model_name = model_name or os.getenv("LLM_MODEL_NAME", "gpt-3.5-turbo")
# 使用asyncio.run()运行异步create方法
- self.recognizer_async = asyncio.run(AsyncIntentRecognizer.create(
- api_key=self.api_key,
- base_url=self.base_url,
- model_name=self.model_name
- ))
+ self.recognizer_async = asyncio.run(AsyncIntentRecognizer.create())
self.dify_query_retrieval = DifyQueryRetrieval(api_key=dify_api_key, base_url=dify_base_url)
def is_retrieved_doc_relevant(self, query: str, retrieved_doc: List[Dict[str, Any]]) -> Dict[str, Any]:
@@ -174,7 +167,7 @@ class QueryRewriteProcessor:
return []
def process_query(self, query: str,
- conversation_context: str = "",
+ conversation_context: Dict = None,
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, str] = None,
enable_retrieval: bool = False):
@@ -196,12 +189,17 @@ class QueryRewriteProcessor:
while retry_count <= max_retries:
try:
+ if conversation_context is None:
+ conversation_context = {}
+
+ current_softname = conversation_context.get("current_softname", "")
result = asyncio.run(self.recognizer_async.process_query_async(query,
conversation_context=conversation_context,
chat_history=chat_history,
previous_slots=previous_slots,
enable_query_expansion=True,
- use_jieba=True))
+ use_jieba=True,
+ cur_soft_name=current_softname))
# 提取分类信息
classification = result["classification"]
@@ -414,7 +412,7 @@ def main():
# 从环境变量中获取配置,命令行参数优先
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_API_BASE")
- model_name = os.getenv("LLM_MODEL_NAME", "gpt-3.5-turbo")
+ model_name = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
enable_retrieval = args.enable_retrieval
# 初始化查询改写处理器
@@ -441,8 +439,10 @@ def main():
for idx, query in enumerate(examples):
if query.strip() == "":
continue
- query="811619150828能看一下这个锁是16的马"
- conversation_context="当前使用软件:配网计价通D3软件"
+ query="怎么把一个批次拆分成多个批次工程"
+ conversation_context={
+ "current_softname": "配网计价通D3软件"
+ }
# 在调试模式下使用完整的参数
print(json.dumps(processor.process_query(
query,
diff --git a/rag2_0/demo/merge_nouns_with_llm.py b/rag2_0/demo/merge_nouns_with_llm.py
index 84d9d6a..603779e 100755
--- a/rag2_0/demo/merge_nouns_with_llm.py
+++ b/rag2_0/demo/merge_nouns_with_llm.py
@@ -44,7 +44,7 @@ class TermMerger:
{items}
'''
# 配置LLM
- model_name = os.getenv("LLM_MODEL_NAME", "gpt-3.5-turbo")
+ model_name = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_API_BASE")
llm_params = {"temperature": 0.3, "model": model_name}
diff --git a/rag2_0/demo/validate_excel_data_batch.py b/rag2_0/demo/validate_excel_data_batch.py
deleted file mode 100755
index ba60d0a..0000000
--- a/rag2_0/demo/validate_excel_data_batch.py
+++ /dev/null
@@ -1,573 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-File: validate_excel_data_batch.py
-Description: 使用LLM批量验证Excel数据中的问题分类、问题拆解、检索关键词和问题改写是否正确
-"""
-
-import os
-import sys
-import pandas as pd
-import json
-import argparse
-import logging
-import concurrent.futures
-from tqdm import tqdm
-from dotenv import load_dotenv
-from langchain_openai import ChatOpenAI
-from pydantic import BaseModel, Field
-from langchain.output_parsers import PydanticOutputParser
-
-sys.path.append(os.getcwd())
-from rag2_0.intent_recognition.PromptTemplates import classification_info
-from rag2_0.intent_recognition.DataModels import *
-from rag2_0.tool.ModelTool import OpenAiLLM
-
-
-# 定义验证结果的Pydantic模型
-class ValidationResult(BaseModel):
- is_correct: bool = Field(description="验证是否通过")
- confidence_score: float = Field(description="置信度得分")
- reason: str = Field(default="", description="得出结论的原因")
-
-class ExcelDataValidator:
- """Excel数据验证类,用于批量验证Excel数据中的问题分类、问题拆解、检索关键词和问题改写"""
-
- def __init__(self, input_file=None, output_file=None, workers=4, debug=False):
- """
- 初始化验证器
-
- Args:
- input_file: 输入Excel文件路径
- output_file: 输出结果Excel文件路径
- workers: 并行工作线程数
- debug: 是否启用调试模式(串行处理)
- """
- # 加载环境变量
- load_dotenv()
-
- self.input_file = input_file
- self.output_file = output_file
- self.workers = workers
- self.debug = debug
- self.df = None
-
- # 设置日志
- self.setup_logging()
-
- def setup_logging(self):
- """配置日志输出"""
- logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- handlers=[
- logging.StreamHandler()
- ]
- )
- logging.getLogger('httpx').setLevel(logging.WARNING)
- logging.getLogger('openai').setLevel(logging.WARNING)
-
- def load_data_from_excel(self, file_path=None):
- """
- 从Excel文件中读取数据
-
- Args:
- file_path: Excel文件路径,如不提供则使用初始化时的路径
-
- Returns:
- DataFrame对象
- """
- file_path = file_path or self.input_file
- if not file_path:
- logging.error("未指定输入文件路径", exc_info=True)
- return None
-
- try:
- df = pd.read_excel(file_path)
- required_columns = ["问题", "问题分类", "问题改写", "槽位信息", "检索的内容"]
- for col in required_columns:
- if col not in df.columns:
- logging.error(f"缺少必要的列: {col}", exc_info=True)
- return None
- logging.info(f"成功从{file_path}读取了{len(df)}条数据")
- self.df = df
- return df
- except Exception as e:
- logging.error(f"读取Excel文件时出错: {e}", exc_info=True)
- return None
-
- def validate_classification(self, llm:OpenAiLLM , query:str, vertical_class:str, sub_class:str):
- """
- 验证问题分类是否正确
-
- Args:
- llm: LLM模型
- query: 原始问题
- vertical_class: 一级分类
- sub_class: 二级分类
-
- Returns:
- (bool, str, float): 是否正确,错误原因(如果有),置信度
- """
- parser = self.create_validation_parser()
- format_instructions = parser.get_format_instructions()
-
- prompt = f"""
- 背景:用户正在使用电力造价软件,提出的问题可能涉及电力造价软件的使用,也可能涉及电力造价专业知识。我对用户问题进行了分类,请评估以下问题分类是否正确。
-
-我目前总共有以下分类:
-{classification_info}
-
-问题的分类情况如下:
-原始问题: {query}
-一级分类: {vertical_class}
-二级分类: {sub_class}
-
-请从专业角度分析这个分类是否准确,并以JSON格式返回结果。请提供一个0到1之间的置信度得分,表示你对判断的确信程度。
-
-{format_instructions}
-"""
-
- try:
- response = llm.invoke(prompt)
- result = parser.parse(response.content)
- return result.is_correct, result.reason, result.confidence_score
- except Exception as e:
- logging.warning(f"验证问题分类时出错: {e}")
- return False, f"验证过程出错: {str(e)}", 0.0
-
- def _get_slot_model(self, classification: Classification) -> Optional[type]:
- """
- 根据分类结果获取对应的槽位模型类,用于统一提示词处理
-
- Args:
- classification: 意图分类结果
-
- Returns:
- 对应的槽位模型类
- """
- # 软件问题
- if classification.vertical_classification == "软件问题":
- if classification.sub_classification == "软件功能":
- return SoftwareFunctionSlots
- elif classification.sub_classification == "故障排查":
- return SoftwareTroubleShootingSlots
-
- # 业务问题
- elif classification.vertical_classification == "业务问题":
- if classification.sub_classification == "专业咨询":
- return ProfessionalConsultingSlots
- elif classification.sub_classification == "数据问题":
- return DataProblemSlots
-
- # 安装下载注册
- elif classification.vertical_classification == "安装下载注册":
- if classification.sub_classification == "后缀名咨询":
- return FileExtensionConsultingSlots
- elif classification.sub_classification == "软件锁类":
- return SoftwareLockSlots
- elif classification.sub_classification == "安装下载类":
- return InstallationDownloadSlots
- elif classification.sub_classification == "问题排查类":
- return ProblemDiagnosisSlots
-
- # 其他
- elif classification.vertical_classification == "其他":
- return OtherSlots
-
- return None
-
- def validate_slot(self, llm, rewrite, slot_info, vertical_class, sub_class):
- """
- 验证槽位填充是否正确
-
- Args:
- llm: LLM模型
- rewrite: 问题改写
- slot_info: 槽位信息(JSON字符串)
-
- Returns:
- (bool, str, float): 是否正确,错误原因(如果有),置信度
- """
- # 解析槽位信息JSON
- try:
- if isinstance(slot_info, str) and slot_info.strip():
- slots = json.loads(slot_info)
- else:
- slots = slot_info
- except:
- slots = slot_info
-
- parser = self.create_validation_parser()
- format_instructions = parser.get_format_instructions()
- slot_info_prompt = self._get_slot_model(Classification(vertical_classification=vertical_class, sub_classification=sub_class)).model_json_schema()
- slot_info_prompt = json.dumps(slot_info_prompt, ensure_ascii=False)
- prompt = f"""
- 背景:用户正在使用电力造价软件,提出的问题可能涉及电力造价软件的使用帮助,也可能涉及电力造价专业知识。我从用户问题中提取了槽位信息,请评估这些槽位信息是否准确、完整。
-
-问题改写: {rewrite}
-槽位模板:{slot_info_prompt}
-
-填充的槽位信息: {slots}
-
-槽位信息应该准确提取问题中的关键实体和属性,如软件名称、功能名称、错误信息等。请分析这些槽位是否准确填充,并以JSON格式返回结果。请提供一个0到1之间的置信度得分,表示你对判断的确信程度。
-
-{format_instructions}
-"""
-
- try:
- response = llm.invoke(prompt)
- result = parser.parse(response.content)
- return result.is_correct, result.reason, result.confidence_score
- except Exception as e:
- logging.warning(f"验证槽位填充时出错: {e}")
- return False, f"验证过程出错: {str(e)}", 0.0
-
- def validate_retrieve_content(self, llm, rewrite, retrieve_content):
- """
- 验证检索内容是否正确
-
- Args:
- llm: LLM模型
- rewrite: 问题改写
- retrieve_content: 检索内容(可能是JSON字符串或文本)
-
- Returns:
- (bool, str, float): 是否正确,错误原因(如果有),置信度
- """
- # 解析检索内容
- try:
- if isinstance(retrieve_content, str) and retrieve_content.strip():
- if retrieve_content.startswith('{') or retrieve_content.startswith('['):
- content = json.loads(retrieve_content)
- else:
- content = retrieve_content
- else:
- content = retrieve_content
- except:
- content = retrieve_content
-
- parser = self.create_validation_parser()
- format_instructions = parser.get_format_instructions()
-
- prompt = f"""
- 背景:用户正在使用电力造价软件,提出的问题可能涉及电力造价软件的使用帮助,也可能涉及电力造价专业知识。我针对用户问题检索了相关内容,请评估这些检索内容是否能解答提问。
-
-问题改写: {rewrite}
-检索内容: {content}
-
-检索内容应该与问题主题相关,能够提供有用的信息来回答问题。请分析检索内容是否能解答提问、准确,并以JSON格式返回结果。请提供一个0到1之间的置信度得分,表示你对判断的确信程度。
-
-{format_instructions}
-"""
-
- try:
- response = llm.invoke(prompt)
- result = parser.parse(response.content)
- return result.is_correct, result.reason, result.confidence_score
- except Exception as e:
- logging.warning(f"验证检索内容时出错: {e}")
- return False, f"验证过程出错: {str(e)}", 0.0
-
- def validate_rewrite(self, llm, query, rewrite):
- """
- 验证问题改写是否正确
-
- Args:
- llm: LLM模型
- query: 原始问题
- rewrite: 问题改写
-
- Returns:
- (bool, str, float): 是否正确,错误原因(如果有),置信度
- """
- parser = self.create_validation_parser()
- format_instructions = parser.get_format_instructions()
-
- prompt = f"""
- 背景:用户正在使用电力造价软件,提出的问题可能涉及电力造价软件的使用帮助,也可能涉及电力造价专业知识。我对用户问题进行了改写,请评估以下问题改写是否正确。
-
-原始问题: {query}
-问题改写: {rewrite}
-
-问题改写应该保持原问题的核心意图,同时使表达更加清晰、完整。请分析改写是否准确,并以JSON格式返回结果。请提供一个0到1之间的置信度得分,表示你对判断的确信程度。
-
-{format_instructions}
-"""
-
- try:
- response = llm.invoke(prompt)
- result = parser.parse(response.content)
- return result.is_correct, result.reason, result.confidence_score
- except Exception as e:
- logging.warning(f"验证问题改写时出错: {e}")
- return False, f"验证过程出错: {str(e)}", 0.0
-
- def validate_row(self, llm, row_data):
- """
- 按顺序验证一行数据中的各个环节
-
- Args:
- llm: LLM模型
- row_data: (index, row)元组
-
- Returns:
- (index, is_all_correct, error_phase, error_reason, confidence_score): 行索引,是否全部正确,错误环节,错误原因,置信度
- """
- index, row = row_data
- query = row["问题"]
- query_class = row.get("问题分类", "")
- rewrite = row.get("问题改写", "")
- slot_info = row.get("槽位信息", "")
- retrieve_content = row.get("检索的内容", "")
-
- if self.debug:
- logging.info(f"开始验证行 {index}:")
- logging.info(f" 问题: {query}")
- logging.info(f" 问题分类: {query_class}")
- logging.info(f" 问题改写: {rewrite}")
-
- try:
-
- confidence_score = 0.0
- # 1. 验证问题改写
- if rewrite:
- if self.debug:
- logging.info(f" 验证问题改写...")
-
- result = self.validate_rewrite(llm, query, rewrite)
- if isinstance(result, tuple) and len(result) >= 3:
- is_correct, error_reason, rewrite_confidence = result[:3]
- confidence_score = max(confidence_score, rewrite_confidence)
-
- if self.debug:
- logging.info(f" 问题改写验证结果: {'通过' if is_correct else '不通过'}, 置信度: {rewrite_confidence:.2f}")
- if not is_correct:
- logging.info(f" 错误原因: {error_reason}")
-
- if not is_correct:
- return index, False, "问题改写", error_reason, rewrite_confidence
-
- # 2. 验证问题分类
- if query_class:
- if self.debug:
- logging.info(f" 验证问题分类...")
-
- query_class_list = query_class.split(" - ")
- if len(query_class_list) >= 2:
- result = self.validate_classification(llm, rewrite, query_class_list[0], query_class_list[1])
- if isinstance(result, tuple) and len(result) >= 3:
- is_correct, error_reason, classification_confidence = result[:3]
- confidence_score = max(confidence_score, classification_confidence)
-
- if self.debug:
- logging.info(f" 问题分类验证结果: {'通过' if is_correct else '不通过'}, 置信度: {classification_confidence:.2f}")
- if not is_correct:
- logging.info(f" 错误原因: {error_reason}")
-
- if not is_correct:
- return index, False, "问题分类", error_reason, classification_confidence
-
-
-
- # 3. 验证槽位填充
- if slot_info:
- if self.debug:
- logging.info(f" 验证槽位填充...")
-
- result = self.validate_slot(llm, rewrite, slot_info, query_class_list[0], query_class_list[1])
- if isinstance(result, tuple) and len(result) >= 3:
- is_correct, error_reason, slot_confidence = result[:3]
- confidence_score = max(confidence_score, slot_confidence)
-
- if self.debug:
- logging.info(f" 槽位填充验证结果: {'通过' if is_correct else '不通过'}, 置信度: {slot_confidence:.2f}")
- if not is_correct:
- logging.info(f" 错误原因: {error_reason}")
-
- if not is_correct:
- return index, False, "槽位填充", error_reason, slot_confidence
-
- # 4. 验证检索内容
- if retrieve_content and retrieve_content != "" and pd.notna(retrieve_content):
- if self.debug:
- logging.info(f" 验证检索内容...")
-
- result = self.validate_retrieve_content(llm, query, retrieve_content)
- if isinstance(result, tuple) and len(result) >= 3:
- is_correct, error_reason, retrieve_confidence = result[:3]
- confidence_score = max(confidence_score, retrieve_confidence)
-
- if self.debug:
- logging.info(f" 检索内容验证结果: {'通过' if is_correct else '不通过'}, 置信度: {retrieve_confidence:.2f}")
- if not is_correct:
- logging.info(f" 错误原因: {error_reason}")
-
- if not is_correct:
- return index, False, "检索内容", error_reason, retrieve_confidence
-
- if self.debug:
- logging.info(f" 行 {index} 验证完成: 通过, 总置信度: {confidence_score:.2f}")
-
- return index, True, "", "", confidence_score
- except Exception as e:
- error_msg = f"处理行 {index} 时发生错误: {str(e)}"
- logging.error(error_msg, exc_info=True)
- return index, False, "处理错误", error_msg, 0.0
-
- def create_llm_instances(self, count):
- """创建多个LLM实例"""
- api_key = os.getenv("OPENAI_API_KEY")
- base_url = os.getenv("OPENAI_API_BASE")
- model_name = "deepseek-ai/DeepSeek-R1"
-
- llm_params = {"temperature": 0.7, "model": model_name}
- if api_key:
- llm_params["api_key"] = api_key
- if base_url:
- llm_params["base_url"] = base_url
-
- return [OpenAiLLM(**llm_params) for _ in range(count)]
-
- def validate(self, input_file=None, output_file=None, workers=None, debug=None):
- """
- 执行验证过程
-
- Args:
- input_file: 输入Excel文件路径
- output_file: 输出结果Excel文件路径
- workers: 并行工作线程数
- batch_size: 每批处理的行数(已弃用,保留参数保持兼容)
- debug: 是否启用调试模式(串行处理)
-
- Returns:
- 验证后的DataFrame
- """
- input_file = input_file or self.input_file
- output_file = output_file or self.output_file
- workers = workers or self.workers
- debug = debug if debug is not None else self.debug
-
- # 读取数据
- df = self.load_data_from_excel(input_file)
- if df is None:
- return None
-
- # 添加验证结果列
- df["验证结果"] = ""
- df["错误环节"] = ""
- df["错误原因"] = ""
- df["置信度"] = 0.0
-
- # 准备数据
- all_rows = list(df.iterrows())
-
- # 创建LLM实例
- llm = self.create_llm_instances(1)[0]
-
- # 根据模式选择处理方式
- all_results = []
- if debug:
- # 调试模式:串行处理
- logging.info("启用调试模式,使用串行处理...")
- for i, row_data in enumerate(all_rows):
- logging.info(f"处理第 {i+1}/{len(all_rows)} 行...")
- result = self.validate_row(llm, row_data)
- all_results.append(result)
- # 实时更新DataFrame
- index, is_correct, error_phase, error_reason, confidence_score = result
- df.at[index, "验证结果"] = "通过" if is_correct else "不通过"
- df.at[index, "错误环节"] = error_phase
- df.at[index, "错误原因"] = error_reason
- df.at[index, "置信度"] = confidence_score
- # 输出当前结果
- logging.info(f"行 {index} 验证结果: {'通过' if is_correct else '不通过'}, 错误环节: {error_phase}, 错误原因: {error_reason}, 置信度: {confidence_score:.2f}")
- else:
- # 正常模式:并行处理,每行单独处理
- llm_instances = self.create_llm_instances(min(workers, len(all_rows)))
-
- with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
- # 为每行分配一个LLM实例
- future_to_row = {
- executor.submit(self.validate_row, llm_instances[i % len(llm_instances)], row_data):
- i for i, row_data in enumerate(all_rows)
- }
-
- # 使用tqdm显示进度条
- for future in tqdm(concurrent.futures.as_completed(future_to_row), total=len(all_rows), desc="处理进度"):
- result = future.result()
- all_results.append(result)
-
- # 按行索引排序结果,确保与原始数据顺序一致
- all_results.sort(key=lambda x: x[0])
-
- # 将结果填充到DataFrame
- for result in all_results:
- if len(result) >= 5:
- index, is_correct, error_phase, error_reason, confidence_score = result
- df.at[index, "验证结果"] = "通过" if is_correct else "不通过"
- df.at[index, "错误环节"] = error_phase
- df.at[index, "错误原因"] = error_reason
- df.at[index, "置信度"] = confidence_score
- else:
- index, is_correct, error_phase, error_reason = result
- df.at[index, "验证结果"] = "通过" if is_correct else "不通过"
- df.at[index, "错误环节"] = error_phase
- df.at[index, "错误原因"] = error_reason
-
- # 保存结果
- if output_file is None:
- output_file = os.path.join(
- os.path.dirname(input_file),
- f"validated_{os.path.basename(input_file)}"
- )
- df.to_excel(output_file, index=False)
- logging.info(f"验证完成,结果已保存至: {output_file}")
-
- # 输出统计信息
- self.print_statistics(df)
-
- return df
-
- def print_statistics(self, df):
- """打印统计信息"""
- total = len(df)
- passed = len(df[df["验证结果"] == "通过"])
- error_stats = df[df["验证结果"] == "不通过"]["错误环节"].value_counts()
-
- logging.info(f"统计信息: 总计 {total} 条, 通过 {passed} 条, 通过率 {passed/total*100:.2f}%")
- logging.info("错误环节统计:")
- for phase, count in error_stats.items():
- logging.info(f"- {phase}: {count} 条")
-
- def create_validation_parser(self):
- """创建验证结果解析器"""
- return PydanticOutputParser(pydantic_object=ValidationResult)
-
-
-def main():
- """主函数"""
- # 解析命令行参数
- input_excel = os.path.join(os.path.dirname(__file__), "..", "..", "data", "excel", "1500条点踩软件问题测试_意图分类.xlsx")
- output_excel = os.path.join(os.path.dirname(__file__), "..", "..", "data", "excel", "自动验证_问题分类重写结果.xlsx")
-
- parser = argparse.ArgumentParser(description="验证Excel数据中的问题分类、问题拆解、检索关键词和问题改写")
- parser.add_argument("--input", "-i", type=str, help="输入Excel文件路径", default=input_excel)
- parser.add_argument("--output", "-o", type=str, help="输出结果Excel文件路径", default=output_excel)
- parser.add_argument("--workers", "-w", type=int, default=20, help="并行工作线程数")
- args = parser.parse_args()
- logging.info(f"输入文件路径: {args.input}, 输出文件路径: {args.output}, 并行工作线程数: {args.workers}")
- is_debug = hasattr(sys, 'gettrace') and sys.gettrace() is not None
-
- # 创建验证器实例并执行验证
- validator = ExcelDataValidator(
- input_file=args.input,
- output_file=args.output,
- workers=args.workers,
- debug=is_debug
- )
- validator.validate()
-
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/rag2_0/dify/AnalysisDifyAnswer.py b/rag2_0/dify/AnalysisDifyAnswer.py
index 6174634..f30f78c 100644
--- a/rag2_0/dify/AnalysisDifyAnswer.py
+++ b/rag2_0/dify/AnalysisDifyAnswer.py
@@ -3,19 +3,15 @@ import json
from regex import search
-import ijson
+import sys
+import os
+sys.path.append(os.getcwd())
+from rag2_0.dify.dify_tool import DifyTool
-df = pd.read_excel("data/excel/已分析数据汇总(第一轮).xlsx")
-df=df[df["评价"]=="dislike"]
+dify_tool = DifyTool()
+
+df = pd.read_excel("data/excel/0714提问数据汇总(已分析)_软件.xlsx")
-msg_id_list = df["msg_id"].tolist()
-msg_debug_list = {}
-# 流式解析 JSON 数组
-with open("data/excel/msg_debug_list.json", "r", encoding="utf-8") as f:
- # 使用ijson.items直接获取顶层键值对
- for msg_id, data in ijson.kvitems(f, ''):
- if msg_id in msg_id_list:
- msg_debug_list[msg_id] = data
def get_rewrite_query(intent_node_execution_info)->str:
outputs_result =json.loads(intent_node_execution_info['outputs'])
@@ -28,7 +24,7 @@ def judge_error_node_and_reason(intent_node_execution_info, knowledge_filter_nod
outputs_result =json.loads(intent_node_execution_info['outputs'])
result["问题改写结果"] = outputs_result['optimize_query']
- if outputs_result['is_complete'] == False:
+ if outputs_result['is_complete'] == False and outputs_result["has_slot_filling"] == True:
result["错误环节"] = "槽点填充"
result["错误原因"] = f"槽点缺失"
result["具体描述"] = f"缺失内容:{outputs_result['missing_slots']}"
@@ -80,6 +76,8 @@ for index, row in df.iterrows():
answer = row["回答"]
query = row["提问"]
rating = row["评价"]
+ if rating != "dislike":
+ continue
class_type = row["问题分类"]
dislike_reason = row["点踩原因"]
if dislike_reason is None or pd.isna(dislike_reason):
@@ -87,7 +85,8 @@ for index, row in df.iterrows():
answer_wiki_name = row["关联词条"]
search_wiki = row["检索到的词条"]
- node_executions_info = msg_debug_list[msg_id]
+ msg_debug_info = dify_tool.get_message_debug_info_by_id(msg_id)
+ node_executions_info = msg_debug_info["workflow_node_executions_info"]
intent_node_execution_info = [node_execution_info for node_execution_info in node_executions_info
if node_execution_info["title"] == "意图识别结果解析"]
@@ -109,7 +108,7 @@ for index, row in df.iterrows():
print(f"msg_id: {msg_id} 处理失败: {e}")
continue
-df.to_excel("data/excel/已分析数据汇总(第一轮)_分析.xlsx", index=False)
+df.to_excel("data/excel/0714提问数据汇总(已分析)_软件_分析.xlsx", index=False)
diff --git a/rag2_0/dify/AnswerType.py b/rag2_0/dify/AnswerType.py
index fd1f037..e1d9d88 100644
--- a/rag2_0/dify/AnswerType.py
+++ b/rag2_0/dify/AnswerType.py
@@ -84,15 +84,14 @@ async def health_check():
return {"status": "ok"}
@app.get("/query_type", summary="异步检索API")
-async def query_type(query: str, query_type: str, workflow_run_id:str):
+async def query_type(query_type: str, workflow_run_id:str):
try:
# 记录请求
- logger.info(f"接收到请求: {query}, 类型: {query_type}, workflow_run_id: {workflow_run_id}")
+ logger.info(f"接收到请求: 类型: {query_type}, workflow_run_id: {workflow_run_id}")
# 保存 提问、问题类型、当前时间戳到json
timestamp = datetime.datetime.now().isoformat()
query_data = {
- "query": query,
"query_type": query_type,
"timestamp": timestamp,
"workflow_run_id": workflow_run_id
@@ -127,7 +126,7 @@ async def query_type(query: str, query_type: str, workflow_run_id:str):
logger.error(f"保存查询数据时出错: {str(e)}", exc_info=True)
# 返回响应
- content = f"当前提问: {query}
问题类型: {query_type}
操作是否成功: {'成功' if success else '失败'}"
+ content = f"问题类型: {query_type}
操作是否成功: {'成功' if success else '失败'}"
return HTMLResponse(content=content)
except Exception as e:
logger.error(f"处理请求时出错: {str(e)}", exc_info=True)
diff --git a/rag2_0/dify/DifyCompareTest.py b/rag2_0/dify/DifyCompareTest.py
index f2a9e29..342a1a0 100755
--- a/rag2_0/dify/DifyCompareTest.py
+++ b/rag2_0/dify/DifyCompareTest.py
@@ -84,7 +84,7 @@ class DifyComparisonTester:
def get_llm(self, **kwargs):
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_API_BASE")
- model = os.getenv("LLM_MODEL_NAME")
+ model = os.getenv("MODEL_NAME")
return OpenAiLLM(api_key=api_key, base_url=base_url, model=model, **kwargs)
def find_wiki_link(self, row) -> str | None:
diff --git a/rag2_0/dify/GenerateSoftwareWikiLibrary.py b/rag2_0/dify/GenerateSoftwareWikiLibrary.py
new file mode 100644
index 0000000..8e62279
--- /dev/null
+++ b/rag2_0/dify/GenerateSoftwareWikiLibrary.py
@@ -0,0 +1,66 @@
+import os
+import json
+import re
+import sys
+from dotenv import load_dotenv
+
+load_dotenv()
+sys.path.append(os.getcwd())
+
+from rag2_0.dify.dify_client import DifyApi
+
+soft_name_map = {
+ "配网造价软件知识(new)": "配网计价通D3软件",
+ "西藏造价软件知识(new)": "西藏计价通Z1软件",
+ "储能C1计价通软件知识(new)": "储能计价通C1软件",
+ "技改检修工程计价通T1软件知识(new)": "技改检修工程计价通T1软件",
+ "技改检修清单计价通T1软件知识(new)": "技改检修清单计价通T1软件",
+ "电力建设计价通(2018)软件知识(new)": "电力建设计价通软件",
+ "下载安装注册(new)": "下载安装注册",
+}
+
+soft_wiki_file_name = {
+ "配网计价通D3软件": ["配网计价通D3软件.txt", []],
+ "西藏计价通Z1软件": ["西藏计价通Z1软件.txt", []],
+ "储能计价通C1软件": ["储能计价通C1软件.txt", []],
+ "技改检修工程计价通T1软件": ["技改检修工程计价通T1软件.txt", []],
+ "技改检修清单计价通T1软件": ["技改检修清单计价通T1软件.txt", []],
+ "电力建设计价通软件": ["电力建设计价通软件.txt", []],
+ "下载安装注册": ["下载安装注册.txt", []],
+}
+
+def get_soft_wiki_titles(dify_api, soft_name_map, soft_wiki_file_name):
+ """获取每个软件的wiki标题列表"""
+ dataset_list = dify_api.get_all_dataset_list()
+ soft_name_map_keys = list(soft_name_map.keys())
+ for dataset in dataset_list:
+ if dataset["name"] not in soft_name_map_keys:
+ continue
+ dataset_name = dataset["name"]
+ dataset_id = dataset["id"]
+ documents = dify_api.get_documents(dataset_id=dataset_id)
+ for document_id, doc_info in documents.items():
+ document_name = doc_info["name"]
+ wiki_name = document_name.split("/")[-1]
+ wiki_title = re.sub(r'^(.*?)|^\(.*?\)', '', wiki_name)
+ if wiki_title not in soft_wiki_file_name[soft_name_map[dataset_name]][1]:
+ soft_wiki_file_name[soft_name_map[dataset_name]][1].append(wiki_title)
+ return soft_wiki_file_name
+
+def save_wiki_titles(soft_wiki_file_name, output_dir="data/wiki_data"):
+ """将wiki标题列表保存到对应txt文件"""
+ os.makedirs(output_dir, exist_ok=True)
+ for soft_name, (txt_file_name, wiki_titles) in soft_wiki_file_name.items():
+ output_path = os.path.join(output_dir, txt_file_name)
+ with open(output_path, "w", encoding="utf-8") as f:
+ for title in wiki_titles:
+ f.write(title + "\n")
+ print(f"已保存 {soft_name} 的wiki标题列表到 {output_path},共 {len(wiki_titles)} 条")
+
+def main():
+ dify_api = DifyApi()
+ wiki_titles = get_soft_wiki_titles(dify_api, soft_name_map, soft_wiki_file_name)
+ save_wiki_titles(wiki_titles)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/rag2_0/dify/chat_dify_by_workorder.py b/rag2_0/dify/chat_dify_by_workorder.py
deleted file mode 100755
index 58bf921..0000000
--- a/rag2_0/dify/chat_dify_by_workorder.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from rag2_0.dify.dify_tool import NewWorkflowChat
-import pandas as pd
-from concurrent.futures import ThreadPoolExecutor
-from tqdm import tqdm
-import concurrent.futures
-
-
-class ChatDifyByWorkorder:
-
- def __init__(self, api_key=None, base_url="https://api.dify.ai/v1") -> None:
- """
- 初始化ChatDifyByWorkorder类
-
- Args:
- api_key: Dify API密钥,默认为None
- base_url: Dify API的基础URL,默认为"https://api.dify.ai/v1"
- """
- baseurl = "http://172.20.0.145/v1"
- new_workflow_api_key = "app-qxsSybCs7ABiKlC1JabTYVn6"
- self.new_chat = NewWorkflowChat(api_key=new_workflow_api_key, base_url=baseurl)
- self.new_chat_answer = NewWorkflowChat(api_key=new_workflow_api_key, base_url=baseurl)
-
-
- def get_soft_name(self, row) -> str:
- if "博微配网计价通D3" in row["产品线"]:
- return "博微配网计价通D3"
- elif "博微电力建设计价通软件" in row["产品线"]:
- return "电力建设计价通软件"
- elif "新能源系列" in row["产品线"] and "博微新型储能电站建设计价通C1软件" in row["产品名称"]:
- return "储能C1软件"
- elif "博微西藏计价通Z1" in row["产品线"]:
- return "西藏计价通Z1"
- elif "博微技改检修计价通T1软件" in row["产品线"] and "技改检修计价通T1软件-概预算" in row["产品名称"]:
- return "技改检修工程计价通T1"
- elif "博微技改检修计价通T1软件" in row["产品线"] and "技改检修计价通T1软件-清单" in row["产品名称"]:
- return "检修清单计价通T1"
- return ""
-
- def process_query(self, q:str) -> dict:
- """
- 发送问题并获取回答及相关工作流信息
-
- Args:
- q: 用户问题
-
- Returns:
- dict: 包含问题、回答和工作流信息的字典
- """
- retry_count = 0
- max_retries = 2
-
- while retry_count <= max_retries:
- try:
- # 发送问题获取回答和消息ID
- result = self.new_chat.process_question(q)
- return result
- except Exception as e:
- retry_count += 1
- if retry_count <= max_retries:
- continue
- else:
- raise e
-
- def process_answer(self, q:str) -> dict:
- """
- 发送问题并获取回答及相关工作流信息
-
- Args:
- q: 用户问题
-
- Returns:
- dict: 包含问题、回答和工作流信息的字典
- """
- retry_count = 0
- max_retries = 2
-
- while retry_count <= max_retries:
- try:
- # 发送问题获取回答和消息ID
- result = self.new_chat_answer.process_question(q)
- return result
- except Exception as e:
- retry_count += 1
- if retry_count <= max_retries:
- continue
- else:
- raise
-
- def process_row(self, row):
- """处理单行数据"""
- soft_name = self.get_soft_name(row=row)
- if soft_name == "":
- return None
-
- # 使用线程池并发执行查询
- with ThreadPoolExecutor() as executor:
- try:
- # 提交两个任务并获取Future对象
- query_future = executor.submit(self.process_query, q=f"{soft_name},{row['客户问题']}")
- answer_future = executor.submit(self.process_answer, q=f"{soft_name},{row['解决方案']}")
-
- # 获取结果
- query_result = query_future.result()
- answer_result = answer_future.result()
- except Exception as e:
- print(f"处理工单 {row.get('工单编号', '未知')} 时发生错误: {str(e)}")
- return None
-
- worker_id = str(row["工单编号"])
- if query_result is None or answer_result is None:
- print("处理对话出现错误")
- return None
-
- worker_order_info = {
- "工单编号": worker_id,
- "用户问题": row['客户问题'],
- "解决方案": row['解决方案'],
- "AI回答": query_result["新流程答案"],
- "用户问题检索到的词条": query_result["新检索词条"],
- "解决方案检索到的词条": answer_result["新检索词条"],
- }
- return worker_order_info
-
- def run(self, excel_path:str):
- df_data = pd.read_excel(excel_path)
- list_worker_order_info = []
-
- # 创建进度条
- with tqdm(total=len(df_data), desc="处理工单") as pbar:
- # 创建线程池,最大并发数可以根据需要调整
- with ThreadPoolExecutor(max_workers=5) as executor:
- # 提交所有任务
- future_to_row = {executor.submit(self.process_row, row): idx for idx, row in df_data.iterrows()}
-
- # 处理完成的任务
- for future in concurrent.futures.as_completed(future_to_row):
- result = future.result()
- if result is not None:
- list_worker_order_info.append(result)
- pbar.update(1)
-
- return list_worker_order_info
-
-
-
-if __name__=="__main__":
- worker_chat = ChatDifyByWorkorder()
- result = worker_chat.run(excel_path="data/excel/工单记录_均衡提取2000条.xlsx")
- # 可以选择保存结果到Excel
- if result:
- pd.DataFrame(result).to_excel("data/excel/工单处理结果.xlsx", index=False)
\ No newline at end of file
diff --git a/rag2_0/dify/dify_client/__init__.py b/rag2_0/dify/dify_client/__init__.py
index 7e4c8c2..8c37050 100755
--- a/rag2_0/dify/dify_client/__init__.py
+++ b/rag2_0/dify/dify_client/__init__.py
@@ -1,4 +1,5 @@
-__all__ = ["ChatClient", "CompletionClient", "DifyClient"]
+__all__ = ["ChatClient", "CompletionClient", "DifyClient", "DifyApi"]
from .client import ChatClient, CompletionClient, DifyClient
+from .dify_api import DifyApi
diff --git a/rag2_0/dify/dify_client/dify_api.py b/rag2_0/dify/dify_client/dify_api.py
index c6e585d..0b2c021 100644
--- a/rag2_0/dify/dify_client/dify_api.py
+++ b/rag2_0/dify/dify_client/dify_api.py
@@ -14,12 +14,12 @@ class DifyApi:
用于与Dify API进行交互的类。
"""
- def __init__(self, dify_url: str="http://10.1.16.39/v1",
- dify_dataset_api_key: str="dataset-skLjmPVonjHo119OWNf3kAmY",
- dify_app_api_key: str="app-wUdkWJx5zeOvmvBUZizMoSw3"):
- self.dify_url = dify_url
- self.dify_dataset_api_key = dify_dataset_api_key
- self.dify_app_api_key = dify_app_api_key
+ def __init__(self, dify_url: str=None,
+ dify_dataset_api_key: str=None,
+ dify_app_api_key: str=None):
+ self.dify_url = dify_url if dify_url else os.environ.get('DIFY_BSAE_URL')
+ self.dify_dataset_api_key = dify_dataset_api_key if dify_dataset_api_key else os.environ.get('DIFY_DATASET_KEY')
+ self.dify_app_api_key = dify_app_api_key if dify_app_api_key else os.environ.get('DIFY_APP_KEY')
def get_document_indexing_status(self, datasets_id: str, batch: str) -> bool:
"""
diff --git a/rag2_0/dify/dify_tool.py b/rag2_0/dify/dify_tool.py
index 6936c29..d636832 100755
--- a/rag2_0/dify/dify_tool.py
+++ b/rag2_0/dify/dify_tool.py
@@ -449,7 +449,7 @@ content: "{content}"
"""
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("OPENAI_API_BASE")
- model = os.getenv("LLM_MODEL_NAME")
+ model = os.getenv("MODEL_NAME")
llm = OpenAiLLM(api_key=api_key, base_url=base_url, model=model)
response = llm.invoke(user_prompt=prompt, need_retry=True)
diff --git a/rag2_0/dify/intent_recognition_api.py b/rag2_0/dify/intent_recognition_api.py
index b193ad3..ae577b4 100755
--- a/rag2_0/dify/intent_recognition_api.py
+++ b/rag2_0/dify/intent_recognition_api.py
@@ -37,7 +37,7 @@ logger = logging.getLogger(__name__)
# 定义请求模型
class IntentRecognizeRequest(BaseModel):
query: str
- conversation_context: str = ""
+ conversation_context: Dict = None
chat_history: Optional[List] = None
previous_slots: str | Dict = None
@@ -89,13 +89,15 @@ _instance = None
@app.on_event("startup")
async def startup_event():
global _instance
- # 初始化AsyncIntentRecognizer实例
- api_key = os.getenv("OPENAI_API_KEY")
- base_url = os.getenv("OPENAI_API_BASE")
- model_name = os.getenv("LLM_MODEL_NAME", "gpt-3.5-turbo")
- _instance = await AsyncIntentRecognizer.create(api_key=api_key, base_url=base_url, model_name=model_name)
+ _instance = await AsyncIntentRecognizer.create()
logger.info("AsyncIntentRecognizer初始化完成")
+@app.post("/intent_recognize1")
+async def intent_recognize(request: Request):
+ data = await request.json()
+ print(data)
+ return {"message": "success"}
+
@app.post("/intent_recognize", response_model=IntentRecognizeResponse, summary="意图识别", description="识别用户查询的意图并进行问题改写")
async def intent_recognize(request: IntentRecognizeRequest):
try:
@@ -103,14 +105,15 @@ async def intent_recognize(request: IntentRecognizeRequest):
raise HTTPException(status_code=400, detail="缺少query参数")
start_time = time.time()
-
+ current_softname = request.conversation_context.get("current_softname", "")
result = await _instance.process_query_async(
query=request.query,
conversation_context=request.conversation_context,
chat_history=request.chat_history,
previous_slots=request.previous_slots,
use_jieba=True,
- enable_query_expansion=True
+ enable_query_expansion=True,
+ cur_soft_name=current_softname
)
end_time = time.time()
diff --git a/rag2_0/dify/test_workorder.py b/rag2_0/dify/test_workorder.py
deleted file mode 100755
index 3f1333e..0000000
--- a/rag2_0/dify/test_workorder.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import pandas as pd
-import random
-import math
-
-work_order_excel="data/excel/6万工单记录.xlsx"
-
-soft_row_data={
- "博微配网计价通D3":{"基本功能":[], "高级功能":[]},
- "储能C1软件":{"基本功能":[], "高级功能":[]},
- "西藏计价通Z1":{"基本功能":[], "高级功能":[]},
- "技改检修工程计价通T1":{"基本功能":[], "高级功能":[]},
- "检修清单计价通T1":{"基本功能":[], "高级功能":[]},
- "电力建设计价通软件":{"基本功能":[], "高级功能":[]},
-}
-
-df = pd.read_excel(work_order_excel)
-
-for idx, row in df.iterrows():
- if pd.isna(row["产品线"]):
- continue
-
- if "博微配网计价通D3" in row["产品线"]:
- soft_row_data["博微配网计价通D3"][row["问题类型"]].append((idx, row))
- elif "博微电力建设计价通软件" in row["产品线"]:
- soft_row_data["电力建设计价通软件"][row["问题类型"]].append((idx, row))
- elif "新能源系列" in row["产品线"] and "博微新型储能电站建设计价通C1软件" in row["产品名称"]:
- soft_row_data["储能C1软件"][row["问题类型"]].append((idx, row))
- elif "博微西藏计价通Z1" in row["产品线"]:
- soft_row_data["西藏计价通Z1"][row["问题类型"]].append((idx, row))
- elif "博微技改检修计价通T1软件" in row["产品线"] and "技改检修计价通T1软件-概预算" in row["产品名称"]:
- soft_row_data["技改检修工程计价通T1"][row["问题类型"]].append((idx, row))
- elif "博微技改检修计价通T1软件" in row["产品线"] and "技改检修计价通T1软件-清单" in row["产品名称"]:
- soft_row_data["检修清单计价通T1"][row["问题类型"]].append((idx, row))
-
-# 计算每个软件和功能类型的数据量
-total_count = 0
-counts = {}
-for software, types in soft_row_data.items():
- counts[software] = {}
- for type_name, rows in types.items():
- counts[software][type_name] = len(rows)
- total_count += len(rows)
-
-print(f"原始数据总量: {total_count}条")
-for software, types in counts.items():
- print(f"{software}: 基本功能 {types['基本功能']}条, 高级功能 {types['高级功能']}条")
-
-# 计算均衡提取的数量
-total_target = 2000
-categories_count = sum(len(types) for types in soft_row_data.values())
-per_category_target = math.ceil(total_target / categories_count)
-
-# 均衡提取数据
-balanced_data = []
-extracted_counts = {}
-extracted_indices = set() # 使用集合存储已提取数据的索引
-
-for software, types in soft_row_data.items():
- extracted_counts[software] = {}
-
- for type_name, rows in types.items():
- # 如果数据量不足,全部提取;否则随机抽取目标数量
- if len(rows) <= per_category_target:
- extracted = rows
- else:
- extracted = random.sample(rows, per_category_target)
-
- extracted_counts[software][type_name] = len(extracted)
- for idx, row in extracted:
- extracted_indices.add(idx) # 记录已提取数据的索引
- balanced_data.append(row)
-
-# 数据量不足2000时,从剩余数据中补充
-remaining_target = total_target - len(balanced_data)
-if remaining_target > 0:
- # 收集所有未被选中的数据
- remaining_data = []
- for software, types in soft_row_data.items():
- for type_name, rows in types.items():
- # 添加未被选中的数据
- for idx, row in rows:
- if idx not in extracted_indices:
- remaining_data.append(row)
-
- # 如果剩余数据足够,随机抽取补充
- if len(remaining_data) >= remaining_target:
- additional_data = random.sample(remaining_data, remaining_target)
- else:
- additional_data = remaining_data
-
- balanced_data.extend(additional_data)
-
-# 输出结果
-print(f"\n均衡提取后数据总量: {len(balanced_data)}条")
-for software, types in extracted_counts.items():
- print(f"{software}: 基本功能 {types['基本功能']}条, 高级功能 {types['高级功能']}条")
-
-# 将均衡提取的数据转换为DataFrame并保存
-balanced_df = pd.DataFrame(balanced_data)
-balanced_df.to_excel("data/excel/均衡提取2000条工单.xlsx", index=False)
-print(f"\n已将均衡提取的{len(balanced_data)}条数据保存至'data/excel/均衡提取2000条工单.xlsx'")
\ No newline at end of file
diff --git a/rag2_0/intent_recognition/IntentRecognition.py b/rag2_0/intent_recognition/IntentRecognition.py
index 13560a4..3c14e17 100755
--- a/rag2_0/intent_recognition/IntentRecognition.py
+++ b/rag2_0/intent_recognition/IntentRecognition.py
@@ -39,10 +39,21 @@ from .ProfessionalNounVector import ProfessionalNounRetriever, AsyncProfessional
from rag2_0.tool.ModelTool import XinferenceReRankerModel, OpenAiLLM, SiliconFlowReRankerModel
class AsyncIntentRecognizer:
+ SOFT_WIKI_PATH = "data/wiki_data"
+ SOFT_NAMETOWIKI_MAP = {
+ "配网计价通D3软件": "配网计价通D3软件.txt",
+ "西藏计价通Z1软件": "西藏计价通Z1软件.txt",
+ "储能计价通C1软件": "储能计价通C1软件.txt",
+ "技改检修工程计价通T1软件": "技改检修工程计价通T1软件.txt",
+ "技改检修清单计价通T1软件": "技改检修清单计价通T1软件.txt",
+ "电力建设计价通软件": "电力建设计价通软件.txt",
+ "下载安装注册": "下载安装注册.txt",
+ }
+
"""
异步意图识别和问题改写类
"""
- def __init__(self, api_key: str = None, base_url: str = None, model_name: str = "gpt-3.5-turbo", vector_index_dir: str = None):
+ def __init__(self):
"""
初始化异步意图识别器
@@ -52,51 +63,53 @@ class AsyncIntentRecognizer:
model_name: 要使用的模型名称
vector_index_dir: 向量索引目录,如果为None则使用默认目录
"""
+ api_key = os.getenv("OPENAI_API_KEY")
+ base_url = os.getenv("OPENAI_API_BASE")
+ model_name = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
# 初始化LLM
llm_params = {
"temperature": 0.2, # 降低随机性,使结果更确定
"top_p": 0.7,
- "model": model_name
+ "model": model_name,
+ "api_key": api_key,
+ "base_url": base_url
}
- # 如果提供了API密钥,则使用提供的密钥
- if api_key:
- llm_params["api_key"] = api_key
-
- # 如果提供了自定义URL,则使用提供的URL
- if base_url:
- llm_params["base_url"] = base_url
-
self._llm = OpenAiLLM(**llm_params)
+ llm_params["model"] = os.getenv("MINI_MODEL_NAME", "gpt-3.5-turbo")
+ self._llm_mini = OpenAiLLM(**llm_params)
# 加载suffix关键词
self._suffix_keywords = self._load_suffix_keywords()
-
+ # 加载软件词条名称库
+ self._soft_wiki_library = self._load_soft_wiki_library()
# 异步检索器将在create方法中初始化
self._noun_retriever = None
- self._api_key = api_key
- self._vector_index_dir = vector_index_dir
+
+ def _load_soft_wiki_library(self):
+ """
+ 加载软件wiki库
+ """
+ SOFT_WIKI_LIBRARY = {}
+ for soft_name, wiki_file_name in self.SOFT_NAMETOWIKI_MAP.items():
+ with open(f"{self.SOFT_WIKI_PATH}/{wiki_file_name}", "r", encoding="utf-8") as f:
+ lines = f.readlines()
+ # 去除空行
+ lines = [line.strip() for line in lines if line.strip()]
+ SOFT_WIKI_LIBRARY[soft_name] = lines
+ return SOFT_WIKI_LIBRARY
@classmethod
- async def create(cls, api_key: str = None, base_url: str = None, model_name: str = "gpt-3.5-turbo", vector_index_dir: str = None):
+ async def create(cls):
"""
异步工厂方法:创建并初始化异步意图识别器实例
-
- Args:
- api_key: OpenAI API密钥,如果为None则从环境变量获取
- base_url: OpenAI API基础URL,如果为None则使用默认URL
- model_name: 要使用的模型名称
- vector_index_dir: 向量索引目录,如果为None则使用默认目录
Returns:
初始化完成的AsyncIntentRecognizer实例
"""
- instance = cls(api_key, base_url, model_name, vector_index_dir)
+ instance = cls()
# 异步初始化名词检索器
- instance._noun_retriever = await AsyncProfessionalNounRetriever.create(
- api_key=api_key,
- index_dir=vector_index_dir
- )
+ instance._noun_retriever = await AsyncProfessionalNounRetriever.create()
return instance
def _load_suffix_keywords(self, filepath: str = None) -> List[str]:
@@ -402,11 +415,12 @@ class AsyncIntentRecognizer:
return f"通过博微软件助手查询软件锁信息,锁注册号为{lock_number}"
- async def process_query_async(self, query: str, conversation_context: str = "",
+ async def process_query_async(self, query: str, conversation_context: Dict = None,
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None,
use_jieba: bool = False,
- enable_query_expansion: bool = False) -> Dict[str, Any]:
+ enable_query_expansion: bool = False,
+ cur_soft_name: str = "") -> Dict[str, Any]:
"""
异步处理用户问题的完整流程
@@ -417,7 +431,7 @@ class AsyncIntentRecognizer:
previous_slots: 历史槽位信息
use_jieba: 是否使用jieba分词辅助提取关键词
enable_query_expansion: 是否启用查询扩展
-
+ cur_soft_name: 当前查询的软件名称
Returns:
包含分类、关键词、改写和槽位填充结果的字典
"""
@@ -425,7 +439,8 @@ class AsyncIntentRecognizer:
chat_history = []
if previous_slots is None:
previous_slots = {}
-
+ if conversation_context is None:
+ conversation_context = {}
# 步骤: 并行执行提问扩展
query_expand_tasks = []
if enable_query_expansion:
@@ -437,9 +452,9 @@ class AsyncIntentRecognizer:
# 5.2: Follow Up Questions
asyncio.create_task(self._generate_follow_up_questions_async(query, chat_history, conversation_context)),
- # 5.3: HyDE
- # asyncio.create_task(self._generate_hypothetical_document_async(query, chat_history, conversation_context)),
-
+ # 5.3: 文档查询
+ asyncio.create_task(self._find_matching_software_docs_async(query, cur_soft_name, chat_history)),
+
# 5.4: 多问题查询
asyncio.create_task(self._generate_multi_questions_async(query, chat_history, conversation_context))
]
@@ -497,23 +512,22 @@ class AsyncIntentRecognizer:
# 收集结果
step_back_result = query_expand_results[0] if query_expand_results[0] else StepBackPrompt(original_query=query, can_use_back_prompt=False, step_back_query=[query])
follow_up_result = query_expand_results[1] if query_expand_results[1] else FollowUpQuestions(original_query=query, follow_up_query=query)
- # hyde_result = query_expand_results[2] if query_expand_results[2] else HypotheticalDocument(original_query=query, hypothetical_answer="")
- multi_questions_result = query_expand_results[2] if query_expand_results[2] else MultiQuestions(original_query=query, sub_questions=[query])
+ wiki_result = query_expand_results[2] if query_expand_results[2] else []
+ multi_questions_result = query_expand_results[3] if query_expand_results[3] else MultiQuestions(original_query=query, sub_questions=[query])
all_questions = multi_questions_result.sub_questions
all_questions.append(query)
all_questions.append(rewrite.rewrite)
all_questions.extend(step_back_result.step_back_query)
all_questions.append(follow_up_result.follow_up_query)
- # all_questions.append(hyde_result.hypothetical_answer)
+ all_questions.extend(wiki_result)
all_questions = list(set(all_questions))
query_expand = {
"all": all_questions,
"step_back": step_back_result.model_dump(),
"follow_up": follow_up_result.model_dump(),
- # "hyde": hyde_result.model_dump(),
- "multi_questions": multi_questions_result.model_dump()
+ "multi_questions": multi_questions_result.model_dump(),
}
# 返回所有结果
@@ -721,45 +735,72 @@ class AsyncIntentRecognizer:
logging.error(f"异步后续问题生成失败: {e}", exc_info=True)
return FollowUpQuestions(original_query=query, follow_up_query=query)
- async def _generate_hypothetical_document_async(self, query: str, chat_history: List[Dict[str, str]] = None, conversation_context: str = "") -> HypotheticalDocument:
+ async def _find_matching_software_docs_async(self, query: str, soft_name: str,
+ chat_history: List[Dict[str, str]] = None,
+ top_k: int = 3) -> List[str]:
"""
- 异步生成假设性文档
+ 异步查找软件文档中与用户问题最匹配的几行内容
Args:
- query: 用户原始问题
+ query: 用户问题
+ soft_name: 软件名称
chat_history: 历史对话记录
- conversation_context: 会话背景信息
+ top_k: 返回的匹配行数,默认为3
Returns:
- 假设性文档结果
+ 匹配的文档行列表
+ """
+ if chat_history is None:
+ chat_history = []
+
+ # 检查软件名称是否在支持的列表中
+ if soft_name not in self.SOFT_NAMETOWIKI_MAP:
+ return []
+
+ # 获取软件文档内容
+ soft_docs = self._soft_wiki_library.get(soft_name, [])
+ if not soft_docs:
+ return []
+ soft_docs.extend(self._soft_wiki_library.get("下载安装注册", []))
+ # soft_docs=soft_docs[:50]
+ # 构建文档字符串,只包含行内容
+ soft_docs_str = "\n".join(f"{doc.strip()}" for i, doc in enumerate(soft_docs))
+
+ # 构建提示词,让LLM选择最匹配的行
+ prompt = f"""
+ {soft_docs_str}
+ ================================
+ 以上为软件功能操作、常见问题排查等功能,结合历史对话,请输出与当前提问最相关的1-3个功能名称,
+ 使用Json格式输出,如下:
+ [{{"content": "行内容"}},...]
+ 当前问题: {query}
+ 历史对话: {json.dumps(chat_history, ensure_ascii=False)}
"""
- hyde_start_time = time.time()
- # 准备提示词
- hyde_parser = PydanticOutputParser(pydantic_object=HypotheticalDocument)
- formatted_prompt = hyde_prompt.format(
- query=query,
- chat_history=json.dumps(chat_history, ensure_ascii=False) if chat_history else "[]",
- # conversation_context=conversation_context,
- output_format=hyde_parser.get_format_instructions()
- )
try:
# 异步调用LLM
- response = await self._llm.invoke_async(formatted_prompt, False)
+ start_time = time.time()
+ response = await self._llm.invoke_async(prompt, False, response_format={"type": "json_object"})
+ end_time = time.time()
- # 解析输出
- response.content = response.content.strip()
- clean_output = re.sub(r'.*?', '', response.content, flags=re.DOTALL)
- parsed_output = hyde_parser.parse(clean_output)
- hyde_end_time = time.time()
- hyde_time = hyde_end_time - hyde_start_time
- logging.debug(f"异步假设性文档生成耗时统计 - 总耗时: {hyde_time:.2f}秒")
- return parsed_output
+ # 解析JSON响应
+ try:
+ wiki_names = []
+ json_response = json.loads(response.content)
+ for match in json_response:
+ wiki_names.append(match["content"])
+ logging.debug(f"软件文档匹配耗时: {end_time - start_time:.2f}秒")
+ return wiki_names
+
+ except json.JSONDecodeError as e:
+ logging.error(f"解析JSON响应时出错: {e}")
+ return []
+
except Exception as e:
- # 如果解析失败,返回空的假设性回答
- logging.error(f"异步假设性文档生成失败: {e}", exc_info=True)
- return HypotheticalDocument(original_query=query, hypothetical_answer="")
-
+ logging.error(f"查找匹配软件文档时出错: {e}", exc_info=True)
+ # 出错时返回空列表
+ return []
+
async def _generate_multi_questions_async(self, query: str, chat_history: List[Dict[str, str]] = None, conversation_context: str = "") -> MultiQuestions:
"""
异步生成多角度问题
diff --git a/rag2_0/intent_recognition/ProfessionalNounVector.py b/rag2_0/intent_recognition/ProfessionalNounVector.py
index 8d6afb2..43dcca2 100755
--- a/rag2_0/intent_recognition/ProfessionalNounVector.py
+++ b/rag2_0/intent_recognition/ProfessionalNounVector.py
@@ -28,8 +28,6 @@ def get_embedding_model(api_key: str = None) -> Embeddings:
Returns:
嵌入模型实例
"""
- if not api_key:
- api_key = os.getenv("SILICONFLOW_API_KEY", "sk-ftnofbucchwnscojohyxwmfzgaykdxihafnlphohsinftkbr")
return SiliconFlowEmbeddings(api_key=api_key)
diff --git a/rag2_0/intent_recognition/PromptTemplates.py b/rag2_0/intent_recognition/PromptTemplates.py
index b2c6702..ed458b8 100755
--- a/rag2_0/intent_recognition/PromptTemplates.py
+++ b/rag2_0/intent_recognition/PromptTemplates.py
@@ -413,7 +413,7 @@ multi_questions_prompt = """
## 任务说明
1. 分析用户的原始问题,理解其核心意图和需求
2. 考虑历史对话和会话背景,理解用户当前问题的上下文
-3. 从不同角度生成2-4个子问题,这些子问题应该:
+3. 从不同角度生成1-3个子问题,这些子问题应该:
- 分别关注原始问题的不同方面或组成部分
- 更加具体和直接
- 共同覆盖原始问题的完整意图
diff --git a/rag2_0/tool/ModelTool.py b/rag2_0/tool/ModelTool.py
index cab73c6..86a5472 100755
--- a/rag2_0/tool/ModelTool.py
+++ b/rag2_0/tool/ModelTool.py
@@ -236,7 +236,7 @@ class OpenAiLLM:
self._model = kwargs.get("model")
kwargs.pop("model")
else:
- self._model = os.getenv("LLM_MODEL_NAME")
+ self._model = os.getenv("MODEL_NAME")
self._kwargs = kwargs
@@ -284,13 +284,19 @@ class OpenAiLLM:
except Exception as e:
raise RuntimeError(f"OpenAiLLM:invoke:error:{str(e)}.api_key:{api_key}") from e
- async def invoke_async(self, user_prompt="你是谁?", need_retry=True):
+ async def invoke_async(self, user_prompt="你是谁?", need_retry=True, **extra_kwargs):
"""异步调用OpenAI API"""
max_retries = 3
retry_count = 0
- if "timeout" not in self._kwargs:
+
+ # 合并额外的kwargs与self._kwargs
+ kwargs = {**self._kwargs}
+ if extra_kwargs:
+ kwargs.update(extra_kwargs)
+
+ if "timeout" not in kwargs:
timeout = httpx.Timeout(300.0)
- self._kwargs["timeout"] = timeout
+ kwargs["timeout"] = timeout
if need_retry:
while retry_count < max_retries:
@@ -302,7 +308,7 @@ class OpenAiLLM:
completion = await client.chat.completions.create(
model=self._model,
messages=[{'role': 'user', 'content': user_prompt}],
- **self._kwargs
+ **kwargs
)
return completion.choices[0].message
@@ -319,7 +325,7 @@ class OpenAiLLM:
completion = await client.chat.completions.create(
model=self._model,
messages=[{'role': 'user', 'content': user_prompt}],
- **self._kwargs
+ **kwargs
)
return completion.choices[0].message
except Exception as e: