Compare commits

..

28 Commits

Author SHA1 Message Date
ly 32136be5db 增加图QA问答 2025-07-14 15:36:15 +08:00
ly 46552a536f 更新 2025-07-07 15:29:55 +08:00
ly fcb09c04f2 更新 2025-07-07 12:47:34 +08:00
ly b352571e17 上传 2025-07-07 11:25:17 +08:00
ly cfb4f4ea6a 增加WEB入口 2025-07-07 09:32:05 +08:00
ly dc43b69164 增加streamlt界面入口 2025-07-07 08:51:19 +08:00
ly d1c129c691 实现完整功能 2025-07-07 08:23:02 +08:00
ly 35d50305c8 更新 2025-07-04 14:18:16 +08:00
ly 72e4e9ef6f 删除python缓存 2025-07-04 14:03:41 +08:00
ly 3ba0a4b55c 修改没有结果输出BUG 2025-07-04 14:03:08 +08:00
ly 2889ec514b 更新 2025-07-04 13:59:39 +08:00
ly f94fa82d88 更新代码 2025-07-04 13:59:07 +08:00
ly be98bb1495 更新最新代码 2025-07-04 13:52:08 +08:00
ly af64d962eb 合并 2025-07-02 10:32:38 +08:00
chentianrui 7e7109e01b 上传文件至 tests 2025-06-27 22:36:39 +08:00
chentianrui 856810025d 上传文件至 / 2025-06-27 22:36:20 +08:00
chentianrui bd8b3c29bd 删除 project_implementation.py 2025-06-27 22:36:07 +08:00
chentianrui 7c3b0c2882 上传文件至 / 2025-06-27 22:36:00 +08:00
chentianrui 6c6ddb052d 删除 project.py 2025-06-27 22:35:54 +08:00
chentianrui c1e7f3d007 上传文件至 / 2025-06-27 22:35:46 +08:00
chentianrui a4fc746411 删除 build_kg_ontolo.py 2025-06-27 22:35:36 +08:00
chentianrui 830a2b49c4 上传文件至 / 2025-06-27 22:35:21 +08:00
chentianrui 05e7bd2711 删除 Ontology_Layer.txt 2025-06-27 22:35:04 +08:00
chentianrui 59d76b1d72 上传文件至 / 2025-06-26 18:28:27 +08:00
chentianrui a742c52d00 删除 build_kg.py 2025-06-26 18:28:10 +08:00
chentianrui 90c46d2a3f 修改了知识图谱的工程属性节点保存方式 2025-06-26 15:15:20 +08:00
chentianrui 20b561cf04 删除 build_kg.py 2025-06-26 15:13:25 +08:00
ly 9b3d5b7ef1 调试后整体可以使用的版本。 2025-06-26 12:57:21 +08:00
42 changed files with 141646 additions and 2822 deletions
+6 -5
View File
@@ -1,7 +1,8 @@
BoweiAgent.log
*.log
__pycache__/*
.vscode/launch.json
src/__pycache__/*
.cursor/rules/use.mdc
test_runcode.log
BoweiAgent1.log
.vscode/*
.cursor/*
logs/*
cache/*
转化服务/*
+53 -2
View File
@@ -1,15 +1,30 @@
neo4j:
#uri: bolt://10.1.16.39:7687
uri: bolt://localhost:7487
#uri: bolt://localhost:7487
uri: bolt://172.20.0.145:7687
username: neo4j
password: password
index_name: name_vector_index # 向量索引名称,默认 "vector"
keyword_index_name: name_keyword_index # 关键词索引名称,默认 "keyword"
business_object_structure_path: ./data/business_object_structure.md
bowei_api_docs_path: ./data/bowei_api_docs.md
bowei_api_docs_path: ./src/project.py
max_retries: 0
openai:
temperature: 0
api_keys:
- sk-gycpzfjfxldpmqmqvrtgtvtpusnsrscowhscqfbppmjdlzoi
- sk-lhvfliriyohnaspzuoddgulmdiovpbjcdmfdfdvghapprfru
- sk-tzxcxzberpdpvszhjfyikepjlgslbtgssypxpmwllgithtwz
- sk-ybhbyjkzdydsllxredzntqdyrderxighdieiayudcubvhxsw
- sk-xtjzktvgrkzikkvetzygzozfyjxzgasednowyjqjeudfqiha
- sk-ufjdohciujtmpfwrrqhiocufexdzqpiotnjwzhbumcpvkpcw
- sk-nvnuzkdgxwzvibtuymwhatfajnerekgsbieqmqadjaqyhkob
- sk-erdnqkazveyiaojrpojawtwlxpnrikhedurlphmyviaixyrg
- sk-xhddshwxrhejbzitozgieightaecdeozqzdgypmedzdydthr
- sk-czilnjaxqwicwoyvfaxgzvqcjhmdzpwvzhbxvnqnvaeocojk
api_key: sk-xlrnesfcuwrpevdwbuhthivpygwyzwbxxsyvhzzwrkpzjduk
api_base: https://api.siliconflow.cn/v1
#api_version: "" # 可选,某些API版本需要指定
@@ -19,7 +34,43 @@ openai:
#model_name: Qwen/Qwen3-8B
#model_name: deepseek-ai/DeepSeek-V3
openai_qa:
temperature: 0
api_keys:
- sk-gycpzfjfxldpmqmqvrtgtvtpusnsrscowhscqfbppmjdlzoi
- sk-lhvfliriyohnaspzuoddgulmdiovpbjcdmfdfdvghapprfru
- sk-tzxcxzberpdpvszhjfyikepjlgslbtgssypxpmwllgithtwz
- sk-ybhbyjkzdydsllxredzntqdyrderxighdieiayudcubvhxsw
- sk-xtjzktvgrkzikkvetzygzozfyjxzgasednowyjqjeudfqiha
- sk-ufjdohciujtmpfwrrqhiocufexdzqpiotnjwzhbumcpvkpcw
- sk-nvnuzkdgxwzvibtuymwhatfajnerekgsbieqmqadjaqyhkob
- sk-erdnqkazveyiaojrpojawtwlxpnrikhedurlphmyviaixyrg
- sk-xhddshwxrhejbzitozgieightaecdeozqzdgypmedzdydthr
- sk-czilnjaxqwicwoyvfaxgzvqcjhmdzpwvzhbxvnqnvaeocojk
api_key: sk-xlrnesfcuwrpevdwbuhthivpygwyzwbxxsyvhzzwrkpzjduk
api_base: https://api.siliconflow.cn/v1
#api_version: "" # 可选,某些API版本需要指定
#organization: your_organization_id # 可选
api_type: openai # 可选,默认为 openai;如果用 Azure 则为 azure
#model_name: Qwen/Qwen2.5-72B-Instruct
#model_name: Qwen/Qwen3-8B
model_name: deepseek-ai/DeepSeek-V3
openai_coder:
temperature: 0
api_keys:
- sk-gycpzfjfxldpmqmqvrtgtvtpusnsrscowhscqfbppmjdlzoi
- sk-lhvfliriyohnaspzuoddgulmdiovpbjcdmfdfdvghapprfru
- sk-tzxcxzberpdpvszhjfyikepjlgslbtgssypxpmwllgithtwz
- sk-ybhbyjkzdydsllxredzntqdyrderxighdieiayudcubvhxsw
- sk-xtjzktvgrkzikkvetzygzozfyjxzgasednowyjqjeudfqiha
- sk-ufjdohciujtmpfwrrqhiocufexdzqpiotnjwzhbumcpvkpcw
- sk-nvnuzkdgxwzvibtuymwhatfajnerekgsbieqmqadjaqyhkob
- sk-erdnqkazveyiaojrpojawtwlxpnrikhedurlphmyviaixyrg
- sk-xhddshwxrhejbzitozgieightaecdeozqzdgypmedzdydthr
- sk-czilnjaxqwicwoyvfaxgzvqcjhmdzpwvzhbxvnqnvaeocojk
api_key: sk-xlrnesfcuwrpevdwbuhthivpygwyzwbxxsyvhzzwrkpzjduk
api_base: https://api.siliconflow.cn/v1
#api_version: "" # 可选,某些API版本需要指定
@@ -577,39 +577,3 @@ class Fee:
self.施工费 = None # xsd:string (可选)
self.单位投资 = None # xsd:string (可选)
class ProjectBuilder:
# 存储注册的工具类
_registry = None
_config = {}
"""项目工具工厂类"""
def __init__(self):
pass
@classmethod
def register(toolkit_class: type, config: dict):
"""
注册工具类到工厂
参数:
toolkit_class: 继承自ProjectToolkit的具体工具类
"""
if not issubclass(toolkit_class, ProjectToolkit):
raise TypeError(f"{toolkit_class.__name__} 必须继承自 ProjectToolkit")
_config = config
_registry = toolkit_class
def build(self) -> ProjectToolkit:
"""
创建工具实例
参数:
返回:
实例化的工具对象
"""
if _registry is None:
raise KeyError(f"未注册的类,请先注册类")
return _registry(_config)
+253 -138
View File
@@ -19,22 +19,19 @@ class ProjectToolkit(ABC):
# 项目划分查询方法
@abstractmethod
def get_division_by_name(self, name):
def get_division_by_name(self, name_part):
"""
通过名称获取项目划分对象
Args:
path (str): 项目划分节点名称
name_part (str): 项目划分节点名称
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的项目划分对象数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表,如果未找到节点,则包含父节点下所有ProjectDivisionItem节点
Note:
当路径为空字符串时,会返回None
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
"""
pass
@@ -47,14 +44,11 @@ class ProjectToolkit(ABC):
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的项目划分对象数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表,如果未找到节点,则包含父节点下所有ProjectDivisionItem节点
Note:
当路径为空字符串时,会返回None
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Dict[str, Any]): 成功时返回的项目划分对象数据,失败时为空字典
"""
pass
@@ -68,11 +62,11 @@ class ProjectToolkit(ABC):
partial_name (str): 目标节点的模糊或不完整名称
Returns:
Tuple[str, List[Dict[str, Any]], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (List[Dict[str, Any]]): 成功时返回的数据列表
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
"""
pass
@@ -86,16 +80,16 @@ class ProjectToolkit(ABC):
paths_str (str): 以'/'分隔的多级节点路径
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]:
- status: 'success'或'error'
- data: 成功时返回的节点数据字典,失败时为{}
- error: 错误信息,成功时为''
- helper_info: 辅助信息列表(当目标节点不存在时返回父节点下的所有ProjectQuantity节点名称)
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Dict[str, Any]): 成功时返回的节点数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_quantities_node_by_parent_and_code(self, parent_path, quantity_type=None, code=None):
def get_quantities_node_by_parent_and_code(self, parent_path, quantity_type, code):
"""
通过父节点路径和编码获取工程量对象(定额、主材或设备),包括子节点
@@ -105,30 +99,31 @@ class ProjectToolkit(ABC):
code (str): 工程量编码,以'/'分隔的多个编码
Returns:
Tuple[str, List[Dict[str, Any]], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (List[Dict[str, Any]]): 成功时返回的数据列表
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[List[Dict[str, Any]], Dict]): 成功时返回的数据列表或字典,失败时为空列表 []
"""
pass
@abstractmethod
def get_quantities_node_by_parent_and_name(self, parent_path, partial_name, quantity_type=None):
def get_quantities_node_by_parent_and_name(self, parent_path, quantity_type, partial_name):
"""
通过父节点路径、模糊节点名称和类型获取工程量对象(主材或者设备),包括子节点
通过父节点路径、类型和模糊节点名称获取工程量对象(主材或者设备),包括子节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
quantity_type (str): 工程量类型('定额'、'主材'、'设备')
partial_name (str): 目标节点的模糊或不完整名称
quantity_type (str): 工程量类型('定额'、'主材'、'设备'或None表示所有类型)
Returns:
Tuple[str, List[Dict[str, Any]], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (List[Dict[str, Any]]): 成功时返回的数据列表
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[List[Dict[str, Any]], Dict]): 成功时返回的数据列表,失败时为空列表 []
"""
pass
@@ -142,31 +137,29 @@ class ProjectToolkit(ABC):
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的节点属性数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 如果未找到目标节点,则包含父节点下所有 MaterialOrEquipment 节点名称
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_material_equipment_by_parent_and_name(self, parent_path, partial_name):
def get_material_equipment_by_parent_and_code(self, parent_path, code):
"""
通过父节点路径和模糊名称获取材机对象
通过父节点路径和编码获取材机对象
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
partial_name (str): 目标节点的模糊或不完整名称
parent_path (str): 父节点的部分路径,以'/'分隔的多级节点路径
code (str): 目标节点的编码属性值
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的节点属性数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 如果未找到目标节点,则包含父节点下所有 MaterialOrEquipment 节点名称
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@@ -174,45 +167,43 @@ class ProjectToolkit(ABC):
@abstractmethod
def get_fee_template_by_path(self, paths_str):
"""
通过路径获取取费节点
通过路径获取取费表模板节点
Args:
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)/直接费
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的节点属性数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 如果未找到目标节点,则包含父节点下所有 FeeCollection 节点名称
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_fee_template_by_parent_and_name(self, parent_path, partial_name):
"""
通过父节点路径和节点名称获取取费节点
通过父节点路径和节点名称获取取费表模板节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)
node_name (str): 目标节点的名称,例如:直接费
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回的节点属性数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 如果未找到父节点,则包含父节点的父节点下所有节点名称
- 如果未找到目标节点,则包含父节点下所有 FeeCollection 节点名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
# 费用表查询方法
@abstractmethod
def get_fee_schedule_on_auxiliary_expense_table(self, table_name, fee_name, fee: str):
def get_fee_schedule_on_auxiliary_expense_table(self, table_name, fee_name, fee_attribute: str):
"""
在辅助费用表中查找费用
@@ -222,20 +213,16 @@ class ProjectToolkit(ABC):
fee_attribute (str): 费用值属性名
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回包含属性值的数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 第一步失败:返回"工程数据"节点下所有子节点名称
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
- 第三步失败:返回费用节点的所有可用属性名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_other_expense_table(self, table_name, fee_name, fee):
def get_fee_schedule_on_other_expense_table(self, table_name, fee_name, fee_attribute):
"""
在其它费用表中查找费用
@@ -245,22 +232,18 @@ class ProjectToolkit(ABC):
fee_attribute (str): 费用值属性名
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回包含属性值的数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 第一步失败:返回"工程数据"节点下所有子节点名称
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
- 第三步失败:返回费用节点的所有可用属性名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_land_acquisition_fee_table_table(self, table_name, fee_name, fee):
def get_fee_schedule_on_land_acquisition_fee_table_table(self, table_name, fee_name, fee_attribute):
"""
在其中:地征用费表中查找费用
在其中:地征用费表中查找费用
Args:
table_name (str): 费用表名称
@@ -268,22 +251,18 @@ class ProjectToolkit(ABC):
fee_attribute (str): 费用值属性名
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回包含属性值的数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 第一步失败:返回"工程数据"节点下所有子节点名称
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
- 第三步失败:返回费用节点的所有可用属性名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_installation_price_difference_table(self, table_name, fee_name, fee):
def get_fee_schedule_on_installation_price_difference_table(self, table_name, fee_name, fee_attribute):
"""
在安装价差表中查找费用
在安装价差费用表中查找费用
Args:
table_name (str): 费用表名称
@@ -291,20 +270,16 @@ class ProjectToolkit(ABC):
fee_attribute (str): 费用值属性名
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回包含属性值的数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 第一步失败:返回"工程数据"节点下所有子节点名称
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
- 第三步失败:返回费用节点的所有可用属性名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_Engineering_Cost_table(self, table_name, fee_name, fee):
def get_fee_schedule_on_Engineering_Cost_table(self, table_name, fee_name, fee_attribute):
"""
在工程费用表中查找费用
@@ -314,15 +289,151 @@ class ProjectToolkit(ABC):
fee_attribute (str): 费用值属性名
Returns:
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
- status (str): 状态,'success'或'error'
- data (Dict[str, Any]): 成功时返回包含属性值的数据
- error (str): 错误时的错误信息
- helper_info (List[Any]): 辅助信息列表:
- 第一步失败:返回"工程数据"节点下所有子节点名称
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
- 第三步失败:返回费用节点的所有可用属性名称
- 否则为空
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
# 工程属性查询方法
@abstractmethod
def get_project_property(self, property_name):
"""
通过属性名称获取工程属性
Args:
property_name (str): 属性名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 项目划分查找取费表
@abstractmethod
def get_fee_table_by_project_division(self, project_division_path, fee_name):
"""
通过项目划分路径和取费名称查找费用
Args:
project_division_path (str): 项目划分节点的路径
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 清单查找取费表
@abstractmethod
def get_fee_table_by_list(self, parent_path, list_code, list_unit, fee_name):
"""
通过清单名称和取费名称查找费用
Args:
parent_path (str): 父级路径
list_code (str): 清单编号
list_unit (str): 清单单位
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 定额节点查找合价
@abstractmethod
def get_fee_table_by_quoto_code(self, parent_path, quantity_type, code, fee_name):
"""
通过父级路径、工程量类型、定额编码和取费名称查找费用
Args:
parent_path (str): 父级路径
quantity_type (str): 工程量类型
code (str): 定额编码
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 工程量节点查找合价
@abstractmethod
def get_fee_table_by_quantities_name(self, parent_path, quantity_type, quantity_name, fee_name):
"""
通过父级路径、工程量类型、工程量名称和取费名称查找费用
Args:
parent_path (str): 父级路径
quantity_type (str): 工程量类型
quantity_name (str): 工程量名称
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 材机节点查找市场价
@abstractmethod
def get_fee_by_material_equipment_code(self, parent_path, material_equipment_code, fee_name):
"""
通过父级路径、材机编码和取费名称查找费用
Args:
parent_path (str): 父级路径
material_equipment_code (str): 材机编码
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
# 查找清单节点
@abstractmethod
def get_fee_table_by_list_name(self, parent_path, list_code, unit):
"""
通过父级路径、清单编号和单位查找清单
Args:
parent_path (str): 父级路径
list_code (str): 清单编号
unit (str): 单位
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@@ -577,39 +688,43 @@ class Fee:
self.施工费 = None # xsd:string (可选)
self.单位投资 = None # xsd:string (可选)
class ProjectBuilder:
# 存储注册的工具类
_registry = None
_config = {}
"""项目工具工厂类"""
def __init__(self):
pass
@classmethod
def register(toolkit_class: type, config: dict):
def register(cls, toolkit_class: type, config: dict):
"""
注册工具类到工厂
参数:
toolkit_class: 继承自ProjectToolkit的具体工具类
"""
if not issubclass(toolkit_class, ProjectToolkit):
raise TypeError(f"{toolkit_class.__name__} 必须继承自 ProjectToolkit")
_config = config
_registry = toolkit_class
def build(self) -> ProjectToolkit:
cls._config = config
cls._registry = toolkit_class
@classmethod
def build(cls) -> ProjectToolkit:
"""
创建工具实例
参数:
返回:
实例化的工具对象
"""
if _registry is None:
if cls._registry is None:
raise KeyError(f"未注册的类,请先注册类")
return _registry(_config)
return cls._registry(cls._config)
+49 -38
View File
@@ -1,59 +1,70 @@
EngineeringData(工程)
├── ProjectPropertySet(工程属性集)
── ProjectProperty(项目编码、电压等级、主变规模)
── ProjectProperty(工程属性)[name:项目划分, value: 架空输电线路工程]
│ ├── ProjectProperty(工程属性)[name:电压等级, value: 220]
│ └── ProjectProperty(工程属性)[name:专业类型, value: 送电]
├── ProjectDivisionSet(项目划分集)
│ │
│ ├── ProjectDivisionTree(安装)
│ │ ├── ProjectDivisionItem(架空输电线路本体工程)[父节点]
│ │ │ ├── ProjectDivisionItem(基础工程)[叶节点]
│ │ │ │ ├── ProjectQuantity(现浇混凝土)[类型:Quota, 量:1500m³, 定额:GD5-2023-025]
│ │ │ │ └── ProjectQuantity(钢筋)[类型:MainMaterial, 量:950t, 型号:HRB400]
│ │ │ │
│ │ │ └── ProjectDivisionItem(GIS室)[叶节点]
│ ├── ProjectDivisionTree(项目划分树)[name: 安装]
│ │ ├── ProjectDivisionItem(项目划分)[name: 架空输电线路本体工程]
│ │ │ ├── ProjectDivisionItem(项目划分)[name: 基础工程]
│ │ │ │ ├──ProjectQuantity(工程量)[name: 汽车运输 金具、绝缘子、零星钢材 装卸, 类型: 定额, 量: 84.486092, 单位: t, 编码: YX1-97]
│ │ │ │ │ └── CostSet(费用预览集)
│ │ │ │ │ ├──CostItem(费用项)[name: 直接工程费_FFZ1, cost: 354010.98]
│ │ │ │ │ ├──CostItem(费用项)[name: 编制年价差_机械价差_JJC, cost: 21869.0211]
│ │ │ │ │ └──CostItem(费用项)[name: 社会保险费_BZHF, cost: 16758.8798]
│ │ │ │ ├── ProjectQuantity(工程量)[name: 商品混凝土(立柱), 类型: 主材, 数量: 39.944, 单位: m³, 编码: SP1004]
│ │ │ │ └── CostSet(费用预览集)
│ │ │ │ ├──CostItem(费用项)[name: 直接工程费_FFZ1, cost: 6843369.393079]
│ │ │ │ ├──CostItem(费用项)[name: 编制年价差_机械价差_JJC, cost: 40402.974077]
│ │ │ │ └──CostItem(费用项)[name: 社会保险费_BZHF, cost: 379304.02941]
│ │ │ │
│ │ │ └── ProjectDivisionItem(项目划分)[name: GIS室]
│ │ │
│ │ └── ProjectDivisionItem(辅助设施工程)[父节点]
│ │ ├── ProjectDivisionItem(事故油池)[叶节点]
│ │ └── ProjectDivisionItem(防火墙)[叶节点]
│ │ └── ProjectDivisionItem(项目划分)[name: 辅助设施工程]
│ │ ├── ProjectDivisionItem(项目划分)[name: 事故油池]
│ │ └── ProjectDivisionItem(项目划分)[name: 施工道路]
│ │
│ └── ProjectDivisionTree(清理项目安装)
│ ├── ProjectDivisionItem(主变压器系统)[父节点]
│ │ ├── ProjectDivisionItem(本体安装)[叶节点]
│ │ │ └── ProjectQuantity(主变压器)[类型:Equipment, 量:4, 型号:SZ11-1000MVA]
│ └── ProjectDivisionTree(项目划分树)[name: 清理项目安装]
│ ├── ProjectDivisionItem(项目划分)[name: 安装过程]
│ │ ├── ProjectDivisionItem(项目划分)[name: 本体安装]
│ │ │ └── ProjectQuantity(工程量)[name:锅炉(烟煤), 类型: 设备, 量:4, 单位: 台]
│ │ │
│ │ └── ProjectDivisionItem(冷却系统)[叶节点]
│ │ └── ProjectDivisionItem(项目划分)[name: 冷却系统]
│ │
│ └── ProjectDivisionItem(配电装置)[父节点]
│ ├── ProjectDivisionItem(500kV GIS)[叶节点]
│ │ └── ProjectQuantity(GIS间隔)[类型:Equipment, 量:8间隔, 单价:580万]
│ └── ProjectDivisionItem(项目划分)[name: 建筑工程]
│ ├── ProjectDivisionItem(项目划分)[name: 辅助工程]
│ │ └── ProjectQuantity(工程量)[name:柴油发电机, 类型: 设备, 量:1, 单位: 台]
│ │
│ └── ProjectDivisionItem(35kV开关柜)[叶节点]
│ └── ProjectDivisionItem(项目划分)[name: 基础永久性围堰]
├── FeeScheduleSet(费用表集)
│ │
│ ├── FeeScheduleItem(工程费用表)
│ │ ├── Fee(直接费)[值:32500万元]
│ │ │ └── Fee(机械费)[值:7850万元]
│ ├── FeeScheduleItem(费用表项)[name: 工程费用表]
│ │ ├── Fee(费用)[name: 架空输电线路本体工程, code: BTGC, 安装费(AZF): 11155128.078755]
│ │ │ └── Fee(费用)[name: 一般线路本体工程, code: XLBT, 安装费(AZF): 11155128.078755]
│ │ │
│ │ └── Fee(措施费)[值:4875万元]
│ │ ├── Fee(安全文明施工费)[费率:3.5%, 值:2800万元]
│ │ └── Fee(夜间施工增加费)[费率:1.2%, 值:2075万元]
│ │ └── Fee(费用)[name: 动态费用, code: DTFY, 安装费(AZF): 0]
│ │ ├── Fee(费用)[name: 建设期贷款利息, code: DKLX, 安装费(AZF): 0]
│ │ └── Fee(费用)[name: 价差预备费, code: JCYBF, 安装费(AZF): 0]
│ │
│ └── FeeScheduleItem(其他费用表)
│ ├── Fee(企业管理费)[费率:5.8%, 值:1885万元]
│ └── Fee(税金)[费率:9%, 值:2925万元]
│ └── FeeScheduleItem(费用表项)[name: 其他费用表]
│ ├── Fee(费用)[name: 工程保险费, code: B6, rate: 100, amount: 0]
│ └── Fee(费用)[name: 招标费, code: B2, rate: 0.28, amount: 31234.358621]
├── FeeTableTemplateSet(取费表模板集)
├── FeeTableTemplateItem(建筑取费表)
│ ├── FeeCollection(直接费)
│ │ └── FeeCollection(人工费调整)[系数:1.25]
├── FeeTableTemplateItem(取费表模板项)[name: 线路取费表]
│ ├── FeeCollection(取费) [name: 设备费, code: SBF, rate: 100]
│ │ └── FeeCollection(取费)[name: 乙供设备不含税价, code: SBY, base: 乙供设备费不含税+乙供设备税金, rate: 100]
│ │
│ └── FeeCollection(措施费)
── FeeCollection(安全文明施工费)[费率:3.5%]
── FeeCollection(夜间施工增加费)[费率:1.2%]
│ └── FeeCollection(取费)[name:直接费, code: FFZ, rate: 100]
── FeeCollection(取费)[name: 直接工程费, code: FFZ1]
── FeeCollection(取费)[name: 人工费, code: RGF, base: 取费定额人工费, rate: 100]
│ └── FeeCollection(取费)[name: 材料费, code: CLF, base: 取费定额乙供材料费不含税, rate: 100]
└── FeeTableTemplateItem(安装取费表)
├── FeeCollection(利润)[费率:7%]
└── FeeCollection(税金)[费率:9%]
└── FeeTableTemplateItem(取费表模板项) [name: 大型土石方取费表]
├── FeeCollection(取费)[name: 税金, code: TFFS, rate: 9]
└── FeeCollection(取费)[name: 合计, code: THJ, rate: 100]
+46
View File
@@ -0,0 +1,46 @@
{"name": "杆塔总基数", "query": "返回项目划分【架空输电线路本体工程/基础工程】下编码包含【YX2-1~7】的所有【定额】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n \n def expand_code_range(code_range):\n if '~' not in code_range:\n return code_range\n prefix, range_part = code_range.split('-')\n start, end = map(int, range_part.split('~'))\n return '/'.join([f\"{prefix}-{i}\" for i in range(start, end + 1)])\n \n expanded_codes = expand_code_range('YX2-1~7')\n \n result_dict = project.get_quantities_node_by_parent_and_code(\n parent_path='架空输电线路本体工程/基础工程',\n quantity_type='定额',\n code=expanded_codes\n )\n \n status = result_dict.get('status', False)\n if not status:\n return result_dict\n \n data = result_dict.get('data', [])\n if not isinstance(data, list):\n data = [data]\n \n total_quantity = 0.0\n for item in data:\n if isinstance(item, dict) and '数量' in item:\n try:\n quantity = float(item['数量'])\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': {'total_quantity': total_quantity}\n }"}
{"name": "角钢塔_塔材量", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_quantities_node_by_parent_and_name(division_path, \"主材\", \"角钢\")\n if not result.get('status', False):\n return result\n \n total_quantity = 0.0\n for material in result.get('data', []):\n if isinstance(material, dict) and material.get('类型') == '主材' and '角钢' in material.get('name', ''):\n try:\n quantity = float(material.get('数量', 0))\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_quantity\n }"}
{"name": "角钢塔_其中:高强钢塔材量", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢、高强】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_division_item_by_path(division_path)\n if not result.get('status', False):\n return result\n \n parent_path = division_path\n quantity_type = \"主材\"\n partial_name = \"角钢、高强\"\n result = project.get_quantities_node_by_parent_and_name(parent_path, quantity_type, partial_name)\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n total_quantity = 0.0\n for material in materials:\n if isinstance(material, Material):\n try:\n quantity = float(material.数量) if material.数量 else 0.0\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_quantity\n }"}
{"name": "角钢塔_塔材装材费_元", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_division_item_by_path(division_path)\n if not result.get('status', False):\n return result\n \n division_data = result.get('data', {})\n if not division_data:\n return {'code': 201, 'message': '项目划分节点不存在', 'status': False, 'data': []}\n \n parent_path = division_path\n quantity_type = \"主材\"\n partial_name = \"角钢\"\n result = project.get_quantities_node_by_parent_and_name(parent_path, quantity_type, partial_name)\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n if not materials:\n return {'code': 201, 'message': '未找到匹配的主材', 'status': False, 'data': []}\n \n total_price = 0.0\n for material in materials:\n if isinstance(material, dict):\n price_str = material.get('单价含税', '0')\n else:\n price_str = getattr(material, '单价含税', '0')\n try:\n price = float(price_str) if price_str else 0.0\n total_price += price\n except ValueError:\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_price}"}
{"name": "角钢塔_其中:高强钢塔材费用_元", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢、高强】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_quantities_node_by_parent_and_name(division_path, \"主材\", \"角钢、高强\")\n if not result.get('status', False):\n return result\n \n total_price = 0.0\n for material in result.get('data', []):\n if isinstance(material, Material):\n try:\n price = float(material.单价含税) if material.单价含税 else 0.0\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "钢管塔_塔材量", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管塔】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_division_item_by_path(division_path)\n if not result.get('status', False):\n return result\n \n parent_path = division_path\n quantity_type = \"主材\"\n partial_name = \"钢管塔\"\n result = project.get_quantities_node_by_parent_and_name(parent_path, quantity_type, partial_name)\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n total_quantity = 0.0\n for material in materials:\n if isinstance(material, dict) and material.get('类型') == '1' and '钢管塔' in material.get('name', ''):\n try:\n quantity = float(material.get('数量', 0))\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_quantity\n }"}
{"name": "钢管塔_钢管价格_元", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管塔】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_quantities_node_by_parent_and_name(path, \"主材\", \"钢管塔\")\n if not result.get('status', False):\n return result\n \n total_price = 0.0\n for material in result.get('data', []):\n if isinstance(material, Material) and \"钢管塔\" in material.name:\n try:\n price = float(material.单价含税) if material.单价含税 else 0.0\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "钢管杆_塔材量", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管杆】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_division_item_by_path(division_path)\n if not result.get('status', False):\n return result\n \n division_data = result.get('data', {})\n if not division_data:\n return {'code': 201, 'message': '未找到指定项目划分', 'status': False, 'data': []}\n \n parent_path = division_path\n quantity_type = \"主材\"\n partial_name = \"钢管杆\"\n result = project.get_quantities_node_by_parent_and_name(parent_path, quantity_type, partial_name)\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n if not materials:\n return {'code': 201, 'message': '未找到包含\"钢管杆\"的主材', 'status': False, 'data': []}\n \n total_quantity = 0.0\n for material in materials:\n if isinstance(material, dict):\n quantity_str = material.get('数量', '0')\n else:\n quantity_str = getattr(material, '数量', '0')\n try:\n quantity = float(quantity_str)\n total_quantity += quantity\n except ValueError:\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_quantity}"}
{"name": "钢管杆_钢管价格_元", "query": "返回项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管杆】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result = project.get_quantities_node_by_parent_and_name(division_path, \"主材\", \"钢管杆\")\n if not result.get('status', False):\n return result\n \n total_price = 0.0\n materials = result.get('data', [])\n for material in materials:\n if isinstance(material, Material) and \"单价\" in material.__dict__:\n try:\n price = float(material.单价)\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "导线及线材_分裂数", "query": "返回项目划分【架空输电线路本体工程/架线工程】下编码包含【['YX5-67', 'YX5-68', 'YX5-69', 'YX5-78', 'YX5-79', 'YX5-59', 'YX5-60', 'YX5-61', 'YX5-62', 'YX5-63', 'YX5-64', 'YX5-65', 'YX5-66', 'YX5-74', 'YX5-75', 'YX5-76', 'YX5-77', 'YX5-52', 'YX5-53', 'YX5-54', 'YX5-55', 'YX5-56', 'YX5-57', 'YX5-58', 'YX5-70', 'YX5-71', 'YX5-72', 'YX5-73', 'YX5-14', 'YX5-15', 'YX5-16', 'YX5-17', 'YX5-43', 'YX5-44', 'YX5-45', 'YX5-46', 'YX5-47', 'YX5-48', 'YX5-49', 'YX5-50', 'YX5-51', 'YX5-10', 'YX5-11', 'YX5-12', 'YX5-13', 'YX5-38', 'YX5-39', 'YX5-40', 'YX5-41', 'YX5-42', 'YX5-8', 'YX5-9']】的所有【定额】的【参数】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n codes = ['YX5-67', 'YX5-68', 'YX5-69', 'YX5-78', 'YX5-79', 'YX5-59', 'YX5-60', 'YX5-61', 'YX5-62', 'YX5-63', 'YX5-64', 'YX5-65', 'YX5-66', 'YX5-74', 'YX5-75', 'YX5-76', 'YX5-77', 'YX5-52', 'YX5-53', 'YX5-54', 'YX5-55', 'YX5-56', 'YX5-57', 'YX5-58', 'YX5-70', 'YX5-71', 'YX5-72', 'YX5-73', 'YX5-14', 'YX5-15', 'YX5-16', 'YX5-17', 'YX5-43', 'YX5-44', 'YX5-45', 'YX5-46', 'YX5-47', 'YX5-48', 'YX5-49', 'YX5-50', 'YX5-51', 'YX5-10', 'YX5-11', 'YX5-12', 'YX5-13', 'YX5-38', 'YX5-39', 'YX5-40', 'YX5-41', 'YX5-42', 'YX5-8', 'YX5-9']\n total = 0.0\n path = '架空输电线路本体工程/架线工程'\n for code in codes:\n result_dict = project.get_quantities_node_by_parent_and_code(path, '定额', code)\n if not result_dict.get('status', False):\n return result_dict\n data = result_dict.get('data', [])\n if isinstance(data, list):\n for item in data:\n if hasattr(item, '参数'):\n total += float(getattr(item, '参数', 0))\n elif hasattr(data, '参数'):\n total += float(getattr(data, '参数', 0))\n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total}"}
{"name": "导线及线材_其中:节能导线量", "query": "返回项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【高导电率】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n total_quantity = 0.0\n \n division_paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in division_paths:\n quantities_result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n quantity_type=\"主材\",\n partial_name=\"高导电率\"\n )\n \n if not quantities_result.get('status', False):\n return quantities_result\n \n materials = quantities_result.get('data', [])\n for material in materials:\n if isinstance(material, Material) and \"高导电率\" in material.name:\n try:\n quantity = float(material.数量) if material.数量 else 0.0\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_quantity\n }"}
{"name": "导线及线材_导线装材费_元", "query": "返回项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【线】的所有【主材】的【单价】之和", "code": "```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n total_price = 0.0\n \n division_paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in division_paths:\n quantities_result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n quantity_type=\"主材\",\n partial_name=\"线\"\n )\n \n if not quantities_result.get('status', False):\n return quantities_result\n \n materials = quantities_result.get('data', [])\n for material in materials:\n if isinstance(material, Material) and \"线\" in material.name:\n try:\n price = float(material.单价含税) if material.单价含税 else 0.0\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }\n```"}
{"name": "导线及线材_其中:节能导线费用_元", "query": "返回项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【高导电率】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n total_price = 0.0\n \n division_paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in division_paths:\n quantities_result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n quantity_type=\"主材\",\n partial_name=\"高导电率\"\n )\n \n if not quantities_result.get('status', False):\n continue\n \n materials = quantities_result.get('data', [])\n for material in materials:\n if isinstance(material, Material) and \"高导电率\" in material.name:\n try:\n price = float(material.单价含税) if material.单价含税 else 0.0\n total_price += price\n except (ValueError, AttributeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "导线及线材_导线类型", "query": "返回项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设@@架空输电线路本体工程/架线工程/其他架线工程】及名称包含【['钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', '扩径导线', '耐热导线', '碳纤维导线']】的所有【主材】的【参数】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\",\n \"架空输电线路本体工程/架线工程/其他架线工程\"\n ]\n material_names = [\n '钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线',\n '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线',\n '扩径导线', '耐热导线', '碳纤维导线'\n ]\n \n total_params = 0.0\n \n for path in division_paths:\n result = project.get_quantities_node_by_parent_and_name(path, \"主材\", \"\")\n if not result.get('status', False):\n continue\n \n materials = result.get('data', [])\n for material in materials:\n if isinstance(material, dict):\n name = material.get('name', '')\n if name in material_names:\n params = float(material.get('参数', 0.0))\n total_params += params\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_params\n }"}
{"name": "基础钢材量", "query": "返回项目划分【架空输电线路本体工程/基础工程/基础砌筑】及名称包含【圆钢】的所有【主材】的【数量】之和", "code": "根据用户问题和上下文信息,我将生成对应的Python代码:\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n \n division_path = \"架空输电线路本体工程/基础工程/基础砌筑\"\n quantity_type = \"主材\"\n partial_name = \"圆钢\"\n \n result = project.get_quantities_node_by_parent_and_name(division_path, quantity_type, partial_name)\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n if not isinstance(materials, list):\n materials = [materials]\n \n total_quantity = 0.0\n for material in materials:\n if not isinstance(material, dict):\n continue\n quantity_str = material.get('数量', '0')\n try:\n quantity = float(quantity_str)\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': {\n 'total_quantity': total_quantity,\n 'unit': '个' if materials else '',\n 'material_count': len(materials)\n }\n }\n```"}
{"name": "基础钢材价格", "query": "返回项目划分【架空输电线路本体工程/基础工程/基础砌筑】及名称包含【圆钢】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n division_path = \"架空输电线路本体工程/基础工程/基础砌筑\"\n result = project.get_quantities_node_by_parent_and_name(division_path, \"主材\", \"圆钢\")\n if not result.get('status', False):\n return result\n \n materials = result.get('data', [])\n total_price = 0.0\n \n for material in materials:\n if isinstance(material, Material) and \"圆钢\" in material.name:\n try:\n price = float(material.单价含税) if material.单价含税 else 0.0\n total_price += price\n except (ValueError, AttributeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "本体费用合计_元", "query": "从【工程费用】中获取【架空输电线路本体工程】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"架空输电线路本体工程\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程人工费_本体_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【人工费】的费用", "code": "根据用户问题和上下文信息,我们需要获取项目划分\"架空输电线路本体工程\"下取费名称为\"人工费\"的费用。根据工程数据访问库,最匹配的方法是`get_fee_table_by_project_division`。\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\n project_division_path=\"架空输电线路本体工程\",\n fee_name=\"人工费\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "本体工程人工费_调试_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【人工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程\", \"人工费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程机械费_本体_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【施工机械使用费】的费用", "code": "根据用户问题和上下文信息,我将生成对应的Python代码:\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\n project_division_path=\"架空输电线路本体工程\",\n fee_name=\"施工机械使用费\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "本体工程机械费_调试_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【施工机械使用费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程\", \"施工机械使用费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "基础工程费用_元", "query": "返回项目划分【架空输电线路本体工程/基础工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/基础工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "杆塔工程费用_元", "query": "返回项目划分【架空输电线路本体工程/杆塔工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/杆塔工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "接地工程费用_元", "query": "返回项目划分【架空输电线路本体工程/接地工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/接地工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "架线工程费用_元", "query": "返回项目划分【架空输电线路本体工程/架线工程】下取费名称等于【合计】的费用", "code": "根据用户问题和上下文信息,我们需要使用`get_fee_table_by_project_division`方法来获取项目划分路径下取费名称为\"合计\"的费用。以下是生成的Python代码:\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/架线工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "附件工程费用_元", "query": "返回项目划分【架空输电线路本体工程/附件安装工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/附件安装工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助工程费用_元", "query": "返回项目划分【架空输电线路本体工程/辅助工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/辅助工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助工程费用_调试_元", "query": "返回项目划分【架空输电线路本体工程/辅助工程】下取费名称等于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程/辅助工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助设施工程_元", "query": "从【工程费用】中获取【辅助设施工程】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用\", \"辅助设施工程\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'code': code, 'message': message, 'data': data}\n else:\n return {'status': False, 'code': code, 'message': message, 'data': data}"}
{"name": "其他费用合计_元", "query": "从【工程费用】中获取【其他费用】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"其他费用\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "建场费合计_元", "query": "从【其他费用】中获取【建设场地征用及清理费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\n table_name=\"其他费用表\",\n fee_name=\"建设场地征用及清理费\",\n fee_attribute=\"amount\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "项目建设管理费合计_元", "query": "从【其他费用】中获取【项目建设管理费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '项目建设管理费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'data': data, 'message': message, 'code': code}\n else:\n return {'status': False, 'data': {}, 'message': message, 'code': code}"}
{"name": "其中:工程监理费_元", "query": "从【其他费用】中获取【工程监理费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '工程监理费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'data': data}\n else:\n return {'status': False, 'message': message, 'code': code}"}
{"name": "项目建设技术服务费合计_元", "query": "从【其他费用】中获取【项目建设技术服务费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '项目建设技术服务费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:项目前期工作费_元", "query": "从【其他费用】中获取【项目前期工作费】的值", "code": "根据用户问题和上下文信息,我们需要从\"其他费用\"中获取\"项目前期工作费\"的值。根据上下文信息,这涉及到\"其他费用表\"和\"项目前期工作费\"两个节点,且\"项目前期工作费\"是\"其他费用表\"的子节点。\n\n最匹配的工程数据访问库函数是`get_fee_schedule_on_other_expense_table`,因为它专门用于在其他费用表中查找费用。\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\n table_name=\"其他费用表\",\n fee_name=\"项目前期工作费\",\n fee_attribute=\"amount\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "其中:勘察费_元", "query": "从【其他费用】中获取【勘察费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '勘察费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'data': data}\n else:\n return {'status': False, 'message': message, 'code': code}"}
{"name": "其中:设计费_元", "query": "从【其他费用】中获取【设计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '设计费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:工程建设检测费_元", "query": "从【其他费用】中获取【工程建设检测费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '工程建设检测费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'data': data}\n else:\n return {'status': False, 'message': message, 'code': code}"}
{"name": "生产准备费_元", "query": "从【其他费用】中获取【生产准备费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '生产准备费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'code': code, 'message': message, 'data': data}\n else:\n return {'status': False, 'code': code, 'message': message, 'data': data}"}
{"name": "其中:安全文明施工费_线路_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【安全文明施工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程\", \"安全文明施工费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:安全文明施工费_调试_元", "query": "返回项目划分【架空输电线路本体工程】下取费名称等于【安全文明施工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_table_by_project_division(\"架空输电线路本体工程\", \"安全文明施工费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "基本预备费_元", "query": "从【工程费用】中获取【基本预备费】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"基本预备费\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "静态投资_元", "query": "从【工程费用】中获取【工程静态投资(一~七项合计)】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table('工程费用表', '工程静态投资(一~七项合计)', '合计费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'code': code, 'message': message, 'data': data}\n else:\n return {'status': False, 'code': code, 'message': message, 'data': data}"}
{"name": "建设期利息_元", "query": "从【工程费用】中获取【建设期贷款利息】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table('工程费用表', '建设期贷款利息', '合计费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return {'status': True, 'code': code, 'message': message, 'data': data}\n else:\n return {'status': False, 'code': code, 'message': message, 'data': {}}"}
{"name": "动态投资_元", "query": "从【工程费用】中获取【工程动态投资(一~八项合计)】的【合计费】的值", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"工程动态投资(一~八项合计)\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "增值税抵扣税额_元", "query": "从【工程费用】中获取【其中:可抵扣增值税额】的【合计费】的值", "code": "根据用户问题和上下文信息,我们需要从工程费用表中获取\"其中:可抵扣增值税额\"的\"合计费\"值。根据上下文信息,这是一个三级嵌套的费用结构:工程费用 → 其中:可抵扣增值税额 → 合计费。\n\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\n table_name=\"工程费用表\",\n fee_name=\"其中:可抵扣增值税额/合计费\",\n fee_attribute=\"合计费\"\n )\n status = result_dict.get('status', False)\n if not status:\n raise Exception(f\"获取费用失败: {result_dict.get('message', '未知错误')}\")\n return result_dict"}
File diff suppressed because it is too large Load Diff
Binary file not shown.
+43 -52
View File
@@ -1,64 +1,54 @@
# main.py
import os
import logging
from datetime import datetime
import logging
import os
import asyncio
import os
current_file = os.path.splitext(os.path.basename(__file__))[0]
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
log_filename = f"{current_file}_{now_str}.log"
logging.basicConfig(
level=logging.DEBUG, # 生产环境可改为 INFO 或 WARNING
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("BoweiAgent.log", encoding="utf-8"), logging.StreamHandler()],
handlers=[
logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"),
logging.StreamHandler()
],
)
logger = logging.getLogger("BoweiAgent")
logger = logging.getLogger(current_file)
import logging
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
# 获取logger并设置级别
httpx_logger = logging.getLogger("httpx")
httpx_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
httpx_logger.propagate = False
# 获取logger并设置级别
openai_logger = logging.getLogger("openai")
openai_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
openai_logger.propagate = False
# 获取logger并设置级别
langsmith_logger = logging.getLogger("langsmith.client")
langsmith_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
langsmith_logger.propagate = False
# 获取logger并设置级别
neo4j_logger = logging.getLogger("neo4j")
neo4j_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
neo4j_logger.propagate = False
# 获取logger并设置级别
urllib3_logger = logging.getLogger("urllib3")
urllib3_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
urllib3_logger.propagate = False
# 获取logger并设置级别
httpcore_logger = logging.getLogger("httpcore")
httpcore_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
# 可选:禁用传播(防止被根logger处理)
httpcore_logger.propagate = False
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
from src.config import Config
from src.document_loader import load_file
from src.llm_client import LLMClient
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.prompt_manager import PromptManager
from src.code_executor import CodeExecutor
from src.dialog_manager import DialogManager
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.embedding_client import EmbeddingClient
from project import ProjectBuilder
from project_implementation import ProjectToolkitNeo4j
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
def main():
@@ -69,14 +59,11 @@ def main():
os.environ["LANGSMITH_TRACING"] = "true" if tracing_enabled else "false"
os.environ["LANGSMITH_API_KEY"] = config.langsmith.get("api_key")
#os.environ["LANGSMITH_API_KEY"] = config.langsmith.get("api_url")
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = LLMClient(config.openai)
llm_client_coder = LLMClient(config.openai_coder)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
@@ -85,11 +72,11 @@ def main():
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, neo4j_conf)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
@@ -100,10 +87,14 @@ def main():
prompt_manager,
)
pre_input_question = "查找名称中包含“工程”的项目划分项,并返回其人工费乘以1000的值。"
pre_input_question = "查找名称中包含“工程”的项目划分项,并返回单位。"
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回其人工费乘以1000的值。'
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回单位。'
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
try:
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
finally:
neo4j_driver.close()
if __name__ == "__main__":
+1032
View File
File diff suppressed because it is too large Load Diff
+245
View File
@@ -0,0 +1,245 @@
import streamlit as st
import asyncio
import time
from datetime import datetime
import logging
from typing import List, Tuple, Optional, Dict, Any
import sys
import os
# 将当前路径添加到系统路径,确保可以导入其他模块
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# 配置日志
current_file = os.path.splitext(os.path.basename(__file__))[0]
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
log_filename = f"{current_file}_{now_str}.log"
# 确保日志目录存在并设置在logs目录下
log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logs")
os.makedirs(log_dir, exist_ok=True)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join(log_dir, log_filename), encoding="utf-8"),
logging.StreamHandler()
],
)
logger = logging.getLogger(current_file)
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
#from langfuse import Langfuse
#from langfuse.langchain import CallbackHandler
from src.code_executor import CodeExecutor
from src.config import Config
#langfuse = Langfuse(
# public_key="pk-lf-4c2b8e97-dd44-41be-bd35-a643cdc8d778",
# secret_key="sk-lf-d00b0882-cd91-4df7-b3a7-658d9d394568",
# host="http://10.1.6.34:3000"
#)
#langfuse_handler = CallbackHandler()
langfuse_handler = None
import os
# Update this with your self-hosted endpoint
os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "http://10.1.6.34:6006/v1/traces"
import phoenix as px
from phoenix.otel import register
from openinference.instrumentation.langchain import LangChainInstrumentor
# configure the Phoenix tracer
tracer_provider = register(
endpoint="http://10.1.6.34:6006/v1/traces",
project_name="my-llm-app", # Default is 'default'
auto_instrument=True, # See 'Trace all calls made to a library' below
verbose=True
)
#tracer = tracer_provider.get_tracer(__name__)
LangChainInstrumentor(tracer_provider=tracer_provider).instrument(skip_dep_check=False)
# 页面配置
st.set_page_config(
page_title="博微造价工程数据问答系统",
page_icon="📊",
layout="wide",
)
# 初始化会话状态
if "messages" not in st.session_state:
st.session_state.messages = []
if "processing" not in st.session_state:
st.session_state.processing = False
# 加载配置和初始化组件
@st.cache_resource
def initialize_components():
"""加载配置并初始化必要组件"""
config = Config()
# 导入必要模块
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.prompt_manager import PromptManager
from src.dialog_manager import DialogManager
from src.document_loader import load_file
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.embedding_client import EmbeddingClient
from langchain_neo4j import Neo4jGraph
from src.engineeringqaagent import EngineeringQAAgent
# 初始化LLM客户端
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
llm_client_qa = MultiAPIKeyChatOpenAI(config.openai_qa)
# 初始化提示词管理器
prompt_manager = PromptManager()
# 初始化代码执行器 - 设置最大重试次数为3
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, max_retries=3)
# 加载API文档
api_docs_path = config.bowei_api_docs_path
bowei_api_docs = load_file(api_docs_path)
# 加载业务对象结构
business_structure_path = config.business_object_structure_path
business_structure = load_file(business_structure_path)
# 初始化嵌入客户端
embedding_client = EmbeddingClient(config.openai)
# 初始化知识检索器
knowledge_retriever = Neo4jRawRetriever(config.neo4j_conf)
# 初始化Neo4jGraph
graph = Neo4jGraph(
url=config.neo4j_conf.get("uri"),
username=config.neo4j_conf.get("username"),
password=config.neo4j_conf.get("password")
)
# 初始化EngineeringQAAgent
qa_agent = EngineeringQAAgent(llm_client_qa, embedding_client, graph, None, callbacks=[langfuse_handler] if langfuse_handler else None)
# 初始化对话管理器
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
st.session_state.dialog_manager = dialog_manager
st.session_state.qa_agent = qa_agent
print('初始化组件完成')
return code_executor, config
# 初始化组件
try:
code_executor, config = initialize_components()
except Exception as e:
st.error(f"初始化组件失败: {str(e)}")
st.stop()
# 页面标题
st.title("博微造价工程数据问答系统")
st.markdown("---")
# 侧边栏
with st.sidebar:
st.header("系统信息")
st.info("博微造价工程数据问答系统是一个基于LLM的智能工具,可以帮助您生成并执行Python代码来回答问题。")
st.markdown("### 使用说明")
st.markdown("1. 在输入框中输入您的问题")
st.markdown("2. 系统将生成并执行相关代码")
st.markdown("3. 查看执行结果和生成的代码")
st.markdown("---")
if st.button("清空对话历史"):
st.session_state.messages = []
st.rerun()
# 显示聊天历史
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
# 处理用户问题
async def process_user_question(user_question):
"""处理用户输入的问题"""
st.session_state.processing = True
# 将用户问题添加到会话历史
st.session_state.messages.append({"role": "user", "content": user_question})
# 显示用户消息
with st.chat_message("user"):
st.write(user_question)
# 显示系统思考中消息
with st.chat_message("assistant"):
thinking_placeholder = st.empty()
thinking_placeholder.markdown("🤔 思考中...")
# 使用EngineeringQAAgent处理用户问题
try:
# 获取QA代理
qa_agent = st.session_state.get('qa_agent')
if not qa_agent:
raise Exception("问答代理未初始化")
# 使用EngineeringQAAgent处理问题
answer = qa_agent.ask(user_question)
import re
cleaned_answer = re.sub(r'`{3}', '', answer, flags=re.DOTALL) # 跨行匹配
# 显示回答
thinking_placeholder.markdown(cleaned_answer)
# 将回答添加到会话历史
st.session_state.messages.append({"role": "assistant", "content": answer})
except Exception as e:
thinking_placeholder.markdown(f"处理问题时出错: {str(e)}")
logger.error(f"处理问题时出错: {str(e)}")
st.session_state.messages.append({"role": "assistant", "content": f"处理问题时出错: {str(e)}"})
st.session_state.processing = False
# 用户输入框
if not st.session_state.processing:
user_question = st.chat_input("请输入您的问题...")
if user_question:
asyncio.run(process_user_question(user_question))
if __name__ == "__main__":
# Streamlit运行此文件时的入口点
pass
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -3,4 +3,7 @@ langchain-core
langchain-experimental
pyyaml
neo4j
langchain-neo4j
langchain-neo4j
langgraph
chardet
streamlit-textcomplete
+156 -56
View File
@@ -1,48 +1,112 @@
# src/code_executor.py
import os
import logging
from datetime import datetime
import logging
from langchain_core.output_parsers import StrOutputParser
from langchain_experimental.utilities import PythonREPL
from langchain_core.tools import Tool
from langchain_experimental.tools import PythonREPLTool
from project import ProjectBuilder
from src.project import ProjectBuilder, ProjectToolkit
import sys
import io
import traceback
import importlib
logger = logging.getLogger("BoweiAgent.CodeExecutor")
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class CodeExecutor:
def __init__(self, prompts, llm_client, max_retries=3):
def __init__(self, prompts, llm_client, max_retries):
self.llm_client = llm_client
self.prompts = prompts
self.max_retries = max_retries
self.max_retries = max_retries if max_retries >= 1 else 1
self.output_parser = StrOutputParser()
def generate_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> str:
def generate_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> dict:
logger.info(f"开始生成代码,访问请求:{user_request}")
prompt = self.prompts.code_gen_prompt.format_prompt(
user_request=user_request, context=context, bowei_api_docs=bowei_api_docs
)
response = self.llm_client.invoke(prompt.to_messages())
code = self.output_parser.parse(response)
logger.debug(f"生成的代码内容:\n{code}")
return code
try:
response = self.llm_client.invoke(prompt.to_messages())
parsed_response = self.output_parser.parse(response)
# 处理 AIMessage 类型的返回值
if hasattr(parsed_response, 'content'):
code = parsed_response.content
else:
code = str(parsed_response)
logger.debug(f"生成的代码内容:\n{code}")
return {
"code": 20000,
"message": 'ok',
"status": True,
"data": code
}
except Exception as e:
logger.error(f"大模型调用失败: {str(e)}", exc_info=True)
return {
"code": 50000,
"message": f'大模型调用失败: {str(e)}',
"status": False,
"data": None
}
def fix_code(self, code: str, error: str) -> str:
def fix_code(self, code: str, error: str) -> dict:
logger.warning(f"代码执行出错,开始修复。错误信息:{error}")
prompt = self.prompts.code_fix_prompt.format_prompt(code=code, error=error)
response = self.llm_client.invoke(prompt.to_messages())
fixed_code = self.output_parser.parse(response)
logger.debug(f"修复后的代码内容:\n{fixed_code}")
return fixed_code
try:
response = self.llm_client.invoke(prompt.to_messages())
parsed_response = self.output_parser.parse(response)
# 处理 AIMessage 类型的返回值
if hasattr(parsed_response, 'content'):
fixed_code = parsed_response.content
else:
fixed_code = str(parsed_response)
logger.debug(f"修复后的代码内容:\n{fixed_code}")
return {
"code": 20000,
"message": 'ok',
"status": True,
"data": fixed_code
}
except Exception as e:
logger.error(f"代码修复时大模型调用失败: {str(e)}", exc_info=True)
return {
"code": 50001,
"message": f'代码修复失败: {str(e)}',
"status": False,
"data": None
}
def execute_code(self, code_str):
def execute_code(self, code_str) -> dict:
"""封装代码执行逻辑"""
logger.debug(f"开始执行代码: {code_str}")
logger.debug(f"开始执行代码:\n {code_str}")
try:
import re
pattern = r'```python(.*?)```'
match = re.search(pattern, code_str, re.DOTALL)
if match:
code_str = match.group(1).strip()
except Exception as e:
logger.warning(f"解析生成代码格式时发生异常: {str(e)}")
old_stdout = None
try:
namespace = {
"project": __import__("project"),
"project": importlib.import_module("src.project"),
"Material": getattr(importlib.import_module("src.project"), "Material", None),
"Ration": getattr(importlib.import_module("src.project"), "Ration", None),
"Equipment": getattr(importlib.import_module("src.project"), "Equipment", None),
"MaterialOrEquipment": getattr(importlib.import_module("src.project"), "MaterialOrEquipment", None),
"ProjectBuilder": ProjectBuilder,
"logger": logger,
}
old_stdout = sys.stdout
@@ -55,65 +119,101 @@ class CodeExecutor:
if "project_get_calculate_function" not in namespace:
raise ValueError("代码中未定义project_get_calculate_function函数")
result_tuple = namespace["project_get_calculate_function"]()
result_dict = namespace["project_get_calculate_function"]()
sys.stdout = old_stdout
output = redirected_output.getvalue().strip()
if not isinstance(result_tuple, tuple) or len(result_tuple) != 4:
raise ValueError("函数应返回包含4个元素的元组(status, data, error, helper_info)")
if not isinstance(result_dict, dict) or len(result_dict) < 3:
raise ValueError("函数应返回至少包含3个元素的字典(status, message, data)")
status, data, error, helper_info = result_tuple
logger.info(f"执行结果: status={status}, data={data}, error={error}")
return {
"status": status,
"data": data,
"error": error,
"helper_info": helper_info,
"output": output,
}
logger.debug(f"执行结果: {result_dict}")
return result_dict
except Exception as e:
# 确保恢复stdout
sys.stdout = old_stdout
logger.error(f"执行代码时出错: {e}")
logger.error(traceback.format_exc())
return {
"status": "error",
"error": str(e),
"helper_info": [],
"traceback": traceback.format_exc(),
"code": 50000,
"message": str(e),
"status": False,
"data": []
}
def generate_and_run_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> str:
code = self.generate_code(user_request, context, bowei_api_docs)
logger.info("开始执行生成的代码")
def generate_and_run_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> dict:
code = ""
pre_code = ""
error_msg = ""
prev_happend_error = False
result = self.generate_code(user_request, context, bowei_api_docs)
if isinstance(result, dict) and result.get('status', False):
code = result['data']
elif isinstance(result, dict):
error_msg = result.get("message", "返回结果中没有错误信息")
logger.error(error_msg)
return {
"code": 50000,
"message": error_msg,
"status": False,
"data": code
}
else:
error_msg = "返回结果格式未知"
logger.error(error_msg)
return {
"code": 50000,
"message": error_msg,
"status": False,
"data": code
}
logger.info("开始执行生成的代码")
for attempt in range(self.max_retries):
try:
if prev_happend_error:
logger.error(f"代码执行失败,尝试第 {attempt+1} 次修复。错误信息:{error_msg}")
code = self.fix_code(pre_code, error_msg)
import re
pre_code = re.sub(r"^```python\s*|\s*```$", "", code.content, flags=re.MULTILINE)
result = self.execute_code(pre_code)
if result["status"] == "success":
logger.info(f"代码执行成功,结果: {result['data']}")
return result["data"]
else:
error_msg = result.get("error", "未知错误")
prev_happend_error = True
result = self.fix_code(pre_code, error_msg)
if isinstance(result, dict) and result.get('status', False):
code = result['data']
elif isinstance(result, dict):
error_msg = result.get("message", "返回结果中没有错误信息")
raise ValueError(error_msg)
else:
error_msg = "返回结果格式未知"
raise ValueError(error_msg)
except Exception as e:
error_msg = str(e)
error_msg = f"生成修复代码时发生异常: {str(e)}"
prev_happend_error = True
logger.error(f"代码执行失败,超过最大重试次数 {self.max_retries}")
return f"代码执行失败,超过最大重试次数 {self.max_retries}\n最后一次错误信息:\n{error_msg}"
try:
import re
pre_code = re.sub(r"^```python\s*|\s*```$", "", code, flags=re.MULTILINE)
except Exception as e:
error_msg = f"解析生成代码格式时发生异常: {str(e)}"
prev_happend_error = True
try:
result = self.execute_code(pre_code)
if isinstance(result, dict) and result.get('status', False):
logger.info(f"代码执行成功,结果: {result['data']}")
return result
elif isinstance(result, dict):
error_msg = result.get("message", "返回结果中没有错误信息")
else:
error_msg = "返回结果格式未知"
prev_happend_error = True
except Exception as e:
error_msg = f"执行代码时发生异常: {str(e)}"
prev_happend_error = True
error_msg = f"代码执行失败,最后一次错误信息: {error_msg}"
logger.error(error_msg)
return {
"code": 50000,
"message": error_msg,
"status": False,
"data": []
}
+13
View File
@@ -1,4 +1,9 @@
# src/config.py
import yaml
import os
import logging
from datetime import datetime
class Config:
def __init__(self, path="config.yaml"):
@@ -16,6 +21,10 @@ class Config:
def openai_coder(self):
return self._config.get("openai_coder", {})
@property
def openai_qa(self):
return self._config.get("openai_qa", {})
@property
def bowei_api_docs_path(self):
return self._config.get("bowei_api_docs_path", "./data/bowei_api_docs.md")
@@ -35,3 +44,7 @@ class Config:
@property
def langsmith(self):
return self._config.get("langsmith", {})
@property
def max_retries(self):
return self._config.get("max_retries", 3)
+80 -48
View File
@@ -1,10 +1,14 @@
# src/dialog_manager.py
import os
import logging
from datetime import datetime
import logging
from langchain.schema import SystemMessage, HumanMessage
import asyncio
logger = logging.getLogger("BoweiAgent.DialogManager")
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class QuestionProcessor:
def __init__(self, llm_client, business_structure, prompts):
@@ -112,12 +116,21 @@ class DialogManager:
rewritten_list.append((rewritten, doc.page_content))
return rewritten_list
async def run_async(self, pre_input: str = None, automated: bool = False):
def understand_user_question(self, user_input: str):
"""
同步调用 understand_user_question_stream 方法
:param user_input: 用户输入的问题
:return: 改写后的问题列表
"""
try:
return asyncio.run(self.understand_user_question_stream(user_input))
except Exception as e:
logger.error(f"执行 understand_user_question 时发生错误: {str(e)}")
return []
async def run_async(self, pre_input: str = None):
logger.info("启动对话管理器,等待用户输入")
if automated:
print("自动化模式已启动。")
else:
print("欢迎使用博微造价工程数据访问系统,输入 exit 退出。")
print("欢迎使用博微造价工程数据访问系统,输入 exit 退出。")
if pre_input:
user_questions = [pre_input]
@@ -127,16 +140,11 @@ class DialogManager:
while True:
if user_questions:
user_question = user_questions.pop(0)
if not automated:
print(f"预输入问题:{user_question}")
elif automated:
if not user_questions:
logger.info("自动化模式下没有更多问题,退出程序。")
break
print(f"预输入问题:{user_question}")
else:
user_question = input("请输入您的问题:")
if user_question.strip().lower() == "exit" and not automated:
if user_question.strip().lower() == "exit":
logger.info("用户退出程序")
print("退出程序。")
break
@@ -147,38 +155,62 @@ class DialogManager:
user_questions.clear()
continue
if automated:
# 自动化模式下选择第一个结果
selected_rewritten, selected_knowledge = rewritten_results[0]
logger.info(f"自动化模式选择第一个访问请求,内容:{selected_rewritten}")
result = self.code_executor.generate_and_run_code(
selected_rewritten,
context=selected_knowledge,
bowei_api_docs=self.bowei_api_docs
)
logger.info("代码执行完成,返回结果")
print("\n访问结果:\n", result)
print("-" * 50)
else:
InteractionHandler.display_rewritten_requests(rewritten_results)
choice_index = InteractionHandler.get_user_choice(rewritten_results)
if choice_index is not None:
selected_rewritten, selected_knowledge = rewritten_results[choice_index]
logger.info(f"用户选择访问请求编号 {choice_index + 1},内容:{selected_rewritten}")
print(f"\n您选择的访问请求是:\n{selected_rewritten}\n")
print(f"相关知识内容:\n{selected_knowledge}\n")
#confirm = input("请确认是否继续执行该请求(输入n取消,其他继续):").strip().lower()
confirm = ""
if confirm == "n":
#logger.info("用户取消执行访问请求")
print("取消执行,您可以重新输入问题。\n" + "-"*50)
else:
#logger.info("用户确认执行访问请求")
result = self.code_executor.generate_and_run_code(
selected_rewritten,
context=selected_knowledge,
bowei_api_docs=self.bowei_api_docs
)
logger.info("代码执行完成,返回结果")
print("\n访问结果:\n", result)
print("-" * 50)
InteractionHandler.display_rewritten_requests(rewritten_results)
choice_index = InteractionHandler.get_user_choice(rewritten_results)
if choice_index is not None:
selected_rewritten, selected_knowledge = rewritten_results[choice_index]
logger.info(f"用户选择访问请求编号 {choice_index + 1},内容:{selected_rewritten}")
print(f"\n您选择的访问请求是:\n{selected_rewritten}\n")
print(f"相关知识内容:\n{selected_knowledge}\n")
#confirm = input("请确认是否继续执行该请求(输入n取消,其他继续):").strip().lower()
confirm = ""
if confirm == "n":
#logger.info("用户取消执行访问请求")
print("取消执行,您可以重新输入问题。\n" + "-"*50)
else:
#logger.info("用户确认执行访问请求")
result = self.execute_generated_code(
selected_rewritten,
selected_knowledge
)
print("\n访问结果:\n", result)
print("-" * 50)
def execute_generated_code(self, selected_rewritten, selected_knowledge):
"""
执行生成的代码并返回结果
:param selected_rewritten: 选中的重写后的请求
:param selected_knowledge: 选中的知识
:return: 代码执行结果
"""
result = self.code_executor.generate_and_run_code(
selected_rewritten,
context=selected_knowledge,
bowei_api_docs=self.bowei_api_docs
)
return result
def generated_code(self, selected_rewritten, selected_knowledge):
"""
生成代码
:param selected_rewritten: 选中的重写后的请求
:param selected_knowledge: 选中的知识
:return: 生成的代码
"""
result = self.code_executor.generate_code(
selected_rewritten,
context=selected_knowledge,
bowei_api_docs=self.bowei_api_docs
)
return result
def execute_code(self, code):
"""
执行代码并返回结果
:param code: 代码
:return: 代码执行结果
"""
result = self.code_executor.execute_code(code)
return result
+5
View File
@@ -1,3 +1,8 @@
# src/document_loader.py
import os
def load_file(path: str) -> str:
try:
with open(path, "r", encoding="utf-8") as f:
+8
View File
@@ -1,6 +1,14 @@
# src/embedding_client.py
import os
import logging
from datetime import datetime
from langchain_openai import OpenAIEmbeddings
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class EmbeddingClient:
def __init__(self, embedding_config: dict):
api_key = embedding_config.get("api_key")
+169
View File
@@ -0,0 +1,169 @@
from langchain_neo4j import GraphCypherQAChain, Neo4jGraph
from langchain.chains import RetrievalQA, LLMChain
from langchain_core.runnables import RunnableSequence
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_community.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from typing import Any
class EngineeringQAAgent:
def __init__(self, llm_client, embedding_client, graph: Neo4jGraph, business_retriever, callbacks=None):
"""
:param llm_client: 聊天大模型实例,如 ChatOpenAI
:param embedding_client: 嵌入模型实例,如 OpenAIEmbeddings(本类未直接使用,但保留)
:param graph: Neo4jGraph实例,连接工程知识图谱
:param business_retriever: 通用业务知识库的Retriever对象
"""
self.llm = llm_client.llm
self.embedding = embedding_client
self.business_retriever = business_retriever
self.callbacks = callbacks
self.kwargs = {}
if self.callbacks:
self.kwargs = {"callbacks": self.callbacks }
CYPHER_GENERATION_TEMPLATE = """Task:Generate Cypher statement to query a graph database.
Instructions:
Use only the provided relationship types and properties in the schema.
Do not use any other relationship types or properties that are not provided.
Schema:
{schema}
Note: Do not include any explanations or apologies in your responses.
Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.
Do not include any text except the generated Cypher statement.
The question is:
{question}"""
CYPHER_GENERATION_PROMPT = PromptTemplate(
input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE
)
# 工程知识图谱查询链
self.engineering_qa = GraphCypherQAChain.from_llm(
llm=self.llm,
graph=graph,
verbose=True,
allow_dangerous_requests=True,
cypher_prompt=CYPHER_GENERATION_PROMPT,
**self.kwargs
)
# Chain of Thought提示模板和链
cot_prompt = PromptTemplate(
input_variables=["question"],
template=(
"你是一个专业的电力造价工程助理。请先将用户的问题拆分成多个子问题,"
"分别检索相关的通用业务知识和工程数据知识库,最后综合回答用户。\n\n"
"用户问题:{question}\n\n"
"请给出拆分的子问题列表和每个子问题的检索计划,"
"然后给出最终综合回答。\n\n"
"思考过程:"
)
)
self.cot_chain = RunnableSequence(
cot_prompt | self.llm
).with_config(
verbose=True,
**self.kwargs
)
# 定义Agent工具
self.tools = [
Tool(
name="EngineeringData",
func=self.engineering_qa.invoke,
description="用于查询具体工程数据相关问题"
),
Tool(
name="BusinessKnowledge",
func=self._business_qa,
description="用于查询电力造价行业通用业务知识"
),
]
# 多轮对话记忆
message_history = ChatMessageHistory()
self.memory = ConversationBufferMemory(
chat_memory=message_history, memory_key="chat_history", return_messages=True)
# 初始化Agent,支持多轮对话和工具调用
self.agent = initialize_agent(
self.tools,
self.llm,
agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
memory=self.memory,
verbose=True,
**self.kwargs
)
def _business_qa(self, query: str) -> str:
if not self.business_retriever:
return "通用业务知识库未配置。"
business_qa = RetrievalQA(
llm=self.llm,
retriever=self.business_retriever,
return_source_documents=False,
**self.kwargs
)
return business_qa.invoke(query)
def ask(self, question: str) -> str:
# 1. 使用CoT链拆分问题和规划检索
cot_output = self.cot_chain.invoke(question)
# 2. 简单示例:先调用业务知识库,再调用工程数据知识库
business_answer = self._business_qa(question)
engineering_answer = self.engineering_qa.run(question)
# 3. 综合回答
final_answer = (
"根据通用业务知识库,得到的信息是:\n"
f"{business_answer}\n\n"
"根据工程数据知识库,得到的信息是:\n"
f"{engineering_answer}\n\n"
"综合以上信息,回答用户问题如下:\n"
f"{cot_output}"
)
return final_answer
# ------------------ 使用示例 ------------------
if __name__ == "__main__":
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.config import Config
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.embedding_client import EmbeddingClient
config = Config()
# 初始化图数据库连接
graph = Neo4jGraph(
url=config.neo4j_conf.get("uri"),
username=config.neo4j_conf.get("username"),
password=config.neo4j_conf.get("password")
)
# 初始化LLM和Embedding
llm = MultiAPIKeyChatOpenAI(config.openai_qa)
embedding = EmbeddingClient(config.embedding)
# 初始化通用业务知识向量库Retriever(示例中未配置)
business_vectorstore = None # 例如 Chroma(collection_name="business_knowledge")
business_retriever = None # business_vectorstore.as_retriever() if business_vectorstore else None
# 创建Agent实例
agent = EngineeringQAAgent(llm, embedding, graph, business_retriever)
# 交互示例
question = "工程里有几个项目划分,每个项目划分的名字分别是什么"
answer = agent.ask(question)
print("回答:", answer)
+22 -1
View File
@@ -1,8 +1,17 @@
# src/llm_client.py
import os
import logging
from datetime import datetime
import os
import getpass
from langchain_openai import ChatOpenAI
from langchain_core.rate_limiters import InMemoryRateLimiter
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class LLMClient:
def __init__(self, openai_config: dict):
@@ -19,11 +28,23 @@ class LLMClient:
api_type = openai_config.get("api_type", "openai")
os.environ["OPENAI_API_TYPE"] = api_type
temperature = openai_config.get("temperature", 0)
model_name = openai_config.get("model_name", "gpt-4o-mini")
# 初始化限速器:每秒最多0.1次请求(即每10秒1次)
#rate_limiter = InMemoryRateLimiter(
# requests_per_second=13,
# check_every_n_seconds=10,
# max_bucket_size=10,
#)
# 开启流式
self.llm = ChatOpenAI(model_name=model_name, temperature=0, streaming=True)
self.llm = ChatOpenAI(
model_name=model_name,
temperature=temperature,
streaming=True,
#rate_limiter=rate_limiter
)
def invoke(self, messages):
# 同步调用,返回完整响应
+64
View File
@@ -0,0 +1,64 @@
# src/multi_llm_client.py
import os
import logging
from datetime import datetime
import os
import getpass
import itertools
from langchain_openai import ChatOpenAI
from langchain_core.rate_limiters import InMemoryRateLimiter
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class MultiAPIKeyChatOpenAI:
def __init__(self, openai_config: dict):
api_keys = openai_config.get("api_keys", [])
if not api_keys:
api_key = openai_config.get("api_key")
if not api_key:
api_key = os.environ.get("OPENAI_API_KEY")
api_keys = [api_key]
api_base = openai_config.get("api_base")
api_type = openai_config.get("api_type", "openai")
model_name = openai_config.get("model_name", "gpt-4o-mini")
temperature = openai_config.get("temperature", 0)
# 设置环境变量(可选,根据需要)
if api_base:
os.environ["OPENAI_API_BASE"] = api_base
os.environ["OPENAI_API_TYPE"] = api_type
# 初始化限速器
#rate_limiter = InMemoryRateLimiter(
# requests_per_second=13,
# check_every_n_seconds=10,
# max_bucket_size=10,
#)
# 创建多个 ChatOpenAI 实例,每个使用不同的 API Key
self.llms = [
ChatOpenAI(
api_key=key,
model_name=model_name,
temperature=temperature,
streaming=True,
#rate_limiter=rate_limiter,
)
for key in api_keys
]
# 轮询器,用于循环调用不同的 llm 实例
self._llm_cycle = itertools.cycle(self.llms)
self.llm = next(self._llm_cycle)
def invoke(self, messages):
llm = next(self._llm_cycle)
return llm.invoke(messages)
def stream(self, messages):
llm = next(self._llm_cycle)
return llm.astream(messages)
+10 -2
View File
@@ -1,11 +1,16 @@
# src/neo4j_raw_retriever.py
import os
import logging
from datetime import datetime
from typing import List
from langchain.schema import Document
from neo4j import GraphDatabase
import logging
logger = logging.getLogger("BoweiAgent.Neo4jRawRetriever")
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class Neo4jRawRetriever:
def __init__(self, neo4j_conf: dict):
@@ -17,6 +22,9 @@ class Neo4jRawRetriever:
def close(self):
self.driver.close()
def driver(self):
return self.driver
def get_relevant_documents(self, cypher_query: str) -> list[Document]:
with self.driver.session() as session:
result = session.run(cypher_query)
+10
View File
@@ -1,6 +1,16 @@
# src/neo4j_retriever.py
import os
import logging
from datetime import datetime
from langchain_neo4j import Neo4jVector
from langchain_openai import OpenAIEmbeddings
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class Neo4jKnowledgeRetriever:
def __init__(self, neo4j_conf: dict, embedding_client):
neo4j_uri = neo4j_conf.get("uri")
+715
View File
@@ -0,0 +1,715 @@
"""
软件知识图谱类定义
根据Ontology_Layer.txt文件中的知识图谱信息创建
"""
from abc import ABC, abstractmethod
import json
from typing import Any, Type
class ProjectToolkit(ABC):
"""
项目类(抽象基类)
描述: 代表整个项目结构的顶层容器
"""
def __init__(self, config: Any):
pass
@abstractmethod
def get_division_by_name(self, name_part):
"""
通过名称获取项目划分对象
Args:
name_part (str): 项目划分节点名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
"""
pass
@abstractmethod
def get_division_item_by_path(self, path):
"""
通过路径获取项目划分对象
Args:
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Dict[str, Any]): 成功时返回的项目划分对象数据,失败时为空字典
"""
pass
@abstractmethod
def get_division_node_by_parent_and_name(self, parent_path, partial_name):
"""
通过父节点路径和模糊节点名称获取项目划分对象,包括子节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
partial_name (str): 目标节点的模糊或不完整名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
"""
pass
@abstractmethod
def get_quantities_by_paths(self, paths_str):
"""
获取指定项目路径下的工程量对象
Args:
paths_str (str): 以'/'分隔的多级节点路径
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Dict[str, Any]): 成功时返回的节点数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_quantities_node_by_parent_and_code(self, parent_path, quantity_type, code):
"""
通过父节点路径和编码获取工程量对象(定额、主材或设备),包括子节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
quantity_type (str): 工程量类型('定额''主材''设备'或None表示所有类型)
code (str): 工程量编码,以'/'分隔的多个编码
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[List[Dict[str, Any]], Dict]): 成功时返回的数据列表或字典,失败时为空列表 []
"""
pass
@abstractmethod
def get_quantities_node_by_parent_and_name(self, parent_path, quantity_type, partial_name):
"""
通过父节点路径、类型和模糊节点名称获取工程量对象(主材或者设备),包括子节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
quantity_type (str): 工程量类型('定额''主材''设备')
partial_name (str): 目标节点的模糊或不完整名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[List[Dict[str, Any]], Dict]): 成功时返回的数据列表,失败时为空列表 []
"""
pass
@abstractmethod
def get_material_equipment_by_path(self, paths_str):
"""
通过路径获取材机对象(MaterialOrEquipment 类型节点)
Args:
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_material_equipment_by_parent_and_code(self, parent_path, code):
"""
通过父节点路径和编码获取材机对象
Args:
parent_path (str): 父节点的部分路径,以'/'分隔的多级节点路径
code (str): 目标节点的编码属性值
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_fee_template_by_path(self, paths_str):
"""
通过路径获取取费表模板节点
Args:
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)/直接费
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_fee_template_by_parent_and_name(self, parent_path, partial_name):
"""
通过父节点路径和节点名称获取取费表模板节点
Args:
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)
node_name (str): 目标节点的名称,例如:直接费
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_auxiliary_expense_table(self, table_name, fee_name, fee_attribute: str):
"""
在辅助费用表中查找费用
Args:
table_name (str): 费用表名称
fee_name (str): 要查找的费用名称(可能在多级子节点中)
fee_attribute (str): 费用值属性名
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_other_expense_table(self, table_name, fee_name, fee_attribute):
"""
在其它费用表中查找费用
Args:
table_name (str): 费用表名称
fee_name (str): 要查找的费用名称(可能在多级子节点中)
fee_attribute (str): 费用值属性名
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_land_acquisition_fee_table_table(self, table_name, fee_name, fee_attribute):
"""
在其中:场地征用费用表中查找费用
Args:
table_name (str): 费用表名称
fee_name (str): 要查找的费用名称(可能在多级子节点中)
fee_attribute (str): 费用值属性名
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_installation_price_difference_table(self, table_name, fee_name, fee_attribute):
"""
在安装价差费用表中查找费用
Args:
table_name (str): 费用表名称
fee_name (str): 要查找的费用名称(可能在多级子节点中)
fee_attribute (str): 费用值属性名
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_fee_schedule_on_Engineering_Cost_table(self, table_name, fee_name, fee_attribute):
"""
在工程费用表中查找费用
Args:
table_name (str): 费用表名称
fee_name (str): 要查找的费用名称(可能在多级子节点中)
fee_attribute (str): 费用值属性名
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
"""
pass
@abstractmethod
def get_project_property(self, property_name):
"""
通过属性名称获取工程属性
Args:
property_name (str): 属性名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_table_by_project_division(self, project_division_path, fee_name):
"""
通过项目划分路径和取费名称查找费用
Args:
project_division_path (str): 项目划分节点的路径
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_table_by_list(self, parent_path, list_code, list_unit, fee_name):
"""
通过清单名称和取费名称查找费用
Args:
parent_path (str): 父级路径
list_code (str): 清单编号
list_unit (str): 清单单位
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_table_by_quoto_code(self, parent_path, quantity_type, code, fee_name):
"""
通过父级路径、工程量类型、定额编码和取费名称查找费用
Args:
parent_path (str): 父级路径
quantity_type (str): 工程量类型
code (str): 定额编码
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_table_by_quantities_name(self, parent_path, quantity_type, quantity_name, fee_name):
"""
通过父级路径、工程量类型、工程量名称和取费名称查找费用
Args:
parent_path (str): 父级路径
quantity_type (str): 工程量类型
quantity_name (str): 工程量名称
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_by_material_equipment_code(self, parent_path, material_equipment_code, fee_name):
"""
通过父级路径、材机编码和取费名称查找费用
Args:
parent_path (str): 父级路径
material_equipment_code (str): 材机编码
fee_name (str): 取费名称
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
@abstractmethod
def get_fee_table_by_list_name(self, parent_path, list_code, unit):
"""
通过父级路径、清单编号和单位查找清单
Args:
parent_path (str): 父级路径
list_code (str): 清单编号
unit (str): 单位
Returns:
dict: 返回字典,字段包括:
- code (int): 状态码,固定为 200(成功)或 201(失败)
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
- status (bool): 成功为 True,失败为 False
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
"""
pass
class ProjectDivisionSet:
"""
项目划分项
描述: 代表项目结构中的层级条目,具有自身的属性,并且可以包含ProjectDivisionTree(项目划分树)
"""
def __init__(self):
self.name = None # xsd:string
class ProjectDivisionTree:
"""
项目划分项
描述: 代表项目结构中的层级条目,具有自身的属性,并且可以包含ProjectDivisionItem(项目划分项)
"""
def __init__(self):
self.name = None # xsd:string
class ProjectDivisionItem:
"""
项目划分项
描述: 代表项目结构中的层级条目,具有自身的属性,并且可以包含子项目划分项或详细工作项
JSON对应: ProjectDivisionSet数组中的对象,或ProjectDivisionItem的children数组中type为"项目划分"的对象
"""
def __init__(self):
self.GUID = None # xsd:string
self.id = None # xsd:string
self.name = None # xsd:string
self.代码 = None # xsd:string (可选)
self.费率 = None # xsd:string
self.单位 = None # xsd:string (可选)
self.取费表id = None # xsd:string
self.颜色标记 = None # xsd:string
self.取费表 = None # xsd:string
self.合价含税 = None # xsd:string (可选)
self.type = None # xsd:string
self.专业类型 = None # xsd:string
self.资源库列表 = None # xsd:list (可选)
self.notCheck = None # xsd:string (可选)
class ProjectQuantity:
"""
工程量
描述: 代表项目划分项(ProjectDivisionItem)下的具体工作单元或物料项,是定额、主材、设备的父类型
"""
def __init__(self):
self.id = None # xsd:string
self.类型 = None # xsd:string ("0"为定额,"1"为主材,"5"为设备)
self.name = None # xsd:string
self.编码 = None # xsd:string
self.单位 = None # xsd:string
self.数量 = None # xsd:string
self.资源库名称 = None # xsd:string
self.投标数量 = None # xsd:string (可选)
self.投标单价 = None # xsd:string (可选)
self.特征段 = None # xsd:string (可选)
self.关联父级量 = None # xsd:string (可选)
self.颜色标记 = None # xsd:string (可选)
self.单价不含税 = None
self.cost_set = None # xsd:CostSet
class Ration(ProjectQuantity):
"""
定额
描述: 代表一种标准的工程量条目,通常包含详细的工、料、机消耗标准
"""
def __init__(self):
super().__init__()
self.计算式 = None # xsd:string
self.中标计算式 = None # xsd:string
self.人工费 = None # xsd:string
self.机械费 = None # xsd:string
self.甲供材料费不含税 = None # xsd:string
self.材料费 = None # xsd:string
self.定额系数 = None # xsd:string
self.人工系数 = None # xsd:string
self.材料系数 = None # xsd:string
self.机械系数 = None # xsd:string
self.定额范围 = None # xsd:string
self.定额章节名称 = None # xsd:string
self.费用类型 = None # xsd:string
self.甲供材料费含税 = None # xsd:string
self.投标合价 = None # xsd:string
self.其中甲供材料费 = None # xsd:string
self.合价不含税 = None # xsd:string
self.基价 = None # xsd:string
self.所属定额库 = None # xsd:string
def __str__(self):
"""返回定额的字符串表示"""
attrs = {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
return json.dumps(attrs, ensure_ascii=False, indent=2)
class Material(ProjectQuantity):
"""
主材
描述: 代表工程中使用的主要材料
"""
def __init__(self):
super().__init__()
self.规格型号 = None # xsd:string
self.损耗率 = None # xsd:string
self.供货方 = None # xsd:string
self.集中配送 = None # xsd:string (可选)
self.单重 = None # xsd:string (可选)
self.市场价不含税 = None # xsd:string
self.市场价含税 = None # xsd:string
self.单价含税 = None # xsd:string
self.结算市场价不含税 = None # xsd:string (可选)
self.结算市场价含税 = None # xsd:string (可选)
self.基准价不含税 = None # xsd:string (可选)
self.基准价含税 = None # xsd:string (可选)
self.费用类型 = None # xsd:string
self.增值税率 = None # xsd:string (可选)
self.合价含税 = None # xsd:string
self.合价不含税 = None # xsd:string
self.线重 = None # xsd:string (可选)
self.制造长度 = None # xsd:string (可选)
self.截面积 = None # xsd:string (可选)
def __str__(self):
"""返回材料的字符串表示"""
attrs = {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
return json.dumps(attrs, ensure_ascii=False, indent=2)
class Equipment(ProjectQuantity):
"""
设备
描述: 代表工程中安装或使用的设备
"""
def __init__(self):
super().__init__()
self.规格型号 = None # xsd:string
self.供货方 = None # xsd:string
self.运杂费率 = None # xsd:string (可选)
self.单价含税 = None # xsd:string
self.设备类型 = None # xsd:string (可选)
self.增值税率 = None # xsd:string (可选)
self.合价含税 = None # xsd:string
self.合价不含税 = None # xsd:string
class MaterialOrEquipment:
"""
材机
描述: 代表DetailedWorkItem中所列出的具体材料、人工或机械设备及其详细信息
"""
def __init__(self):
self.id = None # xsd:string
self.编码 = None # xsd:string
self.名称 = None # xsd:string
self.单位 = None # xsd:string
self.类型 = None # xsd:string
self.供货方 = None # xsd:string
self.预算价不含税 = None # xsd:string
self.市场价不含税 = None # xsd:string
self.预算价含税 = None # xsd:string
self.市场价含税 = None # xsd:string
self.结算预算价不含税 = None # xsd:string
self.结算市场价不含税 = None # xsd:string
self.结算预算价含税 = None # xsd:string
self.结算市场价含税 = None # xsd:string
self.暂估价 = None # xsd:string
self.拆分 = None # xsd:string
self.全口径市场价不含税 = None # xsd:string
self.全口径市场价含税 = None # xsd:string
self.商品砼 = None # xsd:string
self.数量 = None # xsd:string
self.是否未计价 = None # xsd:string
class FeeTableTemplateItem:
"""
取费表模板项
描述:
"""
def __init__(self):
self.name = None # xsd:string
self.OutlayID = None # xsd:string
self.type = None # xsd:string
self.profession = None # xsd:string
class FeeCollection:
"""
取费
描述:
"""
def __init__(self):
self.name = None # xsd:string
self.serialNumber = None # xsd:string
self.code = None # xsd:string
self.base = None # xsd:string
self.rate = None # xsd:string
class FeeScheduleItem:
"""
费用表项
描述:
"""
def __init__(self):
self.name = None # xsd:string
class Fee:
"""
取费
描述:
"""
def __init__(self):
self.name = None # xsd:string
self.serialNumber = None # xsd:string
self.code = None # xsd:string
self.rate = None # xsd:string
self.amount = None # xsd:string
self.输出 = None # xsd:string (可选)
self.取费基数 = None # xsd:string (可选)
self.编制依据 = None # xsd:string (可选)
self.编码 = None # xsd:string (可选)
self.备注 = None # xsd:string (可选)
self.表一显示 = None # xsd:string (可选)
self.报表输出 = None # xsd:string (可选)
self.建安合计费 = None # xsd:string (可选)
self.合计费 = None # xsd:string (可选)
self.占总计 = None # xsd:string (可选)
self.建筑费 = None # xsd:string (可选)
self.安装费 = None # xsd:string (可选)
self.设备费 = None # xsd:string (可选)
self.其他费 = None # xsd:string (可选)
self.施工费 = None # xsd:string (可选)
self.单位投资 = None # xsd:string (可选)
class ProjectBuilder:
"""项目工具工厂类"""
# 存储注册的工具类
_registry: Type[ProjectToolkit] | None = None
_config: Any = None
def __init__(self):
pass
@classmethod
def register(cls, toolkit_class: Type[ProjectToolkit], config: Any):
"""
注册工具类到工厂
参数:
toolkit_class: 继承自ProjectToolkit的具体工具类
"""
if not issubclass(toolkit_class, ProjectToolkit):
raise TypeError(f"{toolkit_class.__name__} 必须继承自 ProjectToolkit")
cls._config = config
cls._registry = toolkit_class
@classmethod
def build(cls) -> ProjectToolkit:
"""
创建工具实例
返回:
实例化的工具对象
"""
if cls._registry is None:
raise KeyError("未注册的类,请先注册类")
return cls._registry(cls._config)
File diff suppressed because it is too large Load Diff
+24 -8
View File
@@ -1,5 +1,8 @@
# src/prompt_manager.py
import os
import logging
from datetime import datetime
from dataclasses import dataclass
from langchain.prompts import ChatPromptTemplate
from langchain.schema import SystemMessage, HumanMessage
@@ -60,14 +63,26 @@ class PromptManager:
# 输出格式(必须严格遵循)
def project_get_calculate_function():
project = ProjectBuilder.build()
status, data, error, helper_info = project.[SELECTED_METHOD]([PARAMETERS])
return status, data, error, helper_info
result_dict = project.[SELECTED_METHOD]([PARAMETERS])
status = result_dict.get('status', False)
message = result_dict.get('message', '')
quantity = 0.0
if isinstance(result_dict, dict) and '数量' in result_dict:
quantity = result_dict['数量']
if status:
return result_dict
else:
return result_dict
# 执行规则
- 参数必须从用户问题或上下文信息中提取
- 输出代码中必须以def project_get_calculate_function() -> Tuple[bool, Any, Optional[str], Dict[str, Any]]函数作为入口函数
- 必须确保生成的代码可以直接执行,代码要注意进行各类错误检查,出错采用抛出异常方式,说明详细信息
- 禁止添加任何注释或解释
- 参数必须从用户问题或上下文信息中提取
- 为函数内部代码生成流程注释,并使用logger进行日志输出,在函数外禁止生成任何注释或解释或非代码内容。
- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数,该函数返回字典包含:'code''status''message''data'四个字段。
- 必须确保生成的代码可以直接执行,代码要注意进行各类容错检查。
- 'data'字段通常要求是浮点或整型值,除非用户要求返回其他类型,同时函数执行过程中发生错误,'data'字段也必须为0,并在message说明错误原因。
- ProjectToolkit 类中涉及项目划分的函数已考虑在其及其子孙项目划分下查找,所以无需生成递归子项目划分的代码。
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7
"""
)
@@ -89,6 +104,7 @@ def project_get_calculate_function():
4. 修复后的代码应该完整,可以直接执行,并且能够返回查询结果
注意:
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7
- 必须只输出最终的Python代码,不要添加任何解释、注释、推理过程或自然语言描述。
- 不要以“以下是修正后的代码”、“修改如下”等语句开头。
- 不要输出任何其他无关的内容。
@@ -131,8 +147,8 @@ def project_get_calculate_function():
{user_input}
改写规则:
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。知识图谱节点标签包括ProjectDivisionItem、ProjectQuantity、Fee、FeeCollection等。
识别目标对象:仅从业务结构中识别用户问题中需要获取的核心对象类型(如 ProjectDivisionItem、ProjectQuantity、Fee、FeeCollection 等)。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。
识别目标对象:仅从业务结构中识别如下类型:ProjectDivisionItem、ProjectAttributeSet、FeeSchedule、FeeItem的核心对象。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
提取查询条件:从用户输入中解析关键条件(如名称、量、类型、值、层级等),条件应基于对象属性(如 name、quantity、type),注意区分查找子串和相等的区别。如果条件涉及层级路径(如路径中包含特定部分),需提取路径部分作为条件(例如,用户提到“架空输电线路”时,解析为路径匹配)。
如果用户指定层级(如“叶节点”),需在条件中体现(例如,添加 WHERE item.isLeaf = true)。
忽略任何计算、转换或后处理要求(如“乘以1000”),只关注获取原始数据对象或属性。
+75
View File
@@ -0,0 +1,75 @@
# src/user_interaction.py
import os
import logging
from datetime import datetime
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from typing import List, Dict, Any
import asyncio
current_file = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(current_file)
class BusinessObject(BaseModel):
name: str = Field(description="对象的名称")
constraints: Dict[str, Any] = Field(description="对象约束条件")
class UserInteraction:
def __init__(self, llm: ChatOpenAI, business_structure: str):
self.llm = llm
self.business_structure = business_structure
self.prompt_template = ChatPromptTemplate.from_template("""
请基于提供的"示例业务结构",分析用户输入问题。严格识别用户输入中提到的所有业务对象(对象必须来源于业务结构,不能是属性)。输出一个JSON数组,每个元素是一个对象,包含键"name""constraints"
- "name": 对象的名称,优先使用用户输入中指定的属性值(如项目划分名称、费用项名称等);如果未指定具体名称,则使用对象类型(如"ProjectDivisionItem")。
- "constraints": 一个对象,包含:
- "type": 业务结构中的对象类型(如ProjectDivisionItem、ProjectQuantity)。
- 其他属性:用户输入中明确指定的属性条件(如"path""name""类型""编码"等),使用业务结构中的属性名(英文或中文,需一致)。
注意:
- 对象只能来源于业务结构(如EngineeringData的子对象),勿将属性(如"数量""编码")识别为对象。
- 勿执行查询、过滤或汇总操作;只识别对象及其约束。
- 对于集合性描述(如"所有【定额】"),输出一个对象代表该集合,在"constraints"中指定条件。
- 输出仅基于用户输入,不推断额外信息。
示例业务结构(供参考):
{business_structure}
示例输入:从项目划分【架空输电线路本体工程/辅助工程】的费用集中查找名称为【'合计'】费用
示例输出:
[
{{
"name": "辅助工程",
"constraints": {{
"type": "ProjectDivisionItem",
"path": "架空输电线路本体工程/辅助工程",
"name": "辅助工程"
}}
}},
{{
"name": "合计",
"constraints": {{
"parent": "辅助工程",
"type": "CostItem",
"name": "合计"
}}
}}
]
用户输入:{query}
""")
self.parser = JsonOutputParser(pydantic_object=List[BusinessObject])
def understand(self, user_input: str) -> List[BusinessObject]:
prompt_text = self.prompt_template.format_prompt(query=user_input, business_structure = self.business_structure)
messages = prompt_text.to_messages()
response = self.llm.invoke(messages)
return self.parser.parse(response.text())
async def aunderstand(self, user_input: str) -> List[BusinessObject]:
prompt_text = self.prompt_template.format_prompt(query=user_input)
messages = prompt_text.to_messages()
response = await self.llm.ainvoke(messages)
return self.parser.parse(response.text())
+46
View File
@@ -0,0 +1,46 @@
{"name": "杆塔总基数", "query": "从【架空输电线路本体工程/基础工程】及其子孙项目划分中查找编码中包含【YX2-1~7】的所有【定额】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n \n def expand_code_range(code_range):\n if '~' not in code_range:\n return code_range\n prefix, range_part = code_range.split('-')\n start, end = map(int, range_part.split('~'))\n return '/'.join([f\"{prefix}-{i}\" for i in range(start, end + 1)])\n \n expanded_codes = expand_code_range('YX2-1~7')\n \n result_dict = project.get_quantities_node_by_parent_and_code(\n parent_path='架空输电线路本体工程/基础工程',\n quantity_type='定额',\n code=expanded_codes\n )\n \n status = result_dict.get('status', False)\n if not status:\n return result_dict\n \n data = result_dict.get('data', [])\n if not isinstance(data, list):\n data = [data]\n \n total_quantity = 0.0\n for item in data:\n if isinstance(item, dict) and '数量' in item:\n try:\n quantity = float(item['数量'])\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': {total_quantity}\n }"}
{"name": "角钢塔_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path='架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立',\n quantity_type='主材',\n partial_name='角钢'\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n if not status:\n return {'code': 201, 'message': message, 'status': False, 'data': 0}\n total_quantity = 0.0\n for item in data:\n if isinstance(item, Material) and '数量' in item.__dict__:\n try:\n total_quantity += float(item.数量)\n except (ValueError, TypeError):\n continue\n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_quantity}"}
{"name": "角钢塔_其中:高强钢塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result_dict = project.get_quantities_node_by_parent_and_name(path, \"主材\", \"角钢、高强\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n if not status:\n return {'code': 201, 'message': message, 'status': False, 'data': 0}\n total = 0.0\n for item in data:\n if isinstance(item, dict) and item.get('类型') == '主材' and '数量' in item:\n try:\n total += float(item['数量'])\n except (ValueError, TypeError):\n continue\n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total}"}
{"name": "角钢塔_塔材装材费_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n parent_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n quantity_type = \"主材\"\n partial_name = \"角钢\"\n \n result_dict = project.get_quantities_node_by_parent_and_name(parent_path, quantity_type, partial_name)\n status = result_dict.get('status', False)\n data = result_dict.get('data', [])\n \n if not status:\n return {'code': 201, 'message': '查询失败: ' + result_dict.get('message', ''), 'status': False, 'data': []}\n \n total_price = 0.0\n for item in data:\n if isinstance(item, Material) and hasattr(item, '单价含税'):\n try:\n price = float(item.单价含税)\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_price}"}
{"name": "角钢塔_其中:高强钢塔材费用_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n path = '架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立'\n result_dict = project.get_quantities_node_by_parent_and_name(path, '主材', '角钢、高强')\n status = result_dict.get('status', False)\n data = result_dict.get('data', [])\n if not status:\n return {'code': 201, 'message': '查询失败: ' + result_dict.get('message', ''), 'status': False, 'data': []}\n \n total_price = 0.0\n for item in data:\n if isinstance(item, Material) and '单价含税' in item.__dict__:\n try:\n price = float(item.单价含税)\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_price}"}
{"name": "钢管塔_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path=\"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\",\n quantity_type=\"主材\",\n partial_name=\"钢管塔\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n \n if not status:\n return {'code': 201, 'message': message, 'status': False, 'data': 0}\n \n total_quantity = 0\n for item in data:\n if isinstance(item, dict) and '数量' in item:\n try:\n total_quantity += float(item['数量'])\n except (ValueError, TypeError):\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_quantity}"}
{"name": "钢管塔_钢管价格_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path='架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立',\n quantity_type='主材',\n partial_name='钢管塔'\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if not status:\n return {'status': False, 'message': message, 'code': code, 'data': []}\n \n total_price = 0.0\n for item in data:\n if isinstance(item, Material) and '单价含税' in item.__dict__:\n try:\n price = float(item.单价含税)\n total_price += price\n except (ValueError, TypeError):\n continue\n \n return {'status': True, 'message': 'Ok', 'code': 200, 'data': total_price}"}
{"name": "钢管杆_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path='架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立',\n quantity_type='主材',\n partial_name='钢管杆'\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n if not status:\n return {'code': 201, 'message': message, 'status': False, 'data': []}\n \n total_quantity = 0.0\n for item in data:\n if isinstance(item, dict) and item.get('类型') == '主材' and '钢管杆' in item.get('name', ''):\n try:\n quantity = float(item.get('数量', 0))\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_quantity}"}
{"name": "钢管杆_钢管价格_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path='架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立',\n quantity_type='主材',\n partial_name='钢管杆'\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n if not status:\n return {'code': 201, 'message': message, 'status': False, 'data': []}\n \n total_price = 0.0\n for item in data:\n if isinstance(item, Material) and '钢管杆' in item.name:\n try:\n price = float(item.单价含税) if item.单价含税 else 0.0\n total_price += price\n except (ValueError, AttributeError):\n continue\n \n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total_price}"}
{"name": "导线及线材_分裂数", "query": "从【架空输电线路本体工程/架线工程】及其子孙项目划分中查找编码中包含【['YX5-67', 'YX5-68', 'YX5-69', 'YX5-78', 'YX5-79', 'YX5-59', 'YX5-60', 'YX5-61', 'YX5-62', 'YX5-63', 'YX5-64', 'YX5-65', 'YX5-66', 'YX5-74', 'YX5-75', 'YX5-76', 'YX5-77', 'YX5-52', 'YX5-53', 'YX5-54', 'YX5-55', 'YX5-56', 'YX5-57', 'YX5-58', 'YX5-70', 'YX5-71', 'YX5-72', 'YX5-73', 'YX5-14', 'YX5-15', 'YX5-16', 'YX5-17', 'YX5-43', 'YX5-44', 'YX5-45', 'YX5-46', 'YX5-47', 'YX5-48', 'YX5-49', 'YX5-50', 'YX5-51', 'YX5-10', 'YX5-11', 'YX5-12', 'YX5-13', 'YX5-38', 'YX5-39', 'YX5-40', 'YX5-41', 'YX5-42', 'YX5-8', 'YX5-9']】的所有【定额】的【参数】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n parent_path = \"架空输电线路本体工程/架线工程\"\n quantity_type = \"定额\"\n codes = ['YX5-67', 'YX5-68', 'YX5-69', 'YX5-78', 'YX5-79', 'YX5-59', 'YX5-60', 'YX5-61', 'YX5-62', 'YX5-63', 'YX5-64', 'YX5-65', 'YX5-66', 'YX5-74', 'YX5-75', 'YX5-76', 'YX5-77', 'YX5-52', 'YX5-53', 'YX5-54', 'YX5-55', 'YX5-56', 'YX5-57', 'YX5-58', 'YX5-70', 'YX5-71', 'YX5-72', 'YX5-73', 'YX5-14', 'YX5-15', 'YX5-16', 'YX5-17', 'YX5-43', 'YX5-44', 'YX5-45', 'YX5-46', 'YX5-47', 'YX5-48', 'YX5-49', 'YX5-50', 'YX5-51', 'YX5-10', 'YX5-11', 'YX5-12', 'YX5-13', 'YX5-38', 'YX5-39', 'YX5-40', 'YX5-41', 'YX5-42', 'YX5-8', 'YX5-9']\n code_str = '/'.join(codes)\n result_dict = project.get_quantities_node_by_parent_and_code(parent_path, quantity_type, code_str)\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n data = result_dict.get('data', [])\n if status:\n total = 0.0\n for item in data:\n if isinstance(item, dict) and '参数' in item:\n try:\n total += float(item['参数'])\n except (ValueError, TypeError):\n pass\n return {'code': 200, 'message': 'Ok', 'status': True, 'data': total}\n else:\n return {'code': 201, 'message': message, 'status': False, 'data': 0.0}"}
{"name": "导线及线材_其中:节能导线量", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n total_quantity = 0.0\n \n paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in paths:\n result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n quantity_type=\"主材\",\n partial_name=\"高导电率\"\n )\n \n if not result.get('status', False):\n continue\n \n materials = result.get('data', [])\n if not isinstance(materials, list):\n materials = [materials]\n \n for material in materials:\n if not isinstance(material, dict):\n continue\n if material.get('类型') != '主材':\n continue\n if '高导电率' not in material.get('name', ''):\n continue\n \n try:\n quantity = float(material.get('数量', 0))\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_quantity\n }"}
{"name": "导线及线材_导线装材费_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【线】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n total_price = 0.0\n \n paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in paths:\n quantities_result = project.get_quantities_node_by_parent_and_name(path, \"主材\", \"线\")\n if not quantities_result.get('status', False):\n continue\n \n materials = quantities_result.get('data', [])\n for material in materials:\n if not isinstance(material, dict):\n continue\n \n price_str = material.get('单价含税', '0')\n try:\n price = float(price_str)\n total_price += price\n except ValueError:\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "导线及线材_其中:节能导线费用_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n total_price = 0.0\n \n paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\"\n ]\n \n for path in paths:\n quantities_result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n quantity_type=\"主材\",\n partial_name=\"高导电率\"\n )\n \n if not quantities_result.get('status', False):\n continue\n \n materials = quantities_result.get('data', [])\n if not isinstance(materials, list):\n materials = [materials]\n \n for material in materials:\n if not isinstance(material, dict):\n continue\n \n price_str = material.get('单价含税', '0')\n try:\n price = float(price_str)\n total_price += price\n except ValueError:\n continue\n \n return {\n 'code': 200 if total_price > 0 else 201,\n 'message': 'Ok' if total_price > 0 else 'No matching materials found',\n 'status': total_price > 0,\n 'data': total_price\n }"}
{"name": "导线及线材_导线类型", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设@@架空输电线路本体工程/架线工程/其他架线工程】及其子孙项目划分中查找名称中包含【['钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', '扩径导线', '耐热导线', '碳纤维导线']】的所有【主材】的【参数】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n parent_paths = [\n \"架空输电线路本体工程/架线工程/导地线架设\",\n \"架空输电线路本体工程/架线工程/导地线跨越架设\",\n \"架空输电线路本体工程/架线工程/其他架线工程\"\n ]\n material_names = [\n '钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', \n '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', \n '扩径导线', '耐热导线', '碳纤维导线'\n ]\n total_params = 0.0\n \n for parent_path in parent_paths:\n for material_name in material_names:\n result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path=parent_path,\n quantity_type='主材',\n partial_name=material_name\n )\n if not result_dict.get('status', False):\n continue\n \n materials = result_dict.get('data', [])\n if not isinstance(materials, list):\n materials = [materials]\n \n for material in materials:\n if not isinstance(material, dict):\n continue\n if '参数' in material:\n try:\n param_value = float(material['参数'])\n total_params += param_value\n except (ValueError, TypeError):\n pass\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_params\n }"}
{"name": "基础钢材量", "query": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【数量】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n base_path = \"架空输电线路本体工程/基础工程/基础砌筑\"\n \n result = project.get_quantities_node_by_parent_and_name(\n parent_path=base_path,\n quantity_type=\"主材\",\n partial_name=\"圆钢\"\n )\n \n if not result.get('status', False):\n return {\n 'code': 201,\n 'message': f\"查询失败: {result.get('message', '未知错误')}\",\n 'status': False,\n 'data': []\n }\n \n materials = result.get('data', [])\n total_quantity = 0.0\n \n for material in materials:\n if isinstance(material, dict) and material.get('类型') == '主材' and '圆钢' in material.get('name', ''):\n try:\n quantity = float(material.get('数量', 0))\n total_quantity += quantity\n except (ValueError, TypeError):\n continue\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': {\n 'total_quantity': total_quantity,\n 'unit': '根据具体主材单位确定'\n }\n }"}
{"name": "基础钢材价格", "query": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【单价】之和", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n base_path = \"架空输电线路本体工程/基础工程/基础砌筑\"\n total_price = 0.0\n \n def process_division(path):\n nonlocal total_price\n quantities_result = project.get_quantities_node_by_parent_and_name(path, \"主材\", \"圆钢\")\n if quantities_result.get('status'):\n materials = quantities_result.get('data', [])\n for material in materials:\n if isinstance(material, dict):\n material_obj = Material()\n material_obj.__dict__.update(material)\n if \"单价含税\" in material and material[\"单价含税\"]:\n try:\n total_price += float(material[\"单价含税\"])\n except (ValueError, TypeError):\n pass\n \n children_result = project.get_division_node_by_parent_and_name(path, \"\")\n if children_result.get('status'):\n children = children_result.get('data', [])\n for child in children:\n if isinstance(child, dict) and \"path\" in child:\n process_division(child[\"path\"])\n \n process_division(base_path)\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_price\n }"}
{"name": "本体费用合计_元", "query": "从【工程费用】中获取【架空输电线路本体工程.合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"架空输电线路本体工程.合计费\", \"amount\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程人工费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【人工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程', '人工费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程人工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【人工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程', '人工费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程机械费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【施工机械使用费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程', '施工机械使用费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "本体工程机械费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【施工机械使用费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程', '施工机械使用费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "基础工程费用_元", "query": "从【架空输电线路本体工程/基础工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/基础工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "杆塔工程费用_元", "query": "从【架空输电线路本体工程/杆塔工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/杆塔工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "接地工程费用_元", "query": "从【架空输电线路本体工程/接地工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/接地工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "架线工程费用_元", "query": "从【架空输电线路本体工程/架线工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/架线工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "附件工程费用_元", "query": "从【架空输电线路本体工程/附件安装工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/附件安装工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助工程费用_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程/辅助工程', '合计')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助工程费用_调试_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取名称属于【合计】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name(\"架空输电线路本体工程/辅助工程\", \"合计\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "辅助设施工程_元", "query": "从【工程费用】中获取【辅助设施工程.合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用\", \"辅助设施工程.合计费\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其他费用合计_元", "query": "从【工程费用】中获取【其他费用.合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"其他费用.合计费\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "建场费合计_元", "query": "从【其他费用】中获取【建设场地征用及清理费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '建设场地征用及清理费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "项目建设管理费合计_元", "query": "从【其他费用】中获取【项目建设管理费】的属性", "code": "根据用户问题和上下文信息,我们需要从\"其他费用\"中获取\"项目建设管理费\"的属性。根据上下文信息,\"其他费用\"是一个FeeScheduleItem类型,而\"项目建设管理费\"是一个Fee类型,且其父节点是\"其他费用\"。\n\n最匹配的方法是使用`get_fee_schedule_on_other_expense_table`方法,因为:\n1. \"其他费用\"对应的是其他费用表\n2. 我们需要获取的是费用属性\n\n以下是生成的Python代码:\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\n table_name=\"其他费用表\",\n fee_name=\"项目建设管理费\",\n fee_attribute=\"*\" # 获取所有属性\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('data', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "其中:工程监理费_元", "query": "从【其他费用】中获取【工程监理费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '工程监理费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "项目建设技术服务费合计_元", "query": "从【其他费用】中获取【项目建设技术服务费】的属性", "code": "根据用户问题和上下文信息,我们需要从\"其他费用\"中获取\"项目建设技术服务费\"的属性。根据上下文信息,\"项目建设技术服务费\"是\"其他费用\"的子节点,类型为\"Fee\"。\n\n从工程数据访问库中,最匹配的方法是`get_fee_schedule_on_other_expense_table`,因为它专门用于在其他费用表中查找费用。\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\n table_name=\"其他费用表\",\n fee_name=\"项目建设技术服务费\",\n fee_attribute=\"amount\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "其中:项目前期工作费_元", "query": "从【其他费用】中获取【项目前期工作费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '项目前期工作费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:勘察费_元", "query": "从【其他费用】中获取【勘察费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\"其他费用表\", \"勘察费\", \"amount\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:设计费_元", "query": "从【其他费用】中获取【设计费】的属性", "code": "根据用户问题和上下文信息,我们需要从\"其他费用\"表中获取\"设计费\"的属性。根据上下文信息,\"其他费用表\"是父节点,\"设计费\"是其子节点。工程数据访问库中提供了`get_fee_schedule_on_other_expense_table`方法专门用于在其他费用表中查找费用。\n\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(table_name=\"其他费用表\", fee_name=\"设计费\", fee_attribute=None)\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', 201)\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:工程建设检测费_元", "query": "从【其他费用】中获取【工程建设检测费】的属性", "code": "根据用户问题和上下文信息,我们需要从\"其他费用\"中获取\"工程建设检测费\"的属性。根据上下文信息,\"工程建设检测费\"是一个Fee类型节点,其父节点是\"其他费用\"FeeScheduleItem类型)。因此,我们可以使用`get_fee_schedule_on_other_expense_table`方法来获取该费用的属性。\n\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table(\n table_name=\"其他费用\",\n fee_name=\"工程建设检测费\",\n fee_attribute=\"*\"\n )\n status = result_dict.get('status', False)\n if not status:\n return result_dict\n return result_dict"}
{"name": "生产准备费_元", "query": "从【其他费用】中获取【生产准备费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_other_expense_table('其他费用表', '生产准备费', 'amount')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:安全文明施工费_线路_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【安全文明施工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name('架空输电线路本体工程', '安全文明施工费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "其中:安全文明施工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【安全文明施工费】的费用", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_division_node_by_parent_and_name(\"架空输电线路本体工程\", \"安全文明施工费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "基本预备费_元", "query": "从【工程费用】中获取【基本预备费.合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table('工程费用表', '基本预备费.合计费', '合计费')\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "静态投资_元", "query": "从【工程费用】中获取【工程静态投资(一~七项合计).合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"工程静态投资(一~七项合计).合计费\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "建设期利息_元", "query": "从【工程费用】中获取【建设期贷款利息.合计费】的属性", "code": "根据用户问题和上下文信息,我们需要从工程费用表中获取\"建设期贷款利息.合计费\"的属性。根据上下文信息,这是一个三级结构:工程费用表 -> 建设期贷款利息 -> 合计费。\n\n最匹配的方法是`get_fee_schedule_on_Engineering_Cost_table`,因为它专门用于在工程费用表中查找费用。\n\n```python\ndef project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\n table_name=\"工程费用表\",\n fee_name=\"建设期贷款利息.合计费\",\n fee_attribute=\"合计费\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('data', '')\n data = result_dict.get('data', [])\n if status:\n return result_dict\n else:\n return result_dict\n```"}
{"name": "动态投资_元", "query": "从【工程费用】中获取【工程动态投资(一~八项合计).合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\n table_name=\"工程费用表\",\n fee_name=\"工程动态投资(一~八项合计).合计费\",\n fee_attribute=\"合计费\"\n )\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
{"name": "增值税抵扣税额_元", "query": "从【工程费用】中获取【其中:可抵扣增值税额.合计费】的属性", "code": "def project_get_calculate_function():\n project = ProjectBuilder.build()\n result_dict = project.get_fee_schedule_on_Engineering_Cost_table(\"工程费用表\", \"其中:可抵扣增值税额.合计费\", \"合计费\")\n status = result_dict.get('status', False)\n message = result_dict.get('message', '')\n code = result_dict.get('code', '')\n data = result_dict.get('data', {})\n if status:\n return result_dict\n else:\n return result_dict"}
View File
+122
View File
@@ -0,0 +1,122 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import logging
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
import json
from src.dialog_manager import DialogManager
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
import yaml
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
success_count = 0
fail_count = 0
questions = []
error_list = []
def main():
global fail_count, success_count, questions, error_list
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
# 加载 zhibiao.json
with open('./tests/zhibiao.json', 'r', encoding='utf-8') as f:
zhibiao_data = json.load(f)
# 提取指标映射关系并批量执行
for item in zhibiao_data:
datasource = item['数据来源']
if datasource == '报表指标' or datasource == '指标库':
continue
query = item['指标描述']['指标映射']
#rewritten_results = dialog_manager.understand_user_question(query)
rewritten_results = []
if rewritten_results is None or rewritten_results == []:
print('问题: {} 没有找到符合要求的数据'.format(query))
fail_count += 1
error_list.append(f"问题 {query} 调用 understand_user_question 返回空结果")
continue
selected_rewritten, selected_knowledge = rewritten_results[0]
result = dialog_manager.execute_generated_code(selected_rewritten, selected_knowledge)
if result is None or result == []:
questions.append(selected_rewritten)
fail_count += 1
error_list.append(f"问题 {query} {selected_rewritten} 调用 execute_generated_code 返回空结果")
else:
success_count += 1
#print(result)
if __name__ == "__main__":
main()
total = success_count + fail_count
if total > 0:
success_rate = success_count / total
fail_rate = fail_count / total
else:
success_rate = fail_rate = 0
print(f"问题总数: {total}")
print(f"成功比例: {success_rate * 100:.2f}%")
print(f"失败比例: {fail_rate * 100:.2f}%")
print("错误列表:")
for error in error_list:
print(error)
+171
View File
@@ -0,0 +1,171 @@
import os
import sys
import logging
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from datetime import datetime
# 获取当前时间,格式化为字符串
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
log_filename = f"test_code1{now_str}.log"
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"),
logging.StreamHandler()
],
)
logger = logging.getLogger("test_code1")
import logging
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
import json
import os
from src.dialog_manager import DialogManager
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
import yaml
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
success_count = 0
fail_count = 0
questions = []
error_list = []
success_results = []
fail_results = []
def main():
global fail_count, success_count, questions, error_list
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
# 加载 zhibiao.jsonl
zhibiao_data = []
with open('./tests/zhibiao.jsonl', 'r', encoding='utf-8') as f:
for line in f:
zhibiao_data.append(json.loads(line))
# 提取指标映射关系并批量执行
for item in zhibiao_data:
query = item['query']
selected_knowledge = item['result']
result = dialog_manager.generated_code(query, selected_knowledge)
if isinstance(result, dict) and result.get('status', False):
code = result['data']
result = dialog_manager.execute_code(code)
if isinstance(result, dict) and result.get('status', False):
success_count += 1
success_results.append({
"query": query,
"result": result.get('data', []),
"status": True
})
else:
questions.append(query)
fail_count += 1
error_msg = result.get('message', '调用 execute_code 返回空结果') if isinstance(result, dict) else f"问题 {query} {selected_knowledge} 调用 execute_generated_code 返回空结果"
error_list.append(error_msg)
fail_results.append({
"query": query,
"result": error_msg,
"status": False
})
else:
questions.append(query)
fail_count += 1
error_msg = result.get('message', '调用 generated_code 返回空结果') if isinstance(result, dict) else f"问题 {query} {selected_knowledge} 调用 execute_generated_code 返回空结果"
error_list.append(error_msg)
fail_results.append({
"query": query,
"result": error_msg,
"status": False
})
#print(result)
if __name__ == "__main__":
main()
total = success_count + fail_count
if total > 0:
success_rate = success_count / total
fail_rate = fail_count / total
else:
success_rate = fail_rate = 0
print(f"问题总数: {total}")
print(f"成功比例: {success_rate * 100:.2f}%")
print(f"失败比例: {fail_rate * 100:.2f}%")
print("错误列表:")
for error in error_list:
print(error)
# 保存成功结果到 jsonl 文件
success_filename = f'./tests/success_{now_str}.jsonl'
with open(success_filename, 'w', encoding='utf-8') as f:
for item in success_results:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
# 保存失败结果到 jsonl 文件
fail_filename = f'./tests/fail_{now_str}.jsonl'
with open(fail_filename, 'w', encoding='utf-8') as f:
for item in fail_results:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
+111
View File
@@ -0,0 +1,111 @@
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import logging
logging.basicConfig(
level=logging.DEBUG, # 生产环境可改为 INFO 或 WARNING
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("logs/test.log", encoding="utf-8"), logging.StreamHandler()],
)
logger = logging.getLogger("test")
import logging
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
import asyncio
import json
from src.dialog_manager import DialogManager
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
import yaml
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
def main():
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder)
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
query = "查找一下项目划分节点【架空输电线路本体工程/基础工程/基础砌筑】下的费用预览中的【主材费_ZCF】"
knowledge = """
<Node element_id='4:f0ca7d86-42db-43e9-87d5-8b942b8972a9:1496' labels=frozenset({'CostItem'}) properties={
<elementId>: 4:3d187051-ffce-4fca-8060-a4d9949fdd38:13295
<id>: 13295
cost: 996944.127814
id: 主材费_ZCF
name: 主材费_ZCF
unique_id: {8C4CC636-3741-4F07-865A-28D38AB7F31D}_主材费_ZCF
<Node element_id='4:f0ca7d86-42db-43e9-87d5-8b942b8972a9:1496' labels=frozenset({'ProjectDivisionItem'}) properties={
<elementId>: 4:3d187051-ffce-4fca-8060-a4d9949fdd38:14682
<id>: 14682
GUID: {8C4CC636-3741-4F07-865A-28D38AB7F31D}
name: 基础砌筑
path: 安装/架空输电线路本体工程/基础工程/基础砌筑
type: 项目划分
专业类型: 线路
取费表: 线路取费表
取费表id: 3_1
序号: 1.3
费率: 0
"""
result = dialog_manager.execute_generated_code(query, knowledge)
print(result)
if __name__ == "__main__":
main()
+109
View File
@@ -0,0 +1,109 @@
import sys
import os
import io
import logging
import traceback
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
from neo4j import GraphDatabase
from src.config import Config
logging.basicConfig(
level=logging.DEBUG, # 生产环境可改为 INFO 或 WARNING
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler("logs/test_runcode.log", encoding="utf-8"), logging.StreamHandler()],
)
logger = logging.getLogger("test_runcode")
def main():
config = Config()
neo4j_conf = config.neo4j_conf
code_str = '''
def project_get_calculate_function():
project = ProjectBuilder.build()
parent_path = "架空输电线路本体工程/基础工程"
quantity_type = "定额"
code = "YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7"
status, data, error, helper_info = project.get_quantities_node_by_parent_and_code(parent_path, quantity_type, code)
if status != "success":
return {
"helper_info": helper_info,
"error": error,
"status": status,
"data": None
}
total_quantity = sum(float(item.get("数量", 0)) for item in data if item.get("数量"))
return {
"helper_info": helper_info,
"error": error,
"status": status,
"data": total_quantity
}
'''
neo4j_driver = GraphDatabase.driver(neo4j_conf.get("uri"), auth=(neo4j_conf.get("username"), neo4j_conf.get("password")))
old_stdout = sys.stdout
redirected_output = io.StringIO()
ProjectBuilder.register(ProjectToolkitNeo4j, neo4j_driver)
try:
namespace = {
"project": __import__("project"),
"ProjectBuilder": ProjectBuilder,
}
sys.stdout = redirected_output
exec(code_str, namespace)
# 确保neo4j_find_function存在
if "project_get_calculate_function" not in namespace:
raise ValueError("代码中未定义project_get_calculate_function函数")
result_dict = namespace["project_get_calculate_function"]()
sys.stdout = old_stdout
output = redirected_output.getvalue().strip()
if not isinstance(result_dict, dict) or len(result_dict) != 4:
raise ValueError("函数应返回包含4个元素的字典(code, status, message, data)")
logger.debug(f"执行结果: {result_dict}")
if result_dict["status"] == "success":
return {
"code": 20000,
"message": result_dict["error"] if result_dict["error"] else '',
"status": True,
"data": result_dict["data"] if result_dict["data"] else []
}
else:
return {
"code": 50000,
"message": result_dict["error"] if result_dict["error"] else '',
"status": False,
"data": result_dict["data"] if result_dict["data"] else []
}
except Exception as e:
# 确保恢复stdout
sys.stdout = old_stdout
logger.error(f"执行代码时出错: {e}")
logger.error(traceback.format_exc())
return {
"code": 50000,
"message": str(e),
"status": False,
"data": []
}
if __name__ == "__main__":
main()
+2292
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
{"name": "杆塔总基数", "query": "从【架空输电线路本体工程/基础工程】及其子孙项目划分中查找编码中包含【YX2-1~7】的所有【定额】的【数量】之和", "result": [{"name": "基础工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/基础工程", "name": "基础工程"}}, {"name": "定额", "constraints": {"parent": "基础工程", "type": "ProjectQuantity", "类型": "定额", "编码": "YX2-1~7"}}]}
{"name": "角钢塔_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【数量】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "parent": "铁塔、钢管杆组立", "类型": "主材", "name": "包含角钢"}}]}
{"name": "角钢塔_其中:高强钢塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【数量】之和", "result": [{"name": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含【角钢、高强】"}}]}
{"name": "角钢塔_塔材装材费_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【单价】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "类型": "主材", "name": "包含角钢"}}]}
{"name": "角钢塔_其中:高强钢塔材费用_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【单价】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "parent": "铁塔、钢管杆组立", "类型": "主材", "name": "包含角钢、高强"}}]}
{"name": "钢管塔_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【数量】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含钢管塔"}}]}
{"name": "钢管塔_钢管价格_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【单价】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "name": "主材", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name_contains": "钢管塔"}}]}
{"name": "钢管杆_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【数量】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含钢管杆"}}]}
{"name": "钢管杆_钢管价格_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【单价】之和", "result": [{"name": "铁塔、钢管杆组立", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name": "铁塔、钢管杆组立"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "name": "主材", "path": "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立", "name_contains": "钢管杆"}}]}
{"name": "导线及线材_分裂数", "query": "从【架空输电线路本体工程/架线工程】及其子孙项目划分中查找编码中包含【['YX5-67', 'YX5-68', 'YX5-69', 'YX5-78', 'YX5-79', 'YX5-59', 'YX5-60', 'YX5-61', 'YX5-62', 'YX5-63', 'YX5-64', 'YX5-65', 'YX5-66', 'YX5-74', 'YX5-75', 'YX5-76', 'YX5-77', 'YX5-52', 'YX5-53', 'YX5-54', 'YX5-55', 'YX5-56', 'YX5-57', 'YX5-58', 'YX5-70', 'YX5-71', 'YX5-72', 'YX5-73', 'YX5-14', 'YX5-15', 'YX5-16', 'YX5-17', 'YX5-43', 'YX5-44', 'YX5-45', 'YX5-46', 'YX5-47', 'YX5-48', 'YX5-49', 'YX5-50', 'YX5-51', 'YX5-10', 'YX5-11', 'YX5-12', 'YX5-13', 'YX5-38', 'YX5-39', 'YX5-40', 'YX5-41', 'YX5-42', 'YX5-8', 'YX5-9']】的所有【定额】的【参数】之和", "result": [{"name": "架空输电线路本体工程/架线工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程", "name": "架线工程"}}, {"name": "定额", "constraints": {"parent": "架空输电线路本体工程/架线工程", "type": "ProjectQuantity", "类型": "定额", "编码": ["YX5-67", "YX5-68", "YX5-69", "YX5-78", "YX5-79", "YX5-59", "YX5-60", "YX5-61", "YX5-62", "YX5-63", "YX5-64", "YX5-65", "YX5-66", "YX5-74", "YX5-75", "YX5-76", "YX5-77", "YX5-52", "YX5-53", "YX5-54", "YX5-55", "YX5-56", "YX5-57", "YX5-58", "YX5-70", "YX5-71", "YX5-72", "YX5-73", "YX5-14", "YX5-15", "YX5-16", "YX5-17", "YX5-43", "YX5-44", "YX5-45", "YX5-46", "YX5-47", "YX5-48", "YX5-49", "YX5-50", "YX5-51", "YX5-10", "YX5-11", "YX5-12", "YX5-13", "YX5-38", "YX5-39", "YX5-40", "YX5-41", "YX5-42", "YX5-8", "YX5-9"]}}]}
{"name": "导线及线材_其中:节能导线量", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【数量】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设", "name": "导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设", "name": "导地线跨越架设"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含高导电率"}}]}
{"name": "导线及线材_导线装材费_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【线】的所有【主材】的【单价】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设", "name": "导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设", "name": "导地线跨越架设"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含线"}}]}
{"name": "导线及线材_其中:节能导线费用_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【单价】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设", "name": "导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设", "name": "导地线跨越架设"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含高导电率"}}]}
{"name": "导线及线材_导线类型", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设@@架空输电线路本体工程/架线工程/其他架线工程】及其子孙项目划分中查找名称中包含【['钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', '扩径导线', '耐热导线', '碳纤维导线']】的所有【主材】的【参数】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设"}}, {"name": "架空输电线路本体工程/架线工程/其他架线工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/其他架线工程"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": ["钢芯铝绞线", "铝包钢芯铝绞线", "中强度铝合金绞线", "铝合金芯铝绞线", "铝合金芯高导电率铝绞线", "钢芯高导电率铝绞线", "特高强度钢芯铝合金绞线", "扩径导线", "耐热导线", "碳纤维导线"]}}]}
{"name": "基础钢材量", "query": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【数量】之和", "result": [{"name": "基础砌筑", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/基础工程/基础砌筑", "name": "基础砌筑"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "name": "圆钢", "类型": "主材"}}]}
{"name": "基础钢材价格", "query": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【单价】之和", "result": [{"name": "基础砌筑", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/基础工程/基础砌筑", "name": "基础砌筑"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "path": "架空输电线路本体工程/基础工程/基础砌筑", "类型": "主材", "name": "包含圆钢"}}]}
{"name": "本体费用合计_元", "query": "从【工程费用】中获取【架空输电线路本体工程】的【合计费】的属性", "result": [{"name": "工程费用表", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "架空输电线路本体工程", "constraints": {"parent": "工程费用表", "type": "Fee", "name": "架空输电线路本体工程", "code": "BTGC"}}, {"name": "合计费", "constraints": {"parent": "架空输电线路本体工程", "type": "Fee", "name": "合计费"}}]}
{"name": "本体工程人工费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【人工费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "人工费", "constraints": {"parent": "架空输电线路本体工程", "type": "FeeCollection", "name": "人工费"}}]}
{"name": "本体工程人工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【人工费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "人工费", "constraints": {"parent": "架空输电线路本体工程", "type": "FeeCollection", "name": "人工费"}}]}
{"name": "本体工程机械费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【施工机械使用费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "施工机械使用费", "constraints": {"parent": "架空输电线路本体工程", "type": "Fee", "name": "施工机械使用费"}}]}
{"name": "本体工程机械费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【施工机械使用费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "施工机械使用费", "constraints": {"parent": "架空输电线路本体工程", "type": "Fee", "name": "施工机械使用费"}}]}
{"name": "基础工程费用_元", "query": "从【架空输电线路本体工程/基础工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "基础工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/基础工程", "name": "基础工程"}}, {"name": "合计", "constraints": {"parent": "基础工程", "type": "CostItem", "name": "合计"}}]}
{"name": "杆塔工程费用_元", "query": "从【架空输电线路本体工程/杆塔工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "杆塔工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程", "name": "杆塔工程"}}, {"name": "合计", "constraints": {"parent": "杆塔工程", "type": "CostItem", "name": "合计"}}]}
{"name": "接地工程费用_元", "query": "从【架空输电线路本体工程/接地工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "接地工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/接地工程", "name": "接地工程"}}, {"name": "合计", "constraints": {"parent": "接地工程", "type": "CostItem", "name": "合计"}}]}
{"name": "架线工程费用_元", "query": "从【架空输电线路本体工程/架线工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "架线工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程", "name": "架线工程"}}, {"name": "合计", "constraints": {"parent": "架线工程", "type": "CostItem", "name": "合计"}}]}
{"name": "附件工程费用_元", "query": "从【架空输电线路本体工程/附件安装工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "附件安装工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/附件安装工程", "name": "附件安装工程"}}, {"name": "合计", "constraints": {"parent": "附件安装工程", "type": "CostItem", "name": "合计"}}]}
{"name": "辅助工程费用_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "辅助工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/辅助工程", "name": "辅助工程"}}, {"name": "合计", "constraints": {"parent": "辅助工程", "type": "CostItem", "name": "合计"}}]}
{"name": "辅助工程费用_调试_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取取费费用名称属于【合计】的费用", "result": [{"name": "辅助工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/辅助工程", "name": "辅助工程"}}, {"name": "合计", "constraints": {"parent": "辅助工程", "type": "CostItem", "name": "合计"}}]}
{"name": "辅助设施工程_元", "query": "从【工程费用】中获取【辅助设施工程】的【合计费】的属性", "result": [{"name": "辅助设施工程", "constraints": {"type": "ProjectDivisionItem", "name": "辅助设施工程"}}, {"name": "合计费", "constraints": {"type": "CostItem", "name": "合计费", "parent": "辅助设施工程"}}]}
{"name": "其他费用合计_元", "query": "从【工程费用】中获取【其他费用】的【合计费】的属性", "result": [{"name": "工程费用表", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "其他费用", "constraints": {"parent": "工程费用表", "type": "Fee", "name": "其他费用"}}, {"name": "合计费", "constraints": {"parent": "其他费用", "type": "Fee", "name": "合计费"}}]}
{"name": "建场费合计_元", "query": "从【其他费用】中获取【建设场地征用及清理费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "建设场地征用及清理费", "constraints": {"parent": "其他费用", "type": "Fee", "name": "建设场地征用及清理费"}}]}
{"name": "项目建设管理费合计_元", "query": "从【其他费用】中获取【项目建设管理费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目建设管理费", "constraints": {"parent": "其他费用", "type": "Fee", "name": "项目建设管理费"}}]}
{"name": "其中:工程监理费_元", "query": "从【其他费用】中获取【工程监理费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "工程监理费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "工程监理费"}}]}
{"name": "项目建设技术服务费合计_元", "query": "从【其他费用】中获取【项目建设技术服务费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目建设技术服务费", "constraints": {"parent": "其他费用", "type": "Fee", "name": "项目建设技术服务费"}}]}
{"name": "其中:项目前期工作费_元", "query": "从【其他费用】中获取【项目前期工作费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目前期工作费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "项目前期工作费"}}]}
{"name": "其中:勘察费_元", "query": "从【其他费用】中获取【勘察费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "勘察费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "勘察费"}}]}
{"name": "其中:设计费_元", "query": "从【其他费用】中获取【设计费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "设计费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "设计费"}}]}
{"name": "其中:工程建设检测费_元", "query": "从【其他费用】中获取【工程建设检测费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "工程建设检测费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "工程建设检测费"}}]}
{"name": "生产准备费_元", "query": "从【其他费用】中获取【生产准备费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "生产准备费", "constraints": {"parent": "其他费用表", "type": "Fee", "name": "生产准备费"}}]}
{"name": "其中:安全文明施工费_线路_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【安全文明施工费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "安全文明施工费", "constraints": {"parent": "架空输电线路本体工程", "type": "Fee", "name": "安全文明施工费"}}]}
{"name": "其中:安全文明施工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【安全文明施工费】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "安全文明施工费", "constraints": {"parent": "架空输电线路本体工程", "type": "Fee", "name": "安全文明施工费"}}]}
{"name": "基本预备费_元", "query": "从【工程费用】中获取【基本预备费】的【合计费】的属性", "result": [{"name": "基本预备费", "constraints": {"type": "Fee", "name": "基本预备费"}}, {"name": "合计费", "constraints": {"parent": "基本预备费", "type": "Fee", "name": "合计费"}}]}
{"name": "静态投资_元", "query": "从【工程费用】中获取【工程静态投资(一~七项合计)】的【合计费】的属性", "result": [{"name": "工程费用表", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "工程静态投资(一~七项合计)", "constraints": {"parent": "工程费用表", "type": "Fee", "name": "工程静态投资(一~七项合计)"}}, {"name": "合计费", "constraints": {"parent": "工程静态投资(一~七项合计)", "type": "Fee", "name": "合计费"}}]}
{"name": "建设期利息_元", "query": "从【工程费用】中获取【建设期贷款利息】的【合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "建设期贷款利息", "constraints": {"parent": "工程费用", "type": "Fee", "name": "建设期贷款利息", "code": "DKLX"}}, {"name": "合计费", "constraints": {"parent": "建设期贷款利息", "type": "Fee", "name": "合计费"}}]}
{"name": "动态投资_元", "query": "从【工程费用】中获取【工程动态投资(一~八项合计)】的【合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "工程动态投资(一~八项合计)", "constraints": {"parent": "工程费用", "type": "Fee", "name": "工程动态投资(一~八项合计)"}}, {"name": "合计费", "constraints": {"parent": "工程动态投资(一~八项合计)", "type": "Fee", "name": "合计费"}}]}
{"name": "增值税抵扣税额_元", "query": "从【工程费用】中获取【其中:可抵扣增值税额】的【合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "其中:可抵扣增值税额", "constraints": {"parent": "工程费用", "type": "Fee", "name": "其中:可抵扣增值税额"}}, {"name": "合计费", "constraints": {"parent": "其中:可抵扣增值税额", "type": "Fee", "name": "合计费"}}]}
+361
View File
@@ -0,0 +1,361 @@
1.实体类型
EngineeringData
name: STRING
ProjectDivisionSet
name: STRING
ProjectDivisionTree
name: STRING
original_second_level: STRING
original_first_level: STRING
ProjectDivisionItem
type: STRING
取费表id: STRING
name: STRING
序号: STRING
取费表: STRING
GUID: STRING
专业类型: STRING
费率: STRING
资源库名称: STRING
代码: STRING
notCheck: STRING
颜色标记: STRING
编码: STRING
最小资源库编码: STRING
原合价: STRING
备注: STRING
List
合价不含税: STRING
数量: STRING
type: STRING
资源库名称: STRING
工作内容: STRING
取费表类型: STRING
类型: STRING
取费表名称: STRING
单位: STRING
清单名称: STRING
编码: STRING
计算规则: STRING
清单全码: STRING
GUID: STRING
guid: STRING
单价不含税: STRING
单价: STRING
name: STRING
项目特征: STRING
计算式: STRING
合价: STRING
取费表: STRING
ProjectQuantity
数量: STRING
资源库名称: STRING
合价不含税: STRING
特征段: STRING
费用类型: STRING
基准价含税: STRING
结算市场价不含税: STRING
投标单价: STRING
单位: STRING
结算市场价含税: STRING
基准价不含税: STRING
颜色标记: STRING
投标数量: STRING
编码: STRING
类型: STRING
规格型号: STRING
关联父级量: STRING
name: STRING
单价不含税: STRING
损耗率: STRING
截面积: STRING
线重: STRING
id: STRING
供货方: STRING
单重: STRING
集中配送: STRING
市场价含税: STRING
制造长度: STRING
单价含税: STRING
市场价不含税: STRING
增值税率: STRING
合价含税: STRING
运杂费率: STRING
设备类型: STRING
计算式: STRING
人工系数: STRING
定额范围: STRING
定额系数: STRING
基价: STRING
机械费: STRING
人工费: STRING
材料系数: STRING
中标计算式: STRING
机械系数: STRING
材料费: STRING
投标合价: STRING
所属定额库: STRING
批注: STRING
标记: STRING
定额调整系数: STRING
监造物料: STRING
备注: STRING
Quota
特征段: STRING
人工系数: STRING
数量: STRING
定额范围: STRING
定额系数: STRING
合价不含税: STRING
基价: STRING
机械费: STRING
资源库名称: STRING
人工费: STRING
材料系数: STRING
投标单价: STRING
中标计算式: STRING
费用类型: STRING
机械系数: STRING
类型: STRING
材料费: STRING
单位: STRING
颜色标记: STRING
投标合价: STRING
编码: STRING
关联父级量: STRING
投标数量: STRING
计算式: STRING
id: STRING
name: STRING
单价不含税: STRING
所属定额库: STRING
批注: STRING
标记: STRING
定额调整系数: STRING
颜色标记: STRING
备注: STRING
MainMaterial
数量: STRING
资源库名称: STRING
合价不含税: STRING
特征段: STRING
费用类型: STRING
基准价含税: STRING
结算市场价不含税: STRING
投标单价: STRING
单位: STRING
结算市场价含税: STRING
基准价不含税: STRING
颜色标记: STRING
投标数量: STRING
编码: STRING
类型: STRING
规格型号: STRING
关联父级量: STRING
name: STRING
单价不含税: STRING
损耗率: STRING
截面积: STRING
线重: STRING
id: STRING
供货方: STRING
单重: STRING
集中配送: STRING
市场价含税: STRING
制造长度: STRING
单价含税: STRING
市场价不含税: STRING
增值税率: STRING
合价含税: STRING
Equipment
特征段: STRING
单价含税: STRING
单位: STRING
资源库名称: STRING
合价不含税: STRING
类型: STRING
投标数量: STRING
投标单价: STRING
关联父级量: STRING
颜色标记: STRING
运杂费率: STRING
设备类型: STRING
编码: STRING
供货方: STRING
规格型号: STRING
单价不含税: STRING
id: STRING
name: STRING
数量: STRING
计算式: STRING
合价含税: STRING
MaterialOrEquipment
type: STRING
预算价不含税: STRING
单位: STRING
结算市场价不含税: STRING
暂估价: STRING
编码: STRING
结算市场价含税: STRING
全口径市场价不含税: STRING
全口径市场价含税: STRING
是否未计价: STRING
unique_id: STRING
供货方: STRING
结算预算价含税: STRING
结算预算价不含税: STRING
市场价含税: STRING
预算价含税: STRING
id: STRING
name: STRING
数量: STRING
市场价不含税: STRING
拆分: STRING
商品砼: STRING
children: STRING
CostSet
name: STRING
GUID: STRING
CostItem
name: STRING
cost: STRING
unique_id: STRING
id: STRING
FeeTableTemplateSet
name: STRING
typeList: STRING
FeeTableTemplateItem
type: STRING
name: STRING
profession: STRING
outlayID: STRING
FeeCollection
name: STRING
serialNumber: STRING
base: STRING
code: STRING
rate: STRING
remark: STRING
FeeScheduleSet
name: STRING
FeeScheduleItem
name: STRING
ProjectPropertySet
name: STRING
ProjectProperty
特殊地区: STRING
调差选择所在地: STRING
工程所在地: STRING
编制时间: STRING
工程版本: STRING
项目划分: STRING
工程阶段: STRING
工程名称: STRING
专业类型: STRING
地区类型: STRING
组价方式: STRING
人工调差系数: STRING
调差选择地区类型: STRING
机械调差系数: STRING
架线类型: STRING
安装机械调差系数: STRING
清单规范: STRING
市场价唯一: STRING
材料调差系数: STRING
甲供材料计入综合单价: STRING
人工按系数调差: STRING
最高投标限价(万元): STRING
工程总投资: STRING
是否按单位控制工程量精度: STRING
安装人工调差系数: STRING
不同土质定额归属不同清单: STRING
住房公积金缴费费率: STRING
是否是合并工程: STRING
招标人: STRING
相同清单合并: STRING
执行规范: STRING
安装材料调差系数: STRING
拆除调差系数年份: STRING
调差系数年份: STRING
工程税率: STRING
甲供材料计入本体: STRING
软件名称: STRING
电压等级: STRING
预算类型: STRING
本期台数: STRING
建筑人工调差系数: STRING
单台容量: STRING
建筑拆除人工调差系数: STRING
配置选项: STRING
施工企业配合调试费费率: STRING
安装其他设备运杂费率: STRING
安装材机调差系数: STRING
工程性质: STRING
表头设置: STRING
工程静态投资(万元): STRING
安装主要设备运杂费率: STRING
编制依据: STRING
工程动态投资(万元): STRING
基本预备费费率: STRING
阶段类型: STRING
BCL版本: STRING
社会保险费缴费费率: STRING
2. 实体间的关系
(:EngineeringData)-[:HAS_CHILD]->(:ProjectPropertySet)
(:EngineeringData)-[:HAS_CHILD]->(:FeeScheduleSet)
(:EngineeringData)-[:HAS_CHILD]->(:FeeTableTemplateSet)
(:EngineeringData)-[:HAS_CHILD]->(:ProjectDivisionSet)
(:ProjectDivisionSet)-[:HAS_CHILD]->(:ProjectDivisionSet)
(:ProjectDivisionSet)-[:HAS_CHILD]->(:ProjectDivisionItem)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:List)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:ProjectDivisionItem)
(:ProjectDivisionItem)-[:USE]->(:CostSet)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:ProjectQuantity)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:Quota)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:MainMaterial)
(:ProjectDivisionItem)-[:HAS_CHILD]->(:Equipment)
(:ProjectQuantity)-[:HAS_CHILD]->(:MaterialOrEquipment)
(:FeeTableTemplateSet)-[:HAS_CHILD]->(:FeeTableTemplateSet)
(:FeeTableTemplateSet)-[:HAS_CHILD]->(:FeeTableTemplateItem)
(:FeeTableTemplateItem)-[:HAS_CHILD]->(:FeeCollection)
(:FeeCollection)-[:HAS_CHILD]->(:FeeCollection)
(:FeeScheduleSet)-[:HAS_CHILD]->(:FeeScheduleItem)
(:FeeScheduleItem)-[:HAS_CHILD]->(:Fee)
(:Fee)-[:HAS_CHILD]->(:Fee)
(:ProjectPropertySet)-[:HAS_CHILD]->(:ProjectProperty)
(:List)-[:HAS_CHILD]->(:ProjectQuantity)
(:List)-[:HAS_CHILD]->(:Equipment)
(:List)-[:HAS_CHILD]->(:MainMaterial)
(:List)-[:HAS_CHILD]->(:Quota)
(:List)-[:USE]->(:CostSet)
(:Quota)-[:HAS_CHILD]->(:MaterialOrEquipment)
(:CostSet)-[:HAS_CHILD]->(:CostItem)
(:ProjectDivisionTree)-[:HAS_CHILD]->(:ProjectDivisionItem)
(:ProjectQuantity)-[:HAS_CHILD]->(:ProjectQuantity)
(:ProjectQuantity)-[:HAS_CHILD]->(:MainMaterial)
(:MainMaterial)-[:HAS_CHILD]->(:ProjectQuantity)
(:MainMaterial)-[:HAS_CHILD]->(:MainMaterial)
(:ProjectQuantity)-[:USE]->(:CostSet)
(:ProjectDivisionTree)-[:USE]->(:CostSet)
(:ProjectDivisionSet)-[:HAS_CHILD]->(:ProjectDivisionTree)
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
import os
import sys
import logging
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
current_file = os.path.splitext(os.path.basename(__file__))[0]
from datetime import datetime
# 获取当前时间,格式化为字符串
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
current_file = os.path.splitext(os.path.basename(__file__))[0]
log_filename = f"{current_file}{now_str}.log"
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"),
logging.StreamHandler()
],
)
logger = logging.getLogger(current_file)
import logging
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
import json
import os
from src.dialog_manager import DialogManager
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
import yaml
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
success_count = 0
fail_count = 0
questions = []
error_list = []
success_results = []
fail_results = []
def main():
global fail_count, success_count, questions, error_list
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
# 加载 zhibiao.jsonl
zhibiao_data = []
with open('./data/zhibiao.jsonl', 'r', encoding='utf-8') as f:
for line in f:
zhibiao_data.append(json.loads(line))
# 提取指标映射关系并批量执行
for item in zhibiao_data:
query = item['query']
name = item['name']
selected_knowledge = item['result']
logger.info(f"指标名称 {name} 问题: {query} 开始生成代码")
result = dialog_manager.generated_code(query, selected_knowledge)
if isinstance(result, dict) and result.get('status', False):
code = result['data']
success_count += 1
success_results.append({
"name": name,
"query": query,
"code": code,
})
else:
questions.append(query)
fail_count += 1
error_msg = result.get('message', '调用 generated_code 返回空结果') if isinstance(result, dict) else f"问题 {query} {selected_knowledge} 调用 execute_generated_code 返回空结果"
error_list.append(error_msg)
fail_results.append({
"name": name,
"query": query,
"messages": error_msg,
"selected_knowledge": selected_knowledge,
})
#print(result)
if __name__ == "__main__":
main()
total = success_count + fail_count
if total > 0:
success_rate = success_count / total
fail_rate = fail_count / total
else:
success_rate = fail_rate = 0
print(f"问题总数: {total}")
print(f"成功比例: {success_rate * 100:.2f}%")
print(f"失败比例: {fail_rate * 100:.2f}%")
print("错误列表:")
for error in error_list:
print(error)
# 保存成功结果到 jsonl 文件
success_filename = f'./data/code.jsonl'
with open(success_filename, 'w', encoding='utf-8') as f:
for item in success_results:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
# 保存失败结果到 jsonl 文件
fail_filename = f'./data/fail_{now_str}.jsonl'
with open(fail_filename, 'w', encoding='utf-8') as f:
for item in fail_results:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
+186
View File
@@ -0,0 +1,186 @@
import streamlit as st
import os
import sys
import json
import logging
import os
from datetime import datetime
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# 获取当前时间,格式化为字符串
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
# 日志目录
log_dir = "logs"
# 如果目录不存在,则创建
os.makedirs(log_dir, exist_ok=True)
current_file = os.path.splitext(os.path.basename(__file__))[0]
log_filename = f"{current_file}_{now_str}.log"
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"), logging.StreamHandler()],
)
logger = logging.getLogger(current_file)
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
from src.code_executor import CodeExecutor
# 初始化资源和客户端的函数,使用缓存避免重复加载
@st.cache_resource
def initialize_resources():
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
# 创建Neo4j检索器
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
return {
"config": config,
"business_structure": business_structure,
"bowei_api_docs": bowei_api_docs,
"llm_client_coder": llm_client_coder,
"prompt_manager": prompt_manager,
"knowledge_retriever": knowledge_retriever,
"embedding_client": embedding_client,
"code_executor": code_executor,
}
# 使用缓存加载JSONL文件
@st.cache_data
def load_jsonl(file_path):
"""加载JSONL文件并返回JSON指标列表"""
records = []
try:
with open(file_path, "r", encoding="utf-8") as file:
for line in file:
if line.strip():
records.append(json.loads(line))
return records
except Exception as e:
st.error(f"加载文件失败: {str(e)}")
return []
def run_code(data, code_executor):
"""运行JSON指标中的代码并返回结果"""
if not data or "code" not in data:
return {"code": 40000, "message": "没有可执行的代码", "status": False, "data": None}
return code_executor.execute_code(data["code"])
def main():
st.set_page_config(layout="wide", page_title="工程指标查看器")
st.title("工程指标查看器")
# 初始化资源
resources = initialize_resources()
code_executor = resources["code_executor"]
# 设置默认文件路径
default_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../tests", "code.jsonl")
# 文件路径输入
file_path = st.text_input("JSONL文件路径", value=default_file_path)
if not os.path.exists(file_path):
st.warning(f"文件不存在: {file_path}")
return
# 加载JSONL文件
records = load_jsonl(file_path)
if not records:
st.warning("没有找到有效的指标")
return
# 初始化会话状态
if "selected_index" not in st.session_state:
st.session_state.selected_index = 0
if "execution_results" not in st.session_state:
st.session_state.execution_results = {}
# 创建两列布局
col1, col2 = st.columns([1, 3])
# 左侧列表
with col1:
st.subheader("指标列表")
for i, record in enumerate(records):
if st.button(record.get("name", f"指标 {i+1}"), key=f"btn_{i}"):
st.session_state.selected_index = i
# 右侧详细信息
with col2:
if st.session_state.selected_index < len(records):
selected_record = records[st.session_state.selected_index]
record_id = selected_record.get("id", str(st.session_state.selected_index))
st.subheader(f"指标名称: {selected_record.get('name', '无名称指标')}")
st.info(selected_record.get('query', '无查询信息'))
# 将指标代码标题、运行代码按钮、执行结果放在同一行
run_btn_col, title_col, result_col = st.columns([1, 1, 4])
with title_col:
st.subheader("执行结果:")
with result_col:
title_code_result = st.subheader("")
with run_btn_col:
if st.button("执行指标代码"):
with st.spinner('正在执行代码...'):
result = run_code(selected_record, code_executor)
if result.get('status'):
#st.success("执行成功")
title_code_result.info(result.get('data'))
else:
title_code_result.error(f"执行失败: {result.get('message', '未知错误')}")
st.code(selected_record.get('code', '无代码'), language='python')
if __name__ == "__main__":
main()
+117
View File
@@ -0,0 +1,117 @@
import json
from typing import Dict, List, Any
import copy
class ExpenseProcessor:
def __init__(self):
pass
@staticmethod
def calculate_parent_costs(node: Dict[str, Any]) -> List[Dict[str, Any]]:
if "children" not in node:
if "id" in node and "cost" in node:
return [{"id": node["id"], "cost": node["cost"]}]
elif "cost" in node:
return [{"cost": node["cost"]}]
return []
result_nodes = []
processed_ids = set()
for child in node["children"]:
child_costs = ExpenseProcessor.calculate_parent_costs(child)
for cost_item in child_costs:
if "id" in cost_item:
found = False
for existing in result_nodes:
if "id" in existing and existing["id"] == cost_item["id"]:
existing["cost"] = str(float(existing["cost"]) + float(cost_item["cost"]))
found = True
break
if not found:
result_nodes.append(copy.deepcopy(cost_item))
processed_ids.add(cost_item["id"])
else:
found = False
for existing in result_nodes:
if "id" not in existing:
existing["cost"] = str(float(existing["cost"]) + float(cost_item["cost"]))
found = True
break
if not found:
result_nodes.append(copy.deepcopy(cost_item))
return result_nodes
@staticmethod
def process_node(node: Dict[str, Any]) -> Dict[str, Any]:
result = copy.deepcopy(node)
if "children" not in node or not node["children"]:
return result
cost_items = ExpenseProcessor.calculate_parent_costs(node)
if cost_items:
result["sum"] = cost_items
result["children"] = [ExpenseProcessor.process_node(child) for child in node["children"]]
return result
@staticmethod
def process_expense_preview(expense_preview: Dict[str, Any]) -> Dict[str, Any]:
result = copy.deepcopy(expense_preview)
for category_key, category_value in expense_preview.items():
for subcategory_key, subcategory_value in category_value.items():
if isinstance(subcategory_value, list):
result[category_key][subcategory_key] = [
ExpenseProcessor.process_node(item) for item in subcategory_value
]
return result
@classmethod
def load_and_process_from_file(cls, input_path: str, output_path: str = None) -> Dict[str, Any]:
"""
从文件加载 JSON 并处理
:param input_path: 输入文件路径
:param output_path: 输出文件路径(可选)
:return: 处理后的完整数据
"""
with open(input_path, "r", encoding="utf-8") as f:
data = json.load(f)
if "projectData" in data and "expensePreview" in data["projectData"]:
processed_data = copy.deepcopy(data)
processed_data["projectData"]["expensePreview"] = cls.process_expense_preview(
data["projectData"]["expensePreview"]
)
if output_path:
with open(output_path, "w", encoding="utf-8") as f:
json.dump(processed_data, f, ensure_ascii=False, indent=4)
print(f"处理完成,结果已保存到 {output_path}")
return processed_data
else:
raise ValueError("未找到 projectData.expensePreview 路径")
@classmethod
def process_raw_data(cls, raw_data: Dict[str, Any]) -> Dict[str, Any]:
"""
直接处理原始数据(不涉及文件读写)
:param raw_data: 原始数据,格式应包含 projectData.expensePreview
:return: 处理后的数据
"""
if "projectData" in raw_data and "expensePreview" in raw_data["projectData"]:
processed_data = copy.deepcopy(raw_data)
processed_data["projectData"]["expensePreview"] = cls.process_expense_preview(
raw_data["projectData"]["expensePreview"]
)
return processed_data
else:
raise ValueError("未找到 projectData.expensePreview 路径")
if __name__ == "__main__":
input_file = "dataset/json/主网预算/架空_clean.json" # 输入 JSON 文件路径
output_file = "dataset/json/主网预算/output.json" # 输出 JSON 文件路径
# 使用类方法加载并处理 JSON 文件
ExpenseProcessor.load_and_process_from_file(input_file, output_file)
+226
View File
@@ -0,0 +1,226 @@
import chardet
import xml.etree.ElementTree as ET
import json
import re
def transform_string(input_str: str) -> str:
# 提取所有【】中的内容
matches = re.findall(r'【(.*?)】', input_str)
if len(matches) < 2:
return input_str # 不符合格式,直接返回
first_block = matches[0] # 第一个块,如“工程费用”
second_block = matches[1] # 第二个块,可能是“基本预备费.合计费”或“基本预备费”
suffix = input_str.split('')[-1] # 获取末尾部分,如“的属性”
# 判断第二个块是否包含点号
if '.' in second_block:
nested_parts = second_block.split('.')
nested_str = ''.join(f'{part}】的' for part in nested_parts).rstrip('')
return f'从【{first_block}】中获取{nested_str}{suffix}'
else:
return input_str
def clean_bracketed_strings(input_str: str) -> str:
# 替换【'xxx'】为【xxx】
result = re.sub(r"'([^']+)'", r"\1】", input_str)
# 替换【['xxx']】为【xxx】
result = re.sub(r"\['([^']+)'\]】", r"\1】", result)
# 替换【['xxx','yyy']】为【xxx,yyy】(多个项的情况)
result = re.sub(r"\[((?:'[^']+',?)+)\]】", lambda m: "" + m.group(1).replace("'", "") + "", result)
return result
def read_xml_as_string(file_path):
# 先读取部分字节探测编码
with open(file_path, 'rb') as f:
raw_data = f.read()
result = chardet.detect(raw_data)
encoding = result['encoding']
# 使用探测到的编码重新读取为字符串
return raw_data.decode(encoding)
def parse_keyword(keyword, indicator_name):
# 特殊处理:电压等级
# if indicator_name == "电压等级":
# return {"映射规则": "1", "指标映射": [keyword]}
# 处理范围表达式(包含"||"分隔符)
if "||" in keyword:
parts = keyword.split("||")
table_rows = []
all_codes = []
for part in parts:
if "@@" not in part:
continue
codes_str, value = part.split("@@", 1)
code_ranges = codes_str.split("")
for code_range in code_ranges:
# 处理连续编号(如YX5-67~69
if "~" in code_range:
prefix, range_part = code_range.rsplit("-", 1)
start_str, end_str = range_part.split("~")
try:
start = int(start_str)
end = int(end_str)
for num in range(start, end + 1):
all_codes.append(f"{prefix}-{num}")
except ValueError:
all_codes.append(code_range)
else:
all_codes.append(code_range)
table_rows.append(f"| {codes_str} | {value} |")
rule_table = "| 资源识别规则 | 指标值 |\n|-------|-------|\n" + "\n".join(table_rows)
return {"映射规则": rule_table, "指标映射": all_codes}
# 处理数学公式(包含"/"和括号)
if "/" in keyword and "(" in keyword and ")" in keyword:
# 提取分子(括号前部分)
molecule = keyword.split("/")[0].strip()
# 提取分母(括号内部分)
denominator_start = keyword.find("(") + 1
denominator_end = keyword.find(")")
denominator_expr = keyword[denominator_start:denominator_end]
# 分割分母中的加法项
denominator_items = [item.strip() for item in denominator_expr.split("+")]
return {"映射规则": keyword, "指标映射": [molecule] + denominator_items}
# 处理加法表达式
if "+" in keyword:
items = [item.strip() for item in keyword.split("+")]
return {"映射规则": keyword, "指标映射": items}
# 默认处理(普通关键字)
return {"映射规则": keyword, "指标映射": [keyword]}
def xml_to_json(xml_content, output_path):
root = ET.fromstring(xml_content)
records = root.findall('.//records/record')
result = []
# 定义需要特殊处理的数据来源类型
scope_based_sources = ["主材单价", "主材参数", "主材数量", "定额参数", "定额数量", "工程费用"]
direct_sources = ["报表指标", "指标库"]
project_division = ["项目划分费用"]
for record in records:
unit = record.get("单位")
unit_type = record.get("单价类型")
order = record.get("序号")
extraction_method = record.get("提取方式")
indicator_type = record.get("指标类型")
index_extraction_scope = record.get("指标提取范围")
data_sources = record.get("数据来源")
indicator_name = record.get("指标名称")
keyword = record.get("关键字")
parsed = parse_keyword(keyword, indicator_name)
base_item = {
"指标名称": indicator_name,
"code": "",
"单位": unit,
"单价类型": unit_type,
"序号": order,
"提取方式": extraction_method,
"指标类型": indicator_type,
"数据来源": data_sources
}
if data_sources in direct_sources:
base_item["指标描述"] = {
"指标映射": parsed["指标映射"],
"映射规则": parsed["映射规则"]
}
result.append(base_item)
elif data_sources in project_division:
mapping_desc = f"返回项目划分【{index_extraction_scope}】下取费名称等于【{keyword}】的费用"
base_item["指标描述"] = {
"指标映射": mapping_desc,
"映射规则": parsed["映射规则"]
}
result.append(base_item)
elif data_sources in scope_based_sources:
temp0 = parsed["指标映射"]
temp1 = parsed["映射规则"]
if index_extraction_scope:
# 取数据来源的最后两个字(如"单价"、"参数"等)
temp2 = data_sources[-2:]
# 取数据来源的开头两个字(如"定额"、"清单"等)
temp3 = data_sources[0:2]
if temp3 in ["清单", "定额", "人材机"]:
mapping_desc = f"返回项目划分【{index_extraction_scope}】下编码包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
elif temp3 in ["主材", "设备"]:
mapping_desc = f"返回项目划分【{index_extraction_scope}】及名称包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
else:
mapping_desc = f"从【{data_sources}】中获取{temp0}的值"
base_item["指标描述"] = {
"指标映射": mapping_desc,
"映射规则": temp1
}
result.append(base_item)
else:
# 处理未定义的数据来源类型
base_item["指标描述"] = {
"指标映射": parsed["指标映射"],
"映射规则": parsed["映射规则"]
}
result.append(base_item)
for item in result:
desc = item.get("指标描述", {})
mapping_rule = desc.get("映射规则")
indicator_map = desc.get("指标映射")
if isinstance(mapping_rule, str) and isinstance(indicator_map, str):
match = re.search(r"@([^\.]+)\.", mapping_rule)
if match:
source = match.group(1)
# 替换【工程费用】为提取的 source(如【其他费用】)
new_mapping = re.sub(r"【.*?】", f"{source}", indicator_map)
new_mapping = re.sub(r"@[^.]+\.", "", new_mapping)
item["指标描述"]["指标映射"] = new_mapping.replace('[', '').replace(']', '')
if isinstance(indicator_map, list):
first_elem = indicator_map[0]
if first_elem.startswith("(") and first_elem.endswith(")"):
del indicator_map[0]
# 保存为 JSON 文件
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2)
for item in result:
desc = item.get("指标描述", {})
mapping_rule = desc.get("映射规则")
indicator_map = desc.get("指标映射")
if isinstance(indicator_map, str):
new_mapping = clean_bracketed_strings(indicator_map)
new_mapping = transform_string(new_mapping)
item["指标描述"]["指标映射"] = new_mapping
# 保存为 JSON 文件
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2)
return "结果已保存"
xml_content = read_xml_as_string('dataset/主网架空线路造价分析指标.xml')
json_output = xml_to_json(xml_content, output_path= "./data/zhibiao.json")
print("转换完毕!")
+184
View File
@@ -0,0 +1,184 @@
# tests/test_userinteraction.py
import os
import sys
import json
import logging
import os
from datetime import datetime
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
current_file = os.path.splitext(os.path.basename(__file__))[0]
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
log_filename = f"{current_file}_{now_str}.log"
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"),
logging.StreamHandler()
],
)
logger = logging.getLogger(current_file)
def setup_logger(logger_name):
"""
设置指定名称的logger,将其级别设置为WARNING并禁用传播
:param logger_name: logger的名称
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
return logger
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
for name in logger_names:
setup_logger(name)
from src.config import Config
from src.document_loader import load_file
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.user_interaction import UserInteraction
import json
from src.dialog_manager import DialogManager
from src.multi_llm_client import MultiAPIKeyChatOpenAI
from src.code_executor import CodeExecutor
from src.neo4j_raw_retriever import Neo4jRawRetriever
from src.prompt_manager import PromptManager
import yaml
from src.config import Config
from src.document_loader import load_file
from src.embedding_client import EmbeddingClient
from src.project import ProjectBuilder, ProjectToolkit
from src.project_implementation import ProjectToolkitNeo4j
success_count = 0
fail_count = 0
questions = []
error_list = []
success_list = []
def main():
global success_count, fail_count, questions, error_list, success_list
config = Config()
business_structure = load_file(config.business_object_structure_path)
bowei_api_docs = load_file(config.bowei_api_docs_path)
llm_client = MultiAPIKeyChatOpenAI(config.openai)
user_interaction = UserInteraction(llm_client.llm, business_structure)
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
prompt_manager = PromptManager()
neo4j_conf = config.neo4j_conf
embedding_conf = config.embedding
embedding_client = EmbeddingClient(embedding_conf)
# 创建Neo4j检索器
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
business_structure,
bowei_api_docs,
code_executor,
knowledge_retriever,
prompt_manager,
)
# 读取 zhibiao.json
zhibiao_path = os.path.join(os.path.dirname(__file__), "../data/zhibiao.json")
with open(zhibiao_path, "r", encoding="utf-8") as f:
zhibiao_data = json.load(f)
isTest = True
isTest = False
if isTest:
zhibiao_data = [
{
"指标名称": "杆塔总基数",
"指标描述": {
"指标映射": "从【架空输电线路本体工程/附件安装工程】项目划分中获取名称属于【'合计'】的费用",
"映射规则": "YX2-1~7"
},
"code": "",
"单位": "",
"单价类型": None,
"序号": "1",
"提取方式": None,
"指标类型": "工程量指标",
"数据来源": "定额数量"
}
]
for idx, item in enumerate(zhibiao_data):
name = item.get("指标名称", "")
datasource = item.get("数据来源", "")
if datasource in ("报表指标", "指标库"):
logger.info(f"跳过索引 {idx},数据来源为 {datasource}")
continue
query = item.get("指标描述", {}).get("指标映射", "")
if not query:
logger.warning(f"索引 {idx} 缺少指标映射,跳过")
continue
try:
# 调用用户交互理解接口(同步调用)
result = user_interaction.understand(query)
if not result:
logger.error(f"问题: {query} 没有找到符合要求的数据")
fail_count += 1
error_list.append(f"指标名称 {name} 问题 {query} 调用 understand 返回空结果")
continue
# 这里示例只打印理解结果,你可以根据业务逻辑替换为后续处理
logger.info(
f"指标名称 {name} 问题: {query} 理解结果: "
f"{[{'name': r.get('name'), 'constraints': r.get('constraints')} for r in result]}"
)
success_list.append({
"name": name,
"query": query,
"result": [{'name': r.get('name'), 'constraints': r.get('constraints')} for r in result]
})
success_count += 1
except Exception as e:
logger.error(f"指标名称 {name} 问题: {query} 处理异常: {e}")
fail_count += 1
error_list.append(f"指标名称 {name} 问题 {query} 异常: {e}")
total = success_count + fail_count
success_rate = (success_count / total) * 100 if total > 0 else 0
fail_rate = (fail_count / total) * 100 if total > 0 else 0
print(f"问题总数: {total}")
print(f"成功比例: {success_rate:.2f}%)")
print(f"失败比例: {fail_rate:.2f}%)")
print("错误列表:")
for error in error_list:
print(error)
# 将成功内容保存为 jsonl 文件
success_jsonl_path = os.path.join(os.path.dirname(__file__), f"../data/zhibiao.jsonl")
with open(success_jsonl_path, "w", encoding="utf-8") as f:
for item in success_list:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
if __name__ == "__main__":
main()