Compare commits
48 Commits
f2201b8b33
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 32136be5db | |||
| 46552a536f | |||
| fcb09c04f2 | |||
| b352571e17 | |||
| cfb4f4ea6a | |||
| dc43b69164 | |||
| d1c129c691 | |||
| 35d50305c8 | |||
| 72e4e9ef6f | |||
| 3ba0a4b55c | |||
| 2889ec514b | |||
| f94fa82d88 | |||
| be98bb1495 | |||
| af64d962eb | |||
| 7e7109e01b | |||
| 856810025d | |||
| bd8b3c29bd | |||
| 7c3b0c2882 | |||
| 6c6ddb052d | |||
| c1e7f3d007 | |||
| a4fc746411 | |||
| 830a2b49c4 | |||
| 05e7bd2711 | |||
| 59d76b1d72 | |||
| a742c52d00 | |||
| 90c46d2a3f | |||
| 20b561cf04 | |||
| 9b3d5b7ef1 | |||
| b792f9acfa | |||
| 31224ba359 | |||
| ddbe21b1ed | |||
| 36f298fe07 | |||
| 5acfd1f90b | |||
| b6d82c490e | |||
| bf3c657ad7 | |||
| 6013b7453c | |||
| a356a93b95 | |||
| a098dee10d | |||
| 1e4c468116 | |||
| 56e5258a4d | |||
| 714beff8ad | |||
| f0d49a6d38 | |||
| 87456077e1 | |||
| 5d18ac28dd | |||
| 98e26e6a5b | |||
| 9ead052a18 | |||
| 18dfc705b4 | |||
| 705cc969d9 |
+6
-2
@@ -1,4 +1,8 @@
|
||||
BoweiAgent.log
|
||||
*.log
|
||||
__pycache__/*
|
||||
.vscode/launch.json
|
||||
src/__pycache__/*
|
||||
.vscode/*
|
||||
.cursor/*
|
||||
logs/*
|
||||
cache/*
|
||||
转化服务/*
|
||||
+69
-5
@@ -1,30 +1,94 @@
|
||||
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:
|
||||
api_key: sk-bbeamiumkouptsrueilgufqqyuumelcsivxwjbdugqwsqhwj
|
||||
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_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版本需要指定
|
||||
#organization: your_organization_id # 可选
|
||||
api_type: openai # 可选,默认为 openai;如果用 Azure 则为 azure
|
||||
#model_name: Qwen/Qwen2.5-Coder-7B-Instruct
|
||||
#model_name: Qwen/Qwen3-8B
|
||||
model_name: deepseek-ai/DeepSeek-V3
|
||||
|
||||
embedding:
|
||||
api_base: https://api.siliconflow.cn/v1
|
||||
model_name: BAAI/bge-m3
|
||||
api_key: sk-bbeamiumkouptsrueilgufqqyuumelcsivxwjbdugqwsqhwj
|
||||
api_key: sk-xlrnesfcuwrpevdwbuhthivpygwyzwbxxsyvhzzwrkpzjduk
|
||||
|
||||
log_level: INFO
|
||||
|
||||
langsmith:
|
||||
api_key: your_langsmith_api_key
|
||||
api_key: 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
|
||||
api_url: https://api.smith.langchain.com/api/v1
|
||||
project: your_project_name
|
||||
tracing_enabled: true
|
||||
|
||||
@@ -7,7 +7,7 @@ from abc import ABC, abstractmethod
|
||||
import json
|
||||
|
||||
|
||||
class ProjectTookiIt(ABC):
|
||||
class ProjectToolkit(ABC):
|
||||
"""
|
||||
项目类(抽象基类)
|
||||
描述: 代表整个项目结构的顶层容器
|
||||
@@ -576,3 +576,4 @@ class Fee:
|
||||
self.其他费 = None # xsd:string (可选)
|
||||
self.施工费 = None # xsd:string (可选)
|
||||
self.单位投资 = None # xsd:string (可选)
|
||||
|
||||
+278
-127
@@ -7,7 +7,7 @@ from abc import ABC, abstractmethod
|
||||
import json
|
||||
|
||||
|
||||
class ProjectTookiIt(ABC):
|
||||
class ProjectToolkit(ABC):
|
||||
"""
|
||||
项目类(抽象基类)
|
||||
描述: 代表整个项目结构的顶层容器
|
||||
@@ -19,22 +19,19 @@ class ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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 ProjectTookiIt(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,3 +688,43 @@ class Fee:
|
||||
self.施工费 = None # xsd:string (可选)
|
||||
self.单位投资 = None # xsd:string (可选)
|
||||
|
||||
|
||||
class ProjectBuilder:
|
||||
# 存储注册的工具类
|
||||
_registry = None
|
||||
_config = {}
|
||||
|
||||
"""项目工具工厂类"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def register(cls, toolkit_class: type, config: dict):
|
||||
"""
|
||||
注册工具类到工厂
|
||||
|
||||
参数:
|
||||
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(f"未注册的类,请先注册类")
|
||||
|
||||
return cls._registry(cls._config)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Project(工程: 500kV智能变电站新建工程)
|
||||
Project(工程)
|
||||
│
|
||||
├── ProjectAttributeSet(工程属性集)
|
||||
│ ├── Attribute(项目编码)[单位:无, 值:BD-500kV-2024-005]
|
||||
|
||||
@@ -1,59 +1,70 @@
|
||||
EngineeringData(工程: 500kV智能变电站新建工程)
|
||||
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]
|
||||
@@ -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.
@@ -1,65 +1,83 @@
|
||||
# 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.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并设置级别
|
||||
neo4j_logger = logging.getLogger("neo4j")
|
||||
neo4j_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
neo4j_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 src.project import ProjectBuilder, ProjectToolkit
|
||||
from src.project_implementation import ProjectToolkitNeo4j
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
config = Config()
|
||||
|
||||
# 根据配置设置环境变量
|
||||
tracing_enabled = config.langsmith.get("tracing_enabled", False)
|
||||
os.environ["LANGSMITH_PROJECT"] = config.langsmith.get("project")
|
||||
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 = MultiAPIKeyChatOpenAI(config.openai)
|
||||
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
|
||||
prompt_manager = PromptManager()
|
||||
|
||||
code_executor = CodeExecutor(prompt_manager.prompts, llm_client)
|
||||
|
||||
neo4j_conf = config.neo4j_conf
|
||||
embedding_conf = config.embedding
|
||||
|
||||
embedding_client = EmbeddingClient(embedding_conf)
|
||||
|
||||
# 创建Neo4j检索器
|
||||
|
||||
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
|
||||
|
||||
|
||||
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
|
||||
|
||||
dialog_manager = DialogManager(
|
||||
llm_client,
|
||||
business_structure,
|
||||
@@ -69,9 +87,15 @@ def main():
|
||||
prompt_manager,
|
||||
)
|
||||
|
||||
pre_input_question = "查找一下名称中包含基础工程的项目划分"
|
||||
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回其人工费乘以1000的值。'
|
||||
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回单位。'
|
||||
|
||||
try:
|
||||
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
|
||||
finally:
|
||||
|
||||
neo4j_driver.close()
|
||||
|
||||
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
+1032
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -3,4 +3,7 @@ langchain-core
|
||||
langchain-experimental
|
||||
pyyaml
|
||||
neo4j
|
||||
langchain-neo4j
|
||||
langchain-neo4j
|
||||
langgraph
|
||||
chardet
|
||||
streamlit-textcomplete
|
||||
+161
-56
@@ -1,50 +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_implementation 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[:50]}...")
|
||||
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": 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,
|
||||
"project_implementation": __import__("project_implementation"),
|
||||
"project": __import__("project"),
|
||||
"logger": logger,
|
||||
}
|
||||
|
||||
old_stdout = sys.stdout
|
||||
@@ -54,61 +116,104 @@ class CodeExecutor:
|
||||
exec(code_str, namespace)
|
||||
|
||||
# 确保neo4j_find_function存在
|
||||
if "neo4j_find_function" not in namespace:
|
||||
raise ValueError("代码中未定义neo4j_find_function函数")
|
||||
if "project_get_calculate_function" not in namespace:
|
||||
raise ValueError("代码中未定义project_get_calculate_function函数")
|
||||
|
||||
result_tuple = namespace["neo4j_find_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)
|
||||
pre_code = code.content
|
||||
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:
|
||||
result = self.execute_code(pre_code)
|
||||
if result["status"] == "success":
|
||||
logger.info(f"代码执行成功,返回结果长度:{len(result['data'])}")
|
||||
return result["data"]
|
||||
else:
|
||||
error_msg = result.get("error", "未知错误")
|
||||
if prev_happend_error:
|
||||
logger.error(f"代码执行失败,尝试第 {attempt+1} 次修复。错误信息:{error_msg}")
|
||||
pre_code = self.fix_code(pre_code, error_msg)
|
||||
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)
|
||||
logger.error(f"代码执行异常,尝试第 {attempt+1} 次修复。异常信息:{error_msg}")
|
||||
pre_code = self.fix_code(pre_code, error_msg)
|
||||
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": []
|
||||
}
|
||||
|
||||
@@ -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"):
|
||||
@@ -12,6 +17,14 @@ class Config:
|
||||
def openai(self):
|
||||
return self._config.get("openai", {})
|
||||
|
||||
@property
|
||||
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")
|
||||
@@ -27,3 +40,11 @@ class Config:
|
||||
@property
|
||||
def embedding(self):
|
||||
return self._config.get("embedding", {})
|
||||
|
||||
@property
|
||||
def langsmith(self):
|
||||
return self._config.get("langsmith", {})
|
||||
|
||||
@property
|
||||
def max_retries(self):
|
||||
return self._config.get("max_retries", 3)
|
||||
|
||||
+147
-83
@@ -1,10 +1,76 @@
|
||||
# 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):
|
||||
self.llm_client = llm_client
|
||||
self.business_structure = business_structure
|
||||
self.prompts = prompts
|
||||
|
||||
async def convert_question_to_cypher(self, user_input: str) -> str:
|
||||
prompt = self.prompts.cypher_conversion_prompt.format_prompt(
|
||||
business_structure=self.business_structure,
|
||||
user_input=user_input
|
||||
)
|
||||
messages = prompt.to_messages()
|
||||
cypher_query = ""
|
||||
async for chunk in self.llm_client.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
cypher_query += chunk.content
|
||||
print()
|
||||
logger.debug(f"生成的Cypher查询语句:{cypher_query.strip()}")
|
||||
return cypher_query.strip()
|
||||
|
||||
async def rewrite_question_with_query_result(self, user_input: str, context: str) -> str:
|
||||
prompt = self.prompts.rewrite_prompt_template.format_prompt(
|
||||
user_input=user_input,
|
||||
context=context
|
||||
)
|
||||
messages = prompt.to_messages()
|
||||
messages.append(HumanMessage(content=user_input))
|
||||
|
||||
logger.debug(f"重写提示词:{messages}")
|
||||
|
||||
result = ""
|
||||
async for chunk in self.llm_client.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
result += chunk.content
|
||||
print()
|
||||
logger.debug(f"重写后的用户问题:{result.strip()}")
|
||||
return result.strip()
|
||||
|
||||
class InteractionHandler:
|
||||
@staticmethod
|
||||
def display_rewritten_requests(rewritten_results):
|
||||
print("\n系统为您理解并改写了以下访问请求,请选择:")
|
||||
for idx, (rewritten, knowledge) in enumerate(rewritten_results, start=1):
|
||||
print(f"{idx}: {rewritten}")
|
||||
print("0: 重新输入问题")
|
||||
|
||||
@staticmethod
|
||||
def get_user_choice(rewritten_results):
|
||||
while True:
|
||||
choice = input("请输入编号选择(0重新输入,默认1):").strip()
|
||||
if choice == "":
|
||||
choice = "1"
|
||||
if choice == "0":
|
||||
logger.info("用户选择重新输入问题")
|
||||
print("请重新输入您的问题。\n" + "-"*50)
|
||||
return None
|
||||
if choice.isdigit() and 1 <= int(choice) <= len(rewritten_results):
|
||||
return int(choice) - 1
|
||||
else:
|
||||
logger.warning(f"用户输入无效选择:{choice}")
|
||||
print("输入无效,请输入有效编号。")
|
||||
|
||||
class DialogManager:
|
||||
def __init__(
|
||||
@@ -21,7 +87,7 @@ class DialogManager:
|
||||
self.bowei_api_docs = bowei_api_docs
|
||||
self.code_executor = code_executor
|
||||
self.knowledge_retriever = knowledge_retriever
|
||||
self.prompts = prompt_manager.prompts
|
||||
self.question_processor = QuestionProcessor(llm_client, business_structure, prompt_manager.prompts)
|
||||
|
||||
def retrieve_relevant_docs(self, user_input: str):
|
||||
logger.debug(f"开始检索知识库,用户输入:{user_input}")
|
||||
@@ -29,67 +95,39 @@ class DialogManager:
|
||||
logger.debug(f"检索到 {len(docs)} 条相关文档")
|
||||
return [doc.page_content for doc in docs]
|
||||
|
||||
async def convert_question_to_cypher(self, user_input: str) -> str:
|
||||
prompt = self.prompts.cypher_conversion_prompt.format_prompt(
|
||||
business_structure=self.business_structure,
|
||||
user_input=user_input
|
||||
)
|
||||
messages = prompt.to_messages()
|
||||
cypher_query = ""
|
||||
async for chunk in self.llm_client.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
cypher_query += chunk.content
|
||||
print()
|
||||
logger.debug(f"生成的Cypher查询语句:{cypher_query.strip()}")
|
||||
return cypher_query.strip()
|
||||
|
||||
async def rewrite_question_with_query_result(self, user_input: str, query_result: str) -> str:
|
||||
prompt = self.prompts.rewrite_prompt_template.format_prompt(
|
||||
business_structure=self.business_structure,
|
||||
context=query_result
|
||||
)
|
||||
messages = prompt.to_messages()
|
||||
messages.append(HumanMessage(content=user_input))
|
||||
|
||||
result = ""
|
||||
async for chunk in self.llm_client.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
result += chunk.content
|
||||
print()
|
||||
logger.debug(f"重写后的用户问题:{result.strip()}")
|
||||
return result.strip()
|
||||
|
||||
async def understand_user_question_stream(self, user_input: str):
|
||||
logger.info(f"理解用户问题(流式):{user_input}")
|
||||
|
||||
# 1. 转换用户问题为Cypher查询
|
||||
cypher_query = await self.convert_question_to_cypher(user_input)
|
||||
cypher_query = await self.question_processor.convert_question_to_cypher(user_input)
|
||||
|
||||
# 2. 执行Cypher查询,最多返回5条
|
||||
docs = self.knowledge_retriever.get_relevant_documents(cypher_query)
|
||||
docs = docs[:5] # 限制最多5条
|
||||
|
||||
if not docs:
|
||||
logger.info("查询无结果,直接用业务结构改写")
|
||||
prompt = self.prompts.understand_prompt.format_prompt(
|
||||
business_structure=self.business_structure,
|
||||
user_input=user_input
|
||||
)
|
||||
messages = prompt.to_messages()
|
||||
result = ""
|
||||
async for chunk in self.llm_client.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
result += chunk.content
|
||||
print()
|
||||
return [(result.strip(), "")]
|
||||
logger.debug("查询无结果")
|
||||
return []
|
||||
|
||||
rewritten_list = []
|
||||
for idx, doc in enumerate(docs, start=1):
|
||||
print(f"\n第{idx}条相关文档改写结果(流式):")
|
||||
rewritten = await self.rewrite_question_with_query_result(user_input, doc.page_content)
|
||||
logger.debug(f"\n第{idx}条相关文档改写结果(流式):")
|
||||
rewritten = await self.question_processor.rewrite_question_with_query_result(user_input, doc.page_content)
|
||||
rewritten_list.append((rewritten, doc.page_content))
|
||||
return rewritten_list
|
||||
|
||||
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("启动对话管理器,等待用户输入")
|
||||
print("欢迎使用博微造价工程数据访问系统,输入 exit 退出。")
|
||||
@@ -112,41 +150,67 @@ class DialogManager:
|
||||
break
|
||||
|
||||
rewritten_results = await self.understand_user_question_stream(user_question)
|
||||
if rewritten_results is None or rewritten_results == []:
|
||||
print('没有找到符合要求的数据,请继续提问')
|
||||
user_questions.clear()
|
||||
continue
|
||||
|
||||
print("\n系统为您理解并改写了以下访问请求,请选择:")
|
||||
for idx, (rewritten, knowledge) in enumerate(rewritten_results, start=1):
|
||||
print(f"{idx}: {rewritten}")
|
||||
print("0: 重新输入问题")
|
||||
|
||||
while True:
|
||||
choice = input("请输入编号选择(0重新输入,默认1):").strip()
|
||||
if choice == "":
|
||||
choice = "1"
|
||||
if choice == "0":
|
||||
logger.info("用户选择重新输入问题")
|
||||
print("请重新输入您的问题。\n" + "-"*50)
|
||||
user_questions.clear()
|
||||
break
|
||||
if choice.isdigit() and 1 <= int(choice) <= len(rewritten_results):
|
||||
selected_rewritten, selected_knowledge = rewritten_results[int(choice) - 1]
|
||||
logger.info(f"用户选择访问请求编号 {choice},内容:{selected_rewritten}")
|
||||
print(f"\n您选择的访问请求是:\n{selected_rewritten}\n")
|
||||
print(f"相关知识内容:\n{selected_knowledge}\n")
|
||||
confirm = input("请确认是否继续执行该请求(输入n取消,其他继续):").strip().lower()
|
||||
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)
|
||||
break
|
||||
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.warning(f"用户输入无效选择:{choice}")
|
||||
print("输入无效,请输入有效编号。")
|
||||
#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
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
@@ -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):
|
||||
# 同步调用,返回完整响应
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
+77
-36
@@ -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
|
||||
@@ -21,13 +24,13 @@ class PromptManager:
|
||||
def _init_prompts(self) -> CodeExecutorPrompts:
|
||||
understand_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
你是一名电力造价业务专家,请基于以下工程文件业务结构,将用户自然语言问题改写成专业查询语句:
|
||||
你是一名电力造价业务专家,请基于以下示意工程文件业务结构,将用户自然语言问题改写成专业查询语句:
|
||||
|
||||
**工程文件业务结构**:
|
||||
**示意工程文件业务结构**:
|
||||
{business_structure}
|
||||
|
||||
**改写规则**:
|
||||
1. **定位目标对象**:从业务结构中识别核心对象(如 `ProjectDivisionTree`→项目划分树、`FeeScheduleItem`→费用表)。
|
||||
1. **定位目标对象**:仅从示意工程文件业务结构中识别核心对象(如 `ProjectDivisionTree`→项目划分树、`FeeScheduleItem`→费用表)。
|
||||
2. **提取条件**:从用户输入中解析关键条件(如名称、量、类型),用【】标注变量。
|
||||
3. **构建专业语句**:格式为:`在[目标对象]中查找【条件】的项。`
|
||||
- 使用业务术语(如“项目划分项”而非“项目”)。
|
||||
@@ -54,26 +57,32 @@ class PromptManager:
|
||||
|
||||
# 工作流程
|
||||
1. 从用户问题中提取关键信息(节点路径、节点类型、节点名称等)
|
||||
2. 根据"用户问题"和"上下文信息"选择最匹配的"工程数据访问库"中的方法
|
||||
3. 生成可直接执行的Python代码
|
||||
2. 根据"用户问题"和"上下文信息"选择最匹配的"工程数据访问库"中的函数和对象属性
|
||||
3. 生成可直接执行的完全满足用户输入问题要求功能效果的Python函数代码
|
||||
|
||||
# 代码模板(必须严格遵循)
|
||||
def neo4j_find_function():
|
||||
# 输出格式(必须严格遵循)
|
||||
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 neo4j_find_function():
|
||||
project = ProjectBuilder.build()
|
||||
status, data, error, helper_info = project.[SELECTED_METHOD]([PARAMETERS])
|
||||
return status, data, error, helper_info
|
||||
- 参数必须从用户问题或上下文信息中提取。
|
||||
- 为函数内部代码生成流程注释,并使用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’
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -82,13 +91,20 @@ def neo4j_find_function():
|
||||
|
||||
你是一个专业的Python工程师。我会给你一段错误python代码和错误信息,你需要帮我修复这段出错的代码
|
||||
|
||||
已执行代码:
|
||||
{code}
|
||||
|
||||
代码执行报错信息:
|
||||
{error}
|
||||
|
||||
你的任务是:
|
||||
1. 根据需要修改的代码{original_code}和代码的错误信息{error_info}来对代码和参数进行修改
|
||||
1. 根据"已执行代码"和"代码执行报错信息"来对“已执行代码”和函数调用参数进行修改,修复执行错误
|
||||
2. 如果错误信息中是代码的逻辑出现错误,那么就需要对代码本身整体结构进行修改
|
||||
3. 如果是代码中参数出现问题了,那么就需要结合错误信息中的帮助信息(helper_info)来对代码总的参数进行修改
|
||||
4. 修复后的代码应该完整,可以直接执行,并且能够返回查询结果
|
||||
|
||||
注意:
|
||||
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
|
||||
- 必须只输出最终的Python代码,不要添加任何解释、注释、推理过程或自然语言描述。
|
||||
- 不要以“以下是修正后的代码”、“修改如下”等语句开头。
|
||||
- 不要输出任何其他无关的内容。
|
||||
@@ -98,33 +114,58 @@ def neo4j_find_function():
|
||||
- 禁止在代码后加上```字样
|
||||
|
||||
请输出你修补后的代码:
|
||||
"""
|
||||
)
|
||||
|
||||
""")
|
||||
|
||||
rewrite_prompt_template = ChatPromptTemplate.from_template(
|
||||
"""你是一个专业的工程业务助理,结合以下工程业务结构信息和相关知识:
|
||||
{business_structure}
|
||||
"""
|
||||
您是一个AI查询改写助手。基于给定的原始查询和上下文知识,生成一个精确的改写查询。步骤:
|
||||
1. 从上下文知识的`labels`提取对象类型,翻译为中文。
|
||||
2. 从`properties`选择对象标识:优先用`path`值,若无则用`name`值。
|
||||
3. 智能映射原始查询的属性名称:
|
||||
- 如果属性名称是上下文属性的缩写、省略或同义词,映射到实际属性名称(如“人工费”可能映射到“费率”或“合价含税”)。
|
||||
- 如果无法映射,保留原始名称。
|
||||
4. 保留原始查询的额外操作(如计算指令)。
|
||||
5. 输出格式:“获取[对象标识][对象类型]的[属性]属性,[额外操作]”。
|
||||
|
||||
相关知识内容:
|
||||
{context}
|
||||
示例参考:
|
||||
- 输入:原始问题="查找名称中包含“工程”的项目划分项,并返回其人工费乘以1000的值。", 上下文知识=...
|
||||
- 输出="获取[安装/架空输电线路本体工程/基础工程/基础工程材料工地运输]项目划分项的人工费,并乘以1000的值"
|
||||
|
||||
请根据用户的问题,结合上述信息,理解并改写成一个针对工程数据的访问请求(简洁明了的描述)。
|
||||
请只输出改写后的访问请求文本,不要多余解释。"""
|
||||
)
|
||||
现在,处理以下输入:
|
||||
- 原始问题:{user_input}
|
||||
- 上下文知识:{context}
|
||||
""")
|
||||
|
||||
cypher_conversion_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
你是一个Neo4j专家。请将用户的自然语言问题转换成一个有效的Cypher查询语句,查询知识图谱中相关信息。
|
||||
只返回Cypher语句,不要任何解释,最多返回5条。
|
||||
你是一名电力造价业务专家,负责将用户自然语言问题中需要访问的对象识别出来,并生成针对该对象的NEO4J知识图谱的Cypher查询语句,获取该对象的全部信息。知识图谱基于从文件“获取[安装/架空输电线路本体工程]项目划分项的单位.”中读取的层级关联结构构建。该文件包含用反斜杠分割的多个字符串(如“安装/架空输电线路本体工程”),每个字符串表示一个完整的层级路径,路径部分用斜杠分隔,对应于知识图谱中ProjectDivisionItem节点的层级关系。路径映射规则:每个路径部分(如“安装”)是一个ProjectDivisionItem节点,父子关系通过关系类型`:CHILD_OF`连接,形成从根节点到叶节点的层级结构。
|
||||
|
||||
业务结构信息:
|
||||
示例业务结构:
|
||||
{business_structure}
|
||||
|
||||
用户问题:
|
||||
{user_input}
|
||||
Cypher查询语句:
|
||||
"""
|
||||
)
|
||||
|
||||
改写规则:
|
||||
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。
|
||||
识别目标对象:仅从业务结构中识别如下类型:ProjectDivisionItem、ProjectAttributeSet、FeeSchedule、FeeItem的核心对象。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
|
||||
提取查询条件:从用户输入中解析关键条件(如名称、量、类型、值、层级等),条件应基于对象属性(如 name、quantity、type),注意区分查找子串和相等的区别。如果条件涉及层级路径(如路径中包含特定部分),需提取路径部分作为条件(例如,用户提到“架空输电线路”时,解析为路径匹配)。
|
||||
如果用户指定层级(如“叶节点”),需在条件中体现(例如,添加 WHERE item.isLeaf = true)。
|
||||
忽略任何计算、转换或后处理要求(如“乘以1000”),只关注获取原始数据对象或属性。
|
||||
构建Cypher查询:生成一个Cypher查询语句,格式为:
|
||||
|
||||
MATCH 子句:匹配目标对象节点,并应用条件。如果涉及层级路径,使用路径匹配(如 MATCH path = (item1:ProjectDivisionItem)-[:CHILD_OF*]->(item2:ProjectDivisionItem) WHERE item1.name = '安装' AND item2.name = '架空输电线路本体工程')。变量仅用于目标节点和必要路径节点。
|
||||
WHERE 子句:包含提取的条件(使用变量或具体值)。
|
||||
RETURN 子句:必须返回对象(如 RETURN item),不能包含对象属性、函数(如乘法、SUM)。
|
||||
LIMIT 子句: 最多返回5条。
|
||||
使用业务术语在节点标签和属性中(例如,ProjectDivisionItem 而不是“项目”)。
|
||||
查询应简洁,只获取数据,不执行计算。
|
||||
输出格式:直接输出最终Cypher查询语句,不添加解释或额外文本。
|
||||
|
||||
**示例**:
|
||||
用户问题:查找一下名称中包含工程的项目划分
|
||||
Cypher查询语句:MATCH (item:ProjectDivisionItem)\nWHERE item.name CONTAINS '工程'\nRETURN item\nLIMIT 5
|
||||
""")
|
||||
|
||||
return CodeExecutorPrompts(
|
||||
understand_prompt=understand_prompt,
|
||||
|
||||
@@ -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())
|
||||
@@ -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"}
|
||||
+122
@@ -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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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": "合计费"}}]}
|
||||
@@ -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
@@ -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')
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
@@ -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("转换完毕!")
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user