Compare commits
2 Commits
7e7109e01b
...
be98bb1495
| Author | SHA1 | Date | |
|---|---|---|---|
| be98bb1495 | |||
| af64d962eb |
+4
-6
@@ -1,7 +1,5 @@
|
||||
BoweiAgent.log
|
||||
*.log
|
||||
__pycache__/*
|
||||
.vscode/launch.json
|
||||
src/__pycache__/*
|
||||
.cursor/rules/use.mdc
|
||||
test_runcode.log
|
||||
BoweiAgent1.log
|
||||
.vscode/*
|
||||
.cursor/*
|
||||
logs/*
|
||||
|
||||
+25
@@ -8,8 +8,21 @@ neo4j:
|
||||
|
||||
business_object_structure_path: ./data/business_object_structure.md
|
||||
bowei_api_docs_path: ./data/bowei_api_docs.md
|
||||
max_retries: 0
|
||||
|
||||
openai:
|
||||
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版本需要指定
|
||||
@@ -20,6 +33,18 @@ openai:
|
||||
#model_name: deepseek-ai/DeepSeek-V3
|
||||
|
||||
openai_coder:
|
||||
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版本需要指定
|
||||
|
||||
@@ -17,6 +17,7 @@ class ProjectToolkit(ABC):
|
||||
self.project_division_set = ProjectDivisionItem() # 项目划分集对象
|
||||
|
||||
# 项目划分查询方法
|
||||
|
||||
@abstractmethod
|
||||
def get_division_by_name(self, name):
|
||||
"""
|
||||
@@ -37,7 +38,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 项目划分查询方法
|
||||
@abstractmethod
|
||||
def get_division_item_by_path(self, path):
|
||||
"""
|
||||
@@ -58,7 +58,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 项目划分查询方法
|
||||
@abstractmethod
|
||||
def get_division_node_by_parent_and_name(self, parent_path, partial_name):
|
||||
"""
|
||||
@@ -95,7 +94,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 工程量查询方法
|
||||
@abstractmethod
|
||||
def get_quantities_node_by_parent_and_code(self, parent_path, quantity_type=None, code=None):
|
||||
"""
|
||||
@@ -115,7 +113,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 工程量查询方法
|
||||
@abstractmethod
|
||||
def get_quantities_node_by_parent_and_name(self, parent_path, partial_name, quantity_type=None):
|
||||
"""
|
||||
@@ -155,13 +152,13 @@ class ProjectToolkit(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_material_equipment_by_parent_and_code(self, parent_path, code):
|
||||
def get_material_equipment_by_parent_and_name(self, parent_path, partial_name):
|
||||
"""
|
||||
通过父节点路径和模糊名称获取材机对象
|
||||
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
code (str): 目标节点编码
|
||||
partial_name (str): 目标节点的模糊或不完整名称
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
@@ -192,7 +189,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 取费查询方法
|
||||
@abstractmethod
|
||||
def get_fee_template_by_parent_and_name(self, parent_path, partial_name):
|
||||
"""
|
||||
@@ -200,7 +196,7 @@ class ProjectToolkit(ABC):
|
||||
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
node_name (str): 目标节点的名称
|
||||
node_name (str): 目标节点的名称,例如:直接费
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
@@ -238,7 +234,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 费用表查询方法
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_other_expense_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
@@ -262,7 +257,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 费用表查询方法
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_land_acquisition_fee_table_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
@@ -286,7 +280,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 费用表查询方法
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_installation_price_difference_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
@@ -310,11 +303,10 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 费用表查询方法
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_Engineering_Cost_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
在工程费用表中查找费用值
|
||||
在工程费用表中查找费用
|
||||
|
||||
Args:
|
||||
table_name (str): 费用表名称
|
||||
@@ -334,147 +326,6 @@ class ProjectToolkit(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
# 工程属性查询方法
|
||||
@abstractmethod
|
||||
def get_project_property(self, property_name):
|
||||
"""
|
||||
通过属性名称获取工程属性
|
||||
|
||||
Args:
|
||||
property_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]): 辅助信息列表
|
||||
"""
|
||||
pass
|
||||
|
||||
# 项目划分节点下查找费用预览中的费用
|
||||
@abstractmethod
|
||||
def get_fee_table_by_project_division(self, project_division_path, fee_name):
|
||||
"""
|
||||
通过项目划分名称和费用名称在费用预览中查找费用
|
||||
|
||||
Args:
|
||||
project_division_path (str): 项目划分节点路径
|
||||
fee_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]): 辅助信息列表
|
||||
"""
|
||||
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:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (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:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (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:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (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:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (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:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ProjectDivisionSet:
|
||||
"""
|
||||
@@ -544,28 +395,6 @@ class ProjectQuantity:
|
||||
self.cost_set = None # xsd:CostSet
|
||||
|
||||
|
||||
class CostItem:
|
||||
"""
|
||||
费用预览集
|
||||
描述: 代表一个费用预览集,通常包含多个费用预览项
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.GUID = None # xsd:string
|
||||
self.name = None # xsd:string
|
||||
|
||||
|
||||
class CostSet:
|
||||
"""
|
||||
费用预览项
|
||||
描述: 代表一个费用预览
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.cost = None # xsd:string
|
||||
self.name = None # xsd:string
|
||||
|
||||
|
||||
class Ration(ProjectQuantity):
|
||||
"""
|
||||
定额
|
||||
@@ -748,42 +577,3 @@ 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)
|
||||
+235
-123
@@ -24,17 +24,14 @@ class ProjectToolkit(ABC):
|
||||
通过名称获取项目划分对象
|
||||
|
||||
Args:
|
||||
path (str): 项目划分节点的名称
|
||||
name_part (str): 项目划分节点名称
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的项目划分对象数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表,如果未找到节点,则包含父节点下所有ProjectDivisionItem节点
|
||||
|
||||
Note:
|
||||
当路径为空字符串时,会返回None
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -47,14 +44,11 @@ class ProjectToolkit(ABC):
|
||||
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的项目划分对象数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表,如果未找到节点,则包含父节点下所有ProjectDivisionItem节点
|
||||
|
||||
Note:
|
||||
当路径为空字符串时,会返回None
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Dict[str, Any]): 成功时返回的项目划分对象数据,失败时为空字典
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -68,11 +62,11 @@ class ProjectToolkit(ABC):
|
||||
partial_name (str): 目标节点的模糊或不完整名称
|
||||
|
||||
Returns:
|
||||
Tuple[str, List[Dict[str, Any]], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (List[Dict[str, Any]]): 成功时返回的数据列表
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (List[Dict[str, Any]]): 成功时返回的数据列表,失败时为空列表
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -86,11 +80,11 @@ class ProjectToolkit(ABC):
|
||||
paths_str (str): 以'/'分隔的多级节点路径
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]:
|
||||
- status: 'success'或'error'
|
||||
- data: 成功时返回的节点数据字典,失败时为{}
|
||||
- error: 错误信息,成功时为''
|
||||
- helper_info: 辅助信息列表(当目标节点不存在时返回父节点下的所有ProjectQuantity节点名称)
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Dict[str, Any]): 成功时返回的节点数据字典,失败时为空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -105,11 +99,11 @@ class ProjectToolkit(ABC):
|
||||
code (str): 工程量编码,以'/'分隔的多个编码
|
||||
|
||||
Returns:
|
||||
Tuple[str, List[Dict[str, Any]], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (List[Dict[str, Any]]): 成功时返回的数据列表
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[List[Dict[str, Any]], Dict]): 成功时返回的数据列表或字典,失败时为空列表 []
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -121,14 +115,14 @@ class ProjectToolkit(ABC):
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
partial_name (str): 目标节点的模糊或不完整名称
|
||||
quantity_type (str): 工程量类型('定额'、'主材'、'设备'或None表示所有类型)
|
||||
quantity_type (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
|
||||
|
||||
@@ -142,31 +136,29 @@ class ProjectToolkit(ABC):
|
||||
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的节点属性数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 如果未找到目标节点,则包含父节点下所有 MaterialOrEquipment 节点名称
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_material_equipment_by_parent_and_name(self, parent_path, partial_name):
|
||||
def get_material_equipment_by_parent_and_code(self, parent_path, code):
|
||||
"""
|
||||
通过父节点路径和模糊名称获取材机对象
|
||||
通过父节点路径和编码获取材机对象
|
||||
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
partial_name (str): 目标节点的模糊或不完整名称
|
||||
parent_path (str): 父节点的部分路径,以'/'分隔的多级节点路径
|
||||
code (str): 目标节点的编码属性值
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的节点属性数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 如果未找到目标节点,则包含父节点下所有 MaterialOrEquipment 节点名称
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -174,39 +166,37 @@ class ProjectToolkit(ABC):
|
||||
@abstractmethod
|
||||
def get_fee_template_by_path(self, paths_str):
|
||||
"""
|
||||
通过路径获取取费节点
|
||||
通过路径获取取费表模板节点
|
||||
|
||||
Args:
|
||||
path (str): 项目划分节点的路径,以'/'分隔的多级节点路径
|
||||
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)/直接费
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的节点属性数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 如果未找到目标节点,则包含父节点下所有 FeeCollection 节点名称
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_fee_template_by_parent_and_name(self, parent_path, partial_name):
|
||||
"""
|
||||
通过父节点路径和节点名称获取取费节点
|
||||
通过父节点路径和节点名称获取取费表模板节点
|
||||
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
例如:工程数据/取费表模板/余物清理/线路取费表(余物清理)
|
||||
node_name (str): 目标节点的名称,例如:直接费
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回的节点属性数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 如果未找到父节点,则包含父节点的父节点下所有节点名称
|
||||
- 如果未找到目标节点,则包含父节点下所有 FeeCollection 节点名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List]): 成功时返回的节点属性数据字典,失败时为空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -222,15 +212,11 @@ class ProjectToolkit(ABC):
|
||||
fee_attribute (str): 费用值属性名
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 第一步失败:返回"工程数据"节点下所有子节点名称
|
||||
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
|
||||
- 第三步失败:返回费用节点的所有可用属性名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -245,22 +231,18 @@ class ProjectToolkit(ABC):
|
||||
fee_attribute (str): 费用值属性名
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 第一步失败:返回"工程数据"节点下所有子节点名称
|
||||
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
|
||||
- 第三步失败:返回费用节点的所有可用属性名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_land_acquisition_fee_table_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
在其中:土地征用费表中查找费用
|
||||
在其中:场地征用费用表中查找费用
|
||||
|
||||
Args:
|
||||
table_name (str): 费用表名称
|
||||
@@ -268,22 +250,18 @@ class ProjectToolkit(ABC):
|
||||
fee_attribute (str): 费用值属性名
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 第一步失败:返回"工程数据"节点下所有子节点名称
|
||||
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
|
||||
- 第三步失败:返回费用节点的所有可用属性名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_fee_schedule_on_installation_price_difference_table(self, table_name, fee_name, fee):
|
||||
"""
|
||||
在安装价差表中查找费用
|
||||
在安装价差费用表中查找费用
|
||||
|
||||
Args:
|
||||
table_name (str): 费用表名称
|
||||
@@ -291,15 +269,11 @@ class ProjectToolkit(ABC):
|
||||
fee_attribute (str): 费用值属性名
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 第一步失败:返回"工程数据"节点下所有子节点名称
|
||||
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
|
||||
- 第三步失败:返回费用节点的所有可用属性名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -314,15 +288,151 @@ class ProjectToolkit(ABC):
|
||||
fee_attribute (str): 费用值属性名
|
||||
|
||||
Returns:
|
||||
Tuple[str, Dict[str, Any], str, List[Any]]: 包含状态、数据、错误信息和辅助信息的元组
|
||||
- status (str): 状态,'success'或'error'
|
||||
- data (Dict[str, Any]): 成功时返回包含属性值的数据
|
||||
- error (str): 错误时的错误信息
|
||||
- helper_info (List[Any]): 辅助信息列表:
|
||||
- 第一步失败:返回"工程数据"节点下所有子节点名称
|
||||
- 第二步失败:返回费用表节点下所有子节点名称(递归查找)
|
||||
- 第三步失败:返回费用节点的所有可用属性名称
|
||||
- 否则为空
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性值(可能是 str/int/float 等),失败时返回 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 工程属性查询方法
|
||||
@abstractmethod
|
||||
def get_project_property(self, property_name):
|
||||
"""
|
||||
通过属性名称获取工程属性
|
||||
|
||||
Args:
|
||||
property_name (str): 属性名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 项目划分查找取费表
|
||||
@abstractmethod
|
||||
def get_fee_table_by_project_division(self, project_division_path, fee_name):
|
||||
"""
|
||||
通过项目划分路径和取费名称查找费用
|
||||
|
||||
Args:
|
||||
project_division_path (str): 项目划分节点的路径
|
||||
fee_name (str): 取费名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 清单查找取费表
|
||||
@abstractmethod
|
||||
def get_fee_table_by_list(self, parent_path, list_code, list_unit, fee_name):
|
||||
"""
|
||||
通过清单名称和取费名称查找费用
|
||||
|
||||
Args:
|
||||
parent_path (str): 父级路径
|
||||
list_code (str): 清单编号
|
||||
list_unit (str): 清单单位
|
||||
fee_name (str): 取费名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 定额节点查找合价
|
||||
@abstractmethod
|
||||
def get_fee_table_by_quoto_code(self, parent_path, quantity_type, code, fee_name):
|
||||
"""
|
||||
通过父级路径、工程量类型、定额编码和取费名称查找费用
|
||||
|
||||
Args:
|
||||
parent_path (str): 父级路径
|
||||
quantity_type (str): 工程量类型
|
||||
code (str): 定额编码
|
||||
fee_name (str): 取费名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 工程量节点查找合价
|
||||
@abstractmethod
|
||||
def get_fee_table_by_quantities_name(self, parent_path, quantity_type, quantity_name, fee_name):
|
||||
"""
|
||||
通过父级路径、工程量类型、工程量名称和取费名称查找费用
|
||||
|
||||
Args:
|
||||
parent_path (str): 父级路径
|
||||
quantity_type (str): 工程量类型
|
||||
quantity_name (str): 工程量名称
|
||||
fee_name (str): 取费名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 材机节点查找市场价
|
||||
@abstractmethod
|
||||
def get_fee_by_material_equipment_code(self, parent_path, material_equipment_code, fee_name):
|
||||
"""
|
||||
通过父级路径、材机编码和取费名称查找费用
|
||||
|
||||
Args:
|
||||
parent_path (str): 父级路径
|
||||
material_equipment_code (str): 材机编码
|
||||
fee_name (str): 取费名称
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
# 查找清单节点
|
||||
@abstractmethod
|
||||
def get_fee_table_by_list_name(self, parent_path, list_code, unit):
|
||||
"""
|
||||
通过父级路径、清单编号和单位查找清单
|
||||
|
||||
Args:
|
||||
parent_path (str): 父级路径
|
||||
list_code (str): 清单编号
|
||||
unit (str): 单位
|
||||
|
||||
Returns:
|
||||
dict: 返回字典,字段包括:
|
||||
- code (int): 状态码,固定为 200(成功)或 201(失败)
|
||||
- message (str): 成功时为 "Ok",失败时包含错误信息和辅助信息
|
||||
- status (bool): 成功为 True,失败为 False
|
||||
- data (Union[Dict, List, Any]): 成功时返回属性数据字典,失败时返回空字典 {}
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -583,34 +693,36 @@ class ProjectBuilder:
|
||||
_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,59 +1,70 @@
|
||||
EngineeringData(工程)
|
||||
│
|
||||
├── ProjectPropertySet(工程属性集)
|
||||
│ └── ProjectProperty(项目编码、电压等级、主变规模)
|
||||
│ ├── ProjectProperty(工程属性)[name:项目划分, value: 架空输电线路工程]
|
||||
│ ├── ProjectProperty(工程属性)[name:电压等级, value: 220]
|
||||
│ └── ProjectProperty(工程属性)[name:专业类型, value: 送电]
|
||||
│
|
||||
├── ProjectDivisionSet(项目划分集)
|
||||
│ │
|
||||
│ ├── ProjectDivisionTree(安装)
|
||||
│ │ ├── ProjectDivisionItem(架空输电线路本体工程)[父节点]
|
||||
│ │ │ ├── ProjectDivisionItem(基础工程)[叶节点]
|
||||
│ │ │ │ ├── ProjectQuantity(现浇混凝土)[类型:Quota, 量:1500m³, 定额:GD5-2023-025]
|
||||
│ │ │ │ └── ProjectQuantity(钢筋)[类型:MainMaterial, 量:950t, 型号:HRB400]
|
||||
│ │ │ │
|
||||
│ │ │ └── ProjectDivisionItem(GIS室)[叶节点]
|
||||
│ ├── ProjectDivisionTree(项目划分树)[name: 安装]
|
||||
│ │ ├── ProjectDivisionItem(项目划分)[name: 架空输电线路本体工程]
|
||||
│ │ │ ├── ProjectDivisionItem(项目划分)[name: 基础工程]
|
||||
│ │ │ │ ├──ProjectQuantity(工程量)[name: 汽车运输 金具、绝缘子、零星钢材 装卸, 类型: 定额, 数量: 84.486092, 单位: t, 编码: YX1-97]
|
||||
│ │ │ │ │ └── CostSet(费用预览集)
|
||||
│ │ │ │ │ ├──CostItem(费用项)[name: 直接工程费_FFZ1, cost: 354010.98]
|
||||
│ │ │ │ │ ├──CostItem(费用项)[name: 编制年价差_机械价差_JJC, cost: 21869.0211]
|
||||
│ │ │ │ │ └──CostItem(费用项)[name: 社会保险费_BZHF, cost: 16758.8798]
|
||||
│ │ │ │ ├── ProjectQuantity(工程量)[name: 商品混凝土(立柱), 类型: 主材, 数量: 39.944, 单位: m³, 编码: SP1004]
|
||||
│ │ │ │ └── CostSet(费用预览集)
|
||||
│ │ │ │ ├──CostItem(费用项)[name: 直接工程费_FFZ1, cost: 6843369.393079]
|
||||
│ │ │ │ ├──CostItem(费用项)[name: 编制年价差_机械价差_JJC, cost: 40402.974077]
|
||||
│ │ │ │ └──CostItem(费用项)[name: 社会保险费_BZHF, cost: 379304.02941]
|
||||
│ │ │ │
|
||||
│ │ │ └── ProjectDivisionItem(项目划分)[name: GIS室]
|
||||
│ │ │
|
||||
│ │ └── ProjectDivisionItem(辅助设施工程)[父节点]
|
||||
│ │ ├── ProjectDivisionItem(事故油池)[叶节点]
|
||||
│ │ └── ProjectDivisionItem(防火墙)[叶节点]
|
||||
│ │ └── ProjectDivisionItem(项目划分)[name: 辅助设施工程]
|
||||
│ │ ├── ProjectDivisionItem(项目划分)[name: 事故油池]
|
||||
│ │ └── ProjectDivisionItem(项目划分)[name: 施工道路]
|
||||
│ │
|
||||
│ └── ProjectDivisionTree(清理项目安装)
|
||||
│ ├── ProjectDivisionItem(主变压器系统)[父节点]
|
||||
│ │ ├── ProjectDivisionItem(本体安装)[叶节点]
|
||||
│ │ │ └── ProjectQuantity(主变压器)[类型:Equipment, 量:4台, 型号:SZ11-1000MVA]
|
||||
│ └── ProjectDivisionTree(项目划分树)[name: 清理项目安装]
|
||||
│ ├── ProjectDivisionItem(项目划分)[name: 安装过程]
|
||||
│ │ ├── ProjectDivisionItem(项目划分)[name: 本体安装]
|
||||
│ │ │ └── ProjectQuantity(工程量)[name:锅炉(烟煤), 类型: 设备, 数量:4, 单位: 台]
|
||||
│ │ │
|
||||
│ │ └── ProjectDivisionItem(冷却系统)[叶节点]
|
||||
│ │ └── ProjectDivisionItem(项目划分)[name: 冷却系统]
|
||||
│ │
|
||||
│ └── ProjectDivisionItem(配电装置)[父节点]
|
||||
│ ├── ProjectDivisionItem(500kV GIS)[叶节点]
|
||||
│ │ └── ProjectQuantity(GIS间隔)[类型:Equipment, 量:8间隔, 单价:580万]
|
||||
│ └── ProjectDivisionItem(项目划分)[name: 建筑工程]
|
||||
│ ├── ProjectDivisionItem(项目划分)[name: 辅助工程]
|
||||
│ │ └── ProjectQuantity(工程量)[name:柴油发电机, 类型: 设备, 数量:1, 单位: 台]
|
||||
│ │
|
||||
│ └── ProjectDivisionItem(35kV开关柜)[叶节点]
|
||||
│ └── ProjectDivisionItem(项目划分)[name: 基础永久性围堰]
|
||||
│
|
||||
├── FeeScheduleSet(费用表集)
|
||||
│ │
|
||||
│ ├── FeeScheduleItem(工程费用表)
|
||||
│ │ ├── Fee(直接费)[值:32500万元]
|
||||
│ │ │ └── Fee(机械费)[值:7850万元]
|
||||
│ ├── FeeScheduleItem(费用表项)[name: 工程费用表]
|
||||
│ │ ├── Fee(费用)[name: 架空输电线路本体工程, code: BTGC, 安装费(AZF): 11155128.078755]
|
||||
│ │ │ └── Fee(费用)[name: 一般线路本体工程, code: XLBT, 安装费(AZF): 11155128.078755]
|
||||
│ │ │
|
||||
│ │ └── Fee(措施费)[值:4875万元]
|
||||
│ │ ├── Fee(安全文明施工费)[费率:3.5%, 值:2800万元]
|
||||
│ │ └── Fee(夜间施工增加费)[费率:1.2%, 值:2075万元]
|
||||
│ │ └── Fee(费用)[name: 动态费用, code: DTFY, 安装费(AZF): 0]
|
||||
│ │ ├── Fee(费用)[name: 建设期贷款利息, code: DKLX, 安装费(AZF): 0]
|
||||
│ │ └── Fee(费用)[name: 价差预备费, code: JCYBF, 安装费(AZF): 0]
|
||||
│ │
|
||||
│ └── FeeScheduleItem(其他费用表)
|
||||
│ ├── Fee(企业管理费)[费率:5.8%, 值:1885万元]
|
||||
│ └── Fee(税金)[费率:9%, 值:2925万元]
|
||||
│ └── FeeScheduleItem(费用表项)[name: 其他费用表]
|
||||
│ ├── Fee(费用)[name: 工程保险费, code: B6, rate: 100, amount: 0]
|
||||
│ └── Fee(费用)[name: 招标费, code: B2, rate: 0.28, amount: 31234.358621]
|
||||
│
|
||||
├── FeeTableTemplateSet(取费表模板集)
|
||||
│
|
||||
├── FeeTableTemplateItem(建筑取费表)
|
||||
│ ├── FeeCollection(直接费)
|
||||
│ │ └── FeeCollection(人工费调整)[系数:1.25]
|
||||
├── FeeTableTemplateItem(取费表模板项)[name: 线路取费表]
|
||||
│ ├── FeeCollection(取费) [name: 设备费, code: SBF, rate: 100]
|
||||
│ │ └── FeeCollection(取费)[name: 乙供设备不含税价, code: SBY, base: 乙供设备费不含税+乙供设备税金, rate: 100]
|
||||
│ │
|
||||
│ └── FeeCollection(措施费)
|
||||
│ ├── FeeCollection(安全文明施工费)[费率:3.5%]
|
||||
│ └── FeeCollection(夜间施工增加费)[费率:1.2%]
|
||||
│ └── FeeCollection(取费)[name:直接费, code: FFZ, rate: 100]
|
||||
│ └── FeeCollection(取费)[name: 直接工程费, code: FFZ1]
|
||||
│ ├── FeeCollection(取费)[name: 人工费, code: RGF, base: 取费定额人工费, rate: 100]
|
||||
│ └── FeeCollection(取费)[name: 材料费, code: CLF, base: 取费定额乙供材料费不含税, rate: 100]
|
||||
│
|
||||
└── FeeTableTemplateItem(安装取费表)
|
||||
├── FeeCollection(利润)[费率:7%]
|
||||
└── FeeCollection(税金)[费率:9%]
|
||||
└── FeeTableTemplateItem(取费表模板项) [name: 大型土石方取费表]
|
||||
├── FeeCollection(取费)[name: 税金, code: TFFS, rate: 9]
|
||||
└── FeeCollection(取费)[name: 合计, code: THJ, rate: 100]
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,63 +1,53 @@
|
||||
# main.py
|
||||
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
current_file = os.path.splitext(os.path.basename(__file__))[0]
|
||||
now_str = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
log_filename = f"{current_file}_{now_str}.log"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG, # 生产环境可改为 INFO 或 WARNING
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.FileHandler("BoweiAgent.log", encoding="utf-8"), logging.StreamHandler()],
|
||||
handlers=[
|
||||
logging.FileHandler(os.path.join("logs", log_filename), encoding="utf-8"),
|
||||
logging.StreamHandler()
|
||||
],
|
||||
)
|
||||
|
||||
logger = logging.getLogger("BoweiAgent")
|
||||
logger = logging.getLogger(current_file)
|
||||
|
||||
import logging
|
||||
def setup_logger(logger_name):
|
||||
"""
|
||||
设置指定名称的logger,将其级别设置为WARNING并禁用传播
|
||||
:param logger_name: logger的名称
|
||||
"""
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
logger.propagate = False # 可选:禁用传播(防止被根logger处理)
|
||||
return logger
|
||||
|
||||
# 获取logger并设置级别
|
||||
httpx_logger = logging.getLogger("httpx")
|
||||
httpx_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
httpx_logger.propagate = False
|
||||
|
||||
# 获取logger并设置级别
|
||||
openai_logger = logging.getLogger("openai")
|
||||
openai_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
openai_logger.propagate = False
|
||||
|
||||
# 获取logger并设置级别
|
||||
langsmith_logger = logging.getLogger("langsmith.client")
|
||||
langsmith_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
langsmith_logger.propagate = False
|
||||
|
||||
# 获取logger并设置级别
|
||||
neo4j_logger = logging.getLogger("neo4j")
|
||||
neo4j_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
neo4j_logger.propagate = False
|
||||
|
||||
# 获取logger并设置级别
|
||||
urllib3_logger = logging.getLogger("urllib3")
|
||||
urllib3_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
urllib3_logger.propagate = False
|
||||
|
||||
# 获取logger并设置级别
|
||||
httpcore_logger = logging.getLogger("httpcore")
|
||||
httpcore_logger.setLevel(logging.WARNING) # 设置httpcore及其子模块的级别
|
||||
# 可选:禁用传播(防止被根logger处理)
|
||||
httpcore_logger.propagate = False
|
||||
logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "httpcore"]
|
||||
for name in logger_names:
|
||||
setup_logger(name)
|
||||
|
||||
from src.config import Config
|
||||
from src.document_loader import load_file
|
||||
from src.llm_client import LLMClient
|
||||
from src.multi_llm_client import MultiAPIKeyChatOpenAI
|
||||
from src.prompt_manager import PromptManager
|
||||
from src.code_executor import CodeExecutor
|
||||
from src.dialog_manager import DialogManager
|
||||
from src.neo4j_raw_retriever import Neo4jRawRetriever
|
||||
from src.embedding_client import EmbeddingClient
|
||||
from project import ProjectBuilder, ProjectToolkit
|
||||
from project_implementation import ProjectToolkitNeo4j
|
||||
|
||||
from src.project import ProjectBuilder, ProjectToolkit
|
||||
from src.project_implementation import ProjectToolkitNeo4j
|
||||
|
||||
|
||||
|
||||
@@ -69,14 +59,11 @@ def main():
|
||||
os.environ["LANGSMITH_TRACING"] = "true" if tracing_enabled else "false"
|
||||
os.environ["LANGSMITH_API_KEY"] = config.langsmith.get("api_key")
|
||||
#os.environ["LANGSMITH_API_KEY"] = config.langsmith.get("api_url")
|
||||
|
||||
business_structure = load_file(config.business_object_structure_path)
|
||||
bowei_api_docs = load_file(config.bowei_api_docs_path)
|
||||
|
||||
llm_client = LLMClient(config.openai)
|
||||
|
||||
llm_client_coder = LLMClient(config.openai_coder)
|
||||
|
||||
llm_client = MultiAPIKeyChatOpenAI(config.openai)
|
||||
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
|
||||
prompt_manager = PromptManager()
|
||||
|
||||
neo4j_conf = config.neo4j_conf
|
||||
@@ -85,12 +72,10 @@ def main():
|
||||
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(
|
||||
@@ -102,10 +87,14 @@ def main():
|
||||
prompt_manager,
|
||||
)
|
||||
|
||||
pre_input_question = "查找名称中包含“工程”的项目划分项,并返回其人工费乘以1000的值。"
|
||||
pre_input_question = "查找名称中包含“工程”的项目划分项,并返回单位。"
|
||||
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回其人工费乘以1000的值。'
|
||||
pre_input_question = '查找名称中包含"工程"的项目划分项,并返回单位。'
|
||||
|
||||
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
|
||||
try:
|
||||
asyncio.run(dialog_manager.run_async(pre_input=pre_input_question))
|
||||
finally:
|
||||
|
||||
neo4j_driver.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -3,4 +3,6 @@ langchain-core
|
||||
langchain-experimental
|
||||
pyyaml
|
||||
neo4j
|
||||
langchain-neo4j
|
||||
langchain-neo4j
|
||||
langgraph
|
||||
chardet
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+114
-46
@@ -1,21 +1,39 @@
|
||||
# src/code_executor.py
|
||||
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
from langchain_experimental.utilities import PythonREPL
|
||||
from langchain_core.tools import Tool
|
||||
from langchain_experimental.tools import PythonREPLTool
|
||||
from project import ProjectBuilder, ProjectToolkit
|
||||
from src.project import ProjectBuilder, ProjectToolkit
|
||||
import sys
|
||||
import io
|
||||
import traceback
|
||||
|
||||
logger = logging.getLogger("BoweiAgent.CodeExecutor")
|
||||
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)
|
||||
|
||||
|
||||
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:
|
||||
@@ -27,7 +45,13 @@ class CodeExecutor:
|
||||
response = self.llm_client.invoke(prompt.to_messages())
|
||||
code = self.output_parser.parse(response)
|
||||
logger.debug(f"生成的代码内容:\n{code}")
|
||||
return code
|
||||
|
||||
return {
|
||||
"code": 20000,
|
||||
"message": 'ok',
|
||||
"status": True,
|
||||
"data": code.content
|
||||
}
|
||||
|
||||
def fix_code(self, code: str, error: str) -> str:
|
||||
logger.warning(f"代码执行出错,开始修复。错误信息:{error}")
|
||||
@@ -35,11 +59,19 @@ class CodeExecutor:
|
||||
response = self.llm_client.invoke(prompt.to_messages())
|
||||
fixed_code = self.output_parser.parse(response)
|
||||
logger.debug(f"修复后的代码内容:\n{fixed_code}")
|
||||
return fixed_code
|
||||
|
||||
def execute_code(self, code_str):
|
||||
return {
|
||||
"code": 20000,
|
||||
"message": 'ok',
|
||||
"status": True,
|
||||
"data": fixed_code.content
|
||||
}
|
||||
|
||||
def execute_code(self, code_str) -> dict:
|
||||
"""封装代码执行逻辑"""
|
||||
logger.debug(f"开始执行代码: {code_str}")
|
||||
logger.debug(f"开始执行代码:\n {code_str}")
|
||||
|
||||
old_stdout = None
|
||||
try:
|
||||
namespace = {
|
||||
"project": __import__("project"),
|
||||
@@ -56,65 +88,101 @@ class CodeExecutor:
|
||||
if "project_get_calculate_function" not in namespace:
|
||||
raise ValueError("代码中未定义project_get_calculate_function函数")
|
||||
|
||||
result_tuple = namespace["project_get_calculate_function"]()
|
||||
result_dict = namespace["project_get_calculate_function"]()
|
||||
|
||||
sys.stdout = old_stdout
|
||||
output = redirected_output.getvalue().strip()
|
||||
|
||||
if not isinstance(result_tuple, tuple) or len(result_tuple) != 4:
|
||||
raise ValueError("函数应返回包含4个元素的元组(status, data, error, helper_info)")
|
||||
if not isinstance(result_dict, dict) or len(result_dict) != 4:
|
||||
raise ValueError("函数应返回包含4个元素的字典(status, data, error, helper_info)")
|
||||
|
||||
status, data, error, helper_info = result_tuple
|
||||
|
||||
logger.info(f"执行结果: status={status}, data={data}, error={error}")
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"data": data,
|
||||
"error": error,
|
||||
"helper_info": helper_info,
|
||||
"output": output,
|
||||
}
|
||||
logger.debug(f"执行结果: {result_dict}")
|
||||
|
||||
return result_dict
|
||||
except Exception as e:
|
||||
# 确保恢复stdout
|
||||
sys.stdout = old_stdout
|
||||
logger.error(f"执行代码时出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
return {
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
"helper_info": [],
|
||||
"traceback": traceback.format_exc(),
|
||||
"code": 50000,
|
||||
"message": str(e),
|
||||
"status": False,
|
||||
"data": []
|
||||
}
|
||||
|
||||
def generate_and_run_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> str:
|
||||
code = self.generate_code(user_request, context, bowei_api_docs)
|
||||
logger.info("开始执行生成的代码")
|
||||
|
||||
def generate_and_run_code(self, user_request: str, context: str = "", bowei_api_docs: str = "") -> dict:
|
||||
code = ""
|
||||
pre_code = ""
|
||||
error_msg = ""
|
||||
prev_happend_error = False
|
||||
|
||||
result = self.generate_code(user_request, context, bowei_api_docs)
|
||||
if isinstance(result, dict) and result.get('status', False):
|
||||
code = result['data']
|
||||
elif isinstance(result, dict):
|
||||
error_msg = result.get("message", "返回结果中没有错误信息")
|
||||
logger.error(error_msg)
|
||||
return {
|
||||
"code": 50000,
|
||||
"message": error_msg,
|
||||
"status": False,
|
||||
"data": code
|
||||
}
|
||||
else:
|
||||
error_msg = "返回结果格式未知"
|
||||
logger.error(error_msg)
|
||||
return {
|
||||
"code": 50000,
|
||||
"message": error_msg,
|
||||
"status": False,
|
||||
"data": code
|
||||
}
|
||||
|
||||
logger.info("开始执行生成的代码")
|
||||
for attempt in range(self.max_retries):
|
||||
try:
|
||||
if prev_happend_error:
|
||||
logger.error(f"代码执行失败,尝试第 {attempt+1} 次修复。错误信息:{error_msg}")
|
||||
code = self.fix_code(pre_code, error_msg)
|
||||
|
||||
import re
|
||||
|
||||
pre_code = re.sub(r"^```python\s*|\s*```$", "", code.content, flags=re.MULTILINE)
|
||||
result = self.execute_code(pre_code)
|
||||
if result["status"] == "success":
|
||||
logger.info(f"代码执行成功,结果: {result['data']}")
|
||||
return result["data"]
|
||||
else:
|
||||
error_msg = result.get("error", "未知错误")
|
||||
prev_happend_error = True
|
||||
result = self.fix_code(pre_code, error_msg)
|
||||
if isinstance(result, dict) and result.get('status', False):
|
||||
code = result['data']
|
||||
elif isinstance(result, dict):
|
||||
error_msg = result.get("message", "返回结果中没有错误信息")
|
||||
raise ValueError(error_msg)
|
||||
else:
|
||||
error_msg = "返回结果格式未知"
|
||||
raise ValueError(error_msg)
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
error_msg = f"生成修复代码时发生异常: {str(e)}"
|
||||
prev_happend_error = True
|
||||
|
||||
logger.error(f"代码执行失败,超过最大重试次数 {self.max_retries}")
|
||||
return f"代码执行失败,超过最大重试次数 {self.max_retries}。\n最后一次错误信息:\n{error_msg}"
|
||||
try:
|
||||
import re
|
||||
pre_code = re.sub(r"^```python\s*|\s*```$", "", code, flags=re.MULTILINE)
|
||||
except Exception as e:
|
||||
error_msg = f"解析生成代码格式时发生异常: {str(e)}"
|
||||
prev_happend_error = True
|
||||
|
||||
try:
|
||||
result = self.execute_code(pre_code)
|
||||
if isinstance(result, dict) and result.get('status', False):
|
||||
logger.info(f"代码执行成功,结果: {result['data']}")
|
||||
return result
|
||||
elif isinstance(result, dict):
|
||||
error_msg = result.get("message", "返回结果中没有错误信息")
|
||||
else:
|
||||
error_msg = "返回结果格式未知"
|
||||
prev_happend_error = True
|
||||
except Exception as e:
|
||||
error_msg = f"执行代码时发生异常: {str(e)}"
|
||||
prev_happend_error = True
|
||||
|
||||
error_msg = f"代码执行失败,最后一次错误信息: {error_msg}"
|
||||
logger.error(error_msg)
|
||||
return {
|
||||
"code": 50000,
|
||||
"message": error_msg,
|
||||
"status": False,
|
||||
"data": []
|
||||
}
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
# src/config.py
|
||||
|
||||
import yaml
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
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)
|
||||
|
||||
class Config:
|
||||
def __init__(self, path="config.yaml"):
|
||||
@@ -35,3 +55,7 @@ class Config:
|
||||
@property
|
||||
def langsmith(self):
|
||||
return self._config.get("langsmith", {})
|
||||
|
||||
@property
|
||||
def max_retries(self):
|
||||
return self._config.get("max_retries", 3)
|
||||
|
||||
+53
-1
@@ -1,10 +1,26 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
class QuestionProcessor:
|
||||
def __init__(self, llm_client, business_structure, prompts):
|
||||
@@ -112,6 +128,18 @@ class DialogManager:
|
||||
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 退出。")
|
||||
@@ -174,3 +202,27 @@ class DialogManager:
|
||||
)
|
||||
|
||||
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,24 @@
|
||||
# src/document_loader.py
|
||||
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
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 load_file(path: str) -> str:
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
class EmbeddingClient:
|
||||
def __init__(self, embedding_config: dict):
|
||||
api_key = embedding_config.get("api_key")
|
||||
|
||||
+26
-1
@@ -1,8 +1,27 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
class LLMClient:
|
||||
def __init__(self, openai_config: dict):
|
||||
@@ -22,8 +41,14 @@ class LLMClient:
|
||||
|
||||
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=0, streaming=True, rate_limiter=rate_limiter)
|
||||
|
||||
def invoke(self, messages):
|
||||
# 同步调用,返回完整响应
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
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")
|
||||
|
||||
# 设置环境变量(可选,根据需要)
|
||||
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=0,
|
||||
streaming=True,
|
||||
rate_limiter=rate_limiter,
|
||||
)
|
||||
for key in api_keys
|
||||
]
|
||||
|
||||
# 轮询器,用于循环调用不同的 llm 实例
|
||||
self._llm_cycle = itertools.cycle(self.llms)
|
||||
|
||||
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,26 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
class Neo4jRawRetriever:
|
||||
def __init__(self, neo4j_conf: dict):
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
class Neo4jKnowledgeRetriever:
|
||||
def __init__(self, neo4j_conf: dict, embedding_client):
|
||||
neo4j_uri = neo4j_conf.get("uri")
|
||||
|
||||
+728
@@ -0,0 +1,728 @@
|
||||
"""
|
||||
软件知识图谱类定义
|
||||
根据Ontology_Layer.txt文件中的知识图谱信息创建
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import json
|
||||
|
||||
|
||||
class ProjectToolkit(ABC):
|
||||
"""
|
||||
项目类(抽象基类)
|
||||
描述: 代表整个项目结构的顶层容器
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.project_division_set = ProjectDivisionItem() # 项目划分集对象
|
||||
|
||||
# 项目划分查询方法
|
||||
|
||||
@abstractmethod
|
||||
def get_division_by_name(self, name):
|
||||
"""
|
||||
通过名称获取项目划分对象
|
||||
|
||||
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=None, code=None):
|
||||
"""
|
||||
通过父节点路径和编码获取工程量对象(定额、主材或设备),包括子节点
|
||||
|
||||
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, partial_name, quantity_type=None):
|
||||
"""
|
||||
通过父节点路径、模糊节点名称和类型获取工程量对象(主材或者设备),包括子节点
|
||||
|
||||
Args:
|
||||
parent_path (str): 父节点的路径,以'/'分隔的多级节点路径
|
||||
partial_name (str): 目标节点的模糊或不完整名称
|
||||
quantity_type (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: 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):
|
||||
"""
|
||||
在其它费用表中查找费用
|
||||
|
||||
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):
|
||||
"""
|
||||
在其中:场地征用费用表中查找费用
|
||||
|
||||
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):
|
||||
"""
|
||||
在安装价差费用表中查找费用
|
||||
|
||||
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):
|
||||
"""
|
||||
在工程费用表中查找费用
|
||||
|
||||
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 = 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)
|
||||
File diff suppressed because it is too large
Load Diff
+197
-5
@@ -1,5 +1,8 @@
|
||||
# src/prompt_manager.py
|
||||
|
||||
import os
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from langchain.prompts import ChatPromptTemplate
|
||||
from langchain.schema import SystemMessage, HumanMessage
|
||||
@@ -60,14 +63,23 @@ class PromptManager:
|
||||
# 输出格式(必须严格遵循)
|
||||
def project_get_calculate_function():
|
||||
project = ProjectBuilder.build()
|
||||
status, data, error, helper_info = project.[SELECTED_METHOD]([PARAMETERS])
|
||||
return status, data, error, helper_info
|
||||
result_dict = project.[SELECTED_METHOD]([PARAMETERS])
|
||||
status = result_dict.get('status', False)
|
||||
message = result_dict.get('message', '')
|
||||
code = result_dict.get('data', '')
|
||||
data = result_dict.get('data', [])
|
||||
if status:
|
||||
return result_dict
|
||||
else:
|
||||
return result_dict
|
||||
|
||||
# 执行规则
|
||||
- 参数必须从用户问题或上下文信息中提取
|
||||
- 输出代码中必须以def project_get_calculate_function() -> Tuple[bool, Any, Optional[str], Dict[str, Any]]函数作为入口函数
|
||||
- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数
|
||||
- 必须确保生成的代码可以直接执行,代码要注意进行各类错误检查,出错采用抛出异常方式,说明详细信息
|
||||
- 禁止添加任何注释或解释
|
||||
- ProjectToolkit 类中涉及项目划分的函数已考虑在其及其子孙项目划分下查找,所以无需生成递归子项目划分的代码
|
||||
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -89,6 +101,7 @@ def project_get_calculate_function():
|
||||
4. 修复后的代码应该完整,可以直接执行,并且能够返回查询结果
|
||||
|
||||
注意:
|
||||
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
|
||||
- 必须只输出最终的Python代码,不要添加任何解释、注释、推理过程或自然语言描述。
|
||||
- 不要以“以下是修正后的代码”、“修改如下”等语句开头。
|
||||
- 不要输出任何其他无关的内容。
|
||||
@@ -131,8 +144,187 @@ def project_get_calculate_function():
|
||||
{user_input}
|
||||
|
||||
改写规则:
|
||||
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。知识图谱节点标签包括ProjectDivisionItem、ProjectQuantity、Fee、FeeCollection等。
|
||||
识别目标对象:仅从业务结构中识别用户问题中需要获取的核心对象类型(如 ProjectDivisionItem、ProjectQuantity、Fee、FeeCollection 等)。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
|
||||
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。
|
||||
识别目标对象:仅从业务结构中识别如下类型:ProjectDivisionItem、ProjectAttributeSet、FeeSchedule、FeeItem的核心对象。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
|
||||
提取查询条件:从用户输入中解析关键条件(如名称、量、类型、值、层级等),条件应基于对象属性(如 name、quantity、type),注意区分查找子串和相等的区别。如果条件涉及层级路径(如路径中包含特定部分),需提取路径部分作为条件(例如,用户提到“架空输电线路”时,解析为路径匹配)。
|
||||
如果用户指定层级(如“叶节点”),需在条件中体现(例如,添加 WHERE item.isLeaf = true)。
|
||||
忽略任何计算、转换或后处理要求(如“乘以1000”),只关注获取原始数据对象或属性。
|
||||
构建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,
|
||||
code_gen_prompt=code_gen_prompt,
|
||||
code_fix_prompt=code_fix_prompt,
|
||||
rewrite_prompt_template=rewrite_prompt_template,
|
||||
cypher_conversion_prompt=cypher_conversion_prompt,
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
@dataclass
|
||||
class CodeExecutorPrompts:
|
||||
understand_prompt: ChatPromptTemplate
|
||||
code_gen_prompt: ChatPromptTemplate
|
||||
code_fix_prompt: ChatPromptTemplate
|
||||
rewrite_prompt_template: ChatPromptTemplate
|
||||
cypher_conversion_prompt: ChatPromptTemplate # 新增Cypher转换提示模板
|
||||
|
||||
|
||||
class PromptManager:
|
||||
def __init__(self):
|
||||
self.prompts = self._init_prompts()
|
||||
|
||||
def _init_prompts(self) -> CodeExecutorPrompts:
|
||||
understand_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
你是一名电力造价业务专家,请基于以下示意工程文件业务结构,将用户自然语言问题改写成专业查询语句:
|
||||
|
||||
**示意工程文件业务结构**:
|
||||
{business_structure}
|
||||
|
||||
**改写规则**:
|
||||
1. **定位目标对象**:仅从示意工程文件业务结构中识别核心对象(如 `ProjectDivisionTree`→项目划分树、`FeeScheduleItem`→费用表)。
|
||||
2. **提取条件**:从用户输入中解析关键条件(如名称、量、类型),用【】标注变量。
|
||||
3. **构建专业语句**:格式为:`在[目标对象]中查找【条件】的项。`
|
||||
- 使用业务术语(如“项目划分项”而非“项目”)。
|
||||
- 条件需明确属性(如【名称】、【量】、【类型】)。
|
||||
4. **精确映射结构**:若用户查询层级(如“叶节点”),需在条件中体现。
|
||||
|
||||
**用户输入**:{user_input}
|
||||
**改写输出**:(仅输出改写后的语句)
|
||||
"""
|
||||
)
|
||||
|
||||
code_gen_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
你是一个专业的Python工程师。我会给你一个用户问题,你需要将其转换为对应的Python代码
|
||||
|
||||
用户问题:
|
||||
{user_request}
|
||||
|
||||
上下文信息:
|
||||
{context}
|
||||
|
||||
工程数据访问库:
|
||||
{bowei_api_docs}
|
||||
|
||||
# 工作流程
|
||||
1. 从用户问题中提取关键信息(节点路径、节点类型、节点名称等)
|
||||
2. 根据"用户问题"和"上下文信息"选择最匹配的"工程数据访问库"中的函数和对象属性
|
||||
3. 生成可直接执行的完全满足用户输入问题要求功能效果的Python函数代码
|
||||
|
||||
# 输出格式(必须严格遵循)
|
||||
def project_get_calculate_function():
|
||||
project = ProjectBuilder.build()
|
||||
result_dict = project.[SELECTED_METHOD]([PARAMETERS])
|
||||
status = result_dict.get('status', False)
|
||||
message = result_dict.get('message', '')
|
||||
code = result_dict.get('data', '')
|
||||
data = result_dict.get('data', [])
|
||||
if status:
|
||||
return result_dict
|
||||
else:
|
||||
return result_dict
|
||||
|
||||
# 执行规则
|
||||
- 参数必须从用户问题或上下文信息中提取
|
||||
- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数
|
||||
- 必须确保生成的代码可以直接执行,代码要注意进行各类错误检查,出错采用抛出异常方式,说明详细信息
|
||||
- 禁止添加任何注释或解释
|
||||
- ProjectToolkit 类中涉及项目划分的函数已考虑在其及其子孙项目划分下查找,所以无需生成递归子项目划分的代码
|
||||
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
|
||||
"""
|
||||
)
|
||||
|
||||
code_fix_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
|
||||
你是一个专业的Python工程师。我会给你一段错误python代码和错误信息,你需要帮我修复这段出错的代码
|
||||
|
||||
已执行代码:
|
||||
{code}
|
||||
|
||||
代码执行报错信息:
|
||||
{error}
|
||||
|
||||
你的任务是:
|
||||
1. 根据"已执行代码"和"代码执行报错信息"来对“已执行代码”和函数调用参数进行修改,修复执行错误
|
||||
2. 如果错误信息中是代码的逻辑出现错误,那么就需要对代码本身整体结构进行修改
|
||||
3. 如果是代码中参数出现问题了,那么就需要结合错误信息中的帮助信息(helper_info)来对代码总的参数进行修改
|
||||
4. 修复后的代码应该完整,可以直接执行,并且能够返回查询结果
|
||||
|
||||
注意:
|
||||
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
|
||||
- 必须只输出最终的Python代码,不要添加任何解释、注释、推理过程或自然语言描述。
|
||||
- 不要以“以下是修正后的代码”、“修改如下”等语句开头。
|
||||
- 不要输出任何其他无关的内容。
|
||||
- 输出格式必须完全符合指定的函数模板。
|
||||
- 如果无法根据已有信息进行修改,请原样返回原始代码。
|
||||
- 禁止在代码前加上```python字样
|
||||
- 禁止在代码后加上```字样
|
||||
|
||||
请输出你修补后的代码:
|
||||
""")
|
||||
|
||||
rewrite_prompt_template = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
您是一个AI查询改写助手。基于给定的原始查询和上下文知识,生成一个精确的改写查询。步骤:
|
||||
1. 从上下文知识的`labels`提取对象类型,翻译为中文。
|
||||
2. 从`properties`选择对象标识:优先用`path`值,若无则用`name`值。
|
||||
3. 智能映射原始查询的属性名称:
|
||||
- 如果属性名称是上下文属性的缩写、省略或同义词,映射到实际属性名称(如“人工费”可能映射到“费率”或“合价含税”)。
|
||||
- 如果无法映射,保留原始名称。
|
||||
4. 保留原始查询的额外操作(如计算指令)。
|
||||
5. 输出格式:“获取[对象标识][对象类型]的[属性]属性,[额外操作]”。
|
||||
|
||||
示例参考:
|
||||
- 输入:原始问题="查找名称中包含“工程”的项目划分项,并返回其人工费乘以1000的值。", 上下文知识=...
|
||||
- 输出="获取[安装/架空输电线路本体工程/基础工程/基础工程材料工地运输]项目划分项的人工费,并乘以1000的值"
|
||||
|
||||
现在,处理以下输入:
|
||||
- 原始问题:{user_input}
|
||||
- 上下文知识:{context}
|
||||
""")
|
||||
|
||||
cypher_conversion_prompt = ChatPromptTemplate.from_template(
|
||||
"""
|
||||
你是一名电力造价业务专家,负责将用户自然语言问题中需要访问的对象识别出来,并生成针对该对象的NEO4J知识图谱的Cypher查询语句,获取该对象的全部信息。知识图谱基于从文件“获取[安装/架空输电线路本体工程]项目划分项的单位.”中读取的层级关联结构构建。该文件包含用反斜杠分割的多个字符串(如“安装/架空输电线路本体工程”),每个字符串表示一个完整的层级路径,路径部分用斜杠分隔,对应于知识图谱中ProjectDivisionItem节点的层级关系。路径映射规则:每个路径部分(如“安装”)是一个ProjectDivisionItem节点,父子关系通过关系类型`:CHILD_OF`连接,形成从根节点到叶节点的层级结构。
|
||||
|
||||
示例业务结构:
|
||||
{business_structure}
|
||||
|
||||
用户问题:
|
||||
{user_input}
|
||||
|
||||
改写规则:
|
||||
识别目标层级: 从用户输入中解析层级路径,例如字符串“安装/架空输电线路本体工程”映射为:根节点(name: '安装') -[:CHILD_OF]-> 子节点(name: '架空输电线路本体工程')。
|
||||
识别目标对象:仅从业务结构中识别如下类型:ProjectDivisionItem、ProjectAttributeSet、FeeSchedule、FeeItem的核心对象。对象类型必须精确映射到结构中的节点标签(例如,使用 ProjectDivisionItem 而非“项目划分项”)。
|
||||
提取查询条件:从用户输入中解析关键条件(如名称、量、类型、值、层级等),条件应基于对象属性(如 name、quantity、type),注意区分查找子串和相等的区别。如果条件涉及层级路径(如路径中包含特定部分),需提取路径部分作为条件(例如,用户提到“架空输电线路”时,解析为路径匹配)。
|
||||
如果用户指定层级(如“叶节点”),需在条件中体现(例如,添加 WHERE item.isLeaf = true)。
|
||||
忽略任何计算、转换或后处理要求(如“乘以1000”),只关注获取原始数据对象或属性。
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# 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]
|
||||
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)
|
||||
|
||||
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 path = '架空输电线路本体工程/基础工程'\n quantity_type = '定额'\n code = 'YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7'\n result_dict = project.get_quantities_node_by_parent_and_code(path, quantity_type, code)\n status = result_dict.get('status', False)\n if not status:\n return result_dict\n data = result_dict.get('data', [])\n total_quantity = 0.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 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 partial_name=\"角钢\",\n quantity_type=\"主材\"\n )\n status = result_dict.get('status', False)\n if not status:\n return result_dict\n \n data = result_dict.get('data', [])\n total_quantity = 0.0\n \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 {\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_quantity = 0.0\n for item in result.get('data', []):\n if isinstance(item, dict) and item.get('类型') == '主材' and '数量' in item:\n try:\n total_quantity += float(item['数量'])\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 \n parent_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n quantity_type = \"主材\"\n partial_name = \"角钢\"\n \n result_dict = project.get_quantities_node_by_parent_and_name(parent_path, partial_name, quantity_type)\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 '单价含税' 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 \n parent_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n quantity_type = \"主材\"\n partial_name = \"角钢、高强\"\n \n result_dict = project.get_quantities_node_by_parent_and_name(parent_path, partial_name, quantity_type)\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, dict) and '单价含税' in 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 result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path=\"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\",\n partial_name=\"钢管塔\",\n quantity_type=\"主材\"\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 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 parent_path = \"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\"\n result_dict = project.get_quantities_node_by_parent_and_name(parent_path, \"钢管塔\", \"主材\")\n status = result_dict.get('status', False)\n data = result_dict.get('data', [])\n if not status:\n return result_dict\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 {\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 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 result_dict\n \n total_quantity = 0.0\n for item in data:\n if isinstance(item, Material) and \"钢管杆\" in item.name:\n try:\n quantity = float(item.数量) if item.数量 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 result = project.get_division_node_by_parent_and_name(\n parent_path=\"架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立\",\n partial_name=\"钢管杆\"\n )\n if not result.get('status', False):\n return result\n \n total_price = 0.0\n for division in result.get('data', []):\n materials_result = project.get_quantities_node_by_parent_and_name(\n parent_path=division['constraints']['path'],\n partial_name=\"主材\",\n quantity_type=\"主材\"\n )\n if not materials_result.get('status', False):\n continue\n \n for material in materials_result.get('data', []):\n if \"钢管杆\" in material.get('name', ''):\n price = float(material.get('单价含税', 0)) if material.get('单价含税') else 0.0\n total_price += price\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 target_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 parent_path = '架空输电线路本体工程/架线工程'\n quantity_type = '定额'\n total_parameter = 0.0\n \n for code in target_codes:\n result_dict = project.get_quantities_node_by_parent_and_code(parent_path, quantity_type, code)\n status = result_dict.get('status', False)\n if not status:\n continue\n data = result_dict.get('data', [])\n if isinstance(data, list):\n for item in data:\n if isinstance(item, dict) and '参数' in item:\n try:\n total_parameter += float(item['参数'])\n except (ValueError, TypeError):\n pass\n elif isinstance(data, dict) and '参数' in data:\n try:\n total_parameter += float(data['参数'])\n except (ValueError, TypeError):\n pass\n \n return {\n 'code': 200,\n 'message': 'Ok',\n 'status': True,\n 'data': total_parameter\n }"}
|
||||
{"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 partial_name=\"高导电率\",\n quantity_type=\"主材\"\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 '数量' in material:\n try:\n total_quantity += float(material['数量'])\n except (ValueError, TypeError):\n pass\n \n return {\n 'code': 200 if total_quantity > 0 else 201,\n 'message': 'Ok' if total_quantity > 0 else 'No matching materials found',\n 'status': total_quantity > 0,\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 result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n partial_name=\"线\",\n quantity_type=\"主材\"\n )\n \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 price_str = material.get('单价含税', '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 {\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 result = project.get_quantities_node_by_parent_and_name(\n parent_path=path,\n partial_name=\"高导电率\",\n quantity_type=\"主材\"\n )\n \n if not result.get('status', False):\n continue\n \n materials = 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 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 partial_name=material_name,\n quantity_type='主材'\n )\n if not result_dict.get('status', False):\n continue\n materials = result_dict.get('data', [])\n for material in materials:\n if isinstance(material, dict) and '参数' in material:\n try:\n param_value = float(material['参数'])\n total_params += param_value\n except (ValueError, TypeError):\n continue\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 result_dict = project.get_quantities_node_by_parent_and_name(\n parent_path=\"架空输电线路本体工程/基础工程/基础砌筑\",\n partial_name=\"圆钢\",\n quantity_type=\"主材\"\n )\n status = result_dict.get('status', False)\n if not status:\n return result_dict\n \n data = result_dict.get('data', [])\n total_quantity = 0.0\n \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 {\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 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 for material_data in quantities_result.get('data', []):\n material = Material()\n material.__dict__.update(material_data)\n if \"单价含税\" in material.__dict__ 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 for child_data in children_result.get('data', []):\n if 'path' in child_data:\n process_division(child_data['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(\"工程费用\", \"架空输电线路本体工程.合计费\", \"合计费\")\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": "根据用户问题和上下文信息,我将生成对应的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('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_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_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": "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`。\n\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 if not status:\n raise Exception(f\"获取费用失败: {result_dict.get('message', '未知错误')}\")\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_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 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": "根据用户问题和上下文信息,我们需要从\"工程费用\"中获取\"其他费用.合计费\"的属性。根据上下文信息,\"工程费用\"的类型是\"FeeScheduleItem\",而\"其他费用.合计费\"的类型是\"Fee\"。\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=\"其他费用\"\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": "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('其他费用表', '工程监理费', 'rate')\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": "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('其他费用表', '勘察费', 'rate')\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('其他费用表', '设计费', 'rate')\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`,因为它专门用于在其他费用表中查找费用。\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=\"*\"\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(table_name='其他费用表', fee_name='生产准备费', fee='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": "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_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=\"合计费\"\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_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"}
|
||||
+42
-22
@@ -2,9 +2,25 @@ 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.llm_client import LLMClient
|
||||
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
|
||||
@@ -13,18 +29,25 @@ from src.config import Config
|
||||
from src.document_loader import load_file
|
||||
from src.embedding_client import EmbeddingClient
|
||||
|
||||
from project import ProjectBuilder, ProjectToolkit
|
||||
from project_implementation import ProjectToolkitNeo4j
|
||||
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 = LLMClient(config.openai)
|
||||
llm_client = MultiAPIKeyChatOpenAI(config.openai)
|
||||
|
||||
llm_client_coder = LLMClient(config.openai_coder)
|
||||
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
|
||||
|
||||
prompt_manager = PromptManager()
|
||||
|
||||
@@ -38,7 +61,7 @@ def main():
|
||||
|
||||
ProjectBuilder.register(ProjectToolkitNeo4j, knowledge_retriever.driver)
|
||||
|
||||
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder)
|
||||
code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
|
||||
|
||||
dialog_manager = DialogManager(
|
||||
llm_client,
|
||||
@@ -55,37 +78,34 @@ def main():
|
||||
|
||||
# 提取指标映射关系并批量执行
|
||||
for item in zhibiao_data:
|
||||
datasource = item['数据来源']
|
||||
if datasource == '报表指标' or datasource == '指标库':
|
||||
continue
|
||||
|
||||
query = item['指标描述']['指标映射']
|
||||
rewritten_results = dialog_manager.understand_user_question_stream(query)
|
||||
#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]
|
||||
questions = []
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
error_list = []
|
||||
|
||||
# 检查 understand_user_question_stream 方法调用结果,假设存在该方法调用
|
||||
# 示例调用,实际使用时请替换为真实调用
|
||||
# stream_result = dialog_manager.understand_user_question_stream(query)
|
||||
# if stream_result is None or stream_result == []:
|
||||
# questions.append(query)
|
||||
# fail_count += 1
|
||||
# error_list.append(f"问题 {query} 调用 understand_user_question_stream 返回空结果")
|
||||
|
||||
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"问题 {selected_rewritten} 调用 execute_generated_code 返回空结果")
|
||||
error_list.append(f"问题 {query} {selected_rewritten} 调用 execute_generated_code 返回空结果")
|
||||
else:
|
||||
success_count += 1
|
||||
|
||||
print(result)
|
||||
#print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
total = success_count + fail_count
|
||||
if total > 0:
|
||||
success_rate = success_count / total
|
||||
@@ -98,5 +118,5 @@ if __name__ == "__main__":
|
||||
print("错误列表:")
|
||||
for error in error_list:
|
||||
print(error)
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@@ -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,162 @@
|
||||
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']
|
||||
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'./tests/code_{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')
|
||||
|
||||
|
||||
+6
-6
@@ -8,7 +8,7 @@ import logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG, # 生产环境可改为 INFO 或 WARNING
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.FileHandler("test.log", encoding="utf-8"), logging.StreamHandler()],
|
||||
handlers=[logging.FileHandler("logs/test.log", encoding="utf-8"), logging.StreamHandler()],
|
||||
)
|
||||
|
||||
logger = logging.getLogger("test")
|
||||
@@ -34,7 +34,7 @@ for name in logger_names:
|
||||
import asyncio
|
||||
import json
|
||||
from src.dialog_manager import DialogManager
|
||||
from src.llm_client import LLMClient
|
||||
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
|
||||
@@ -43,8 +43,8 @@ from src.config import Config
|
||||
from src.document_loader import load_file
|
||||
from src.embedding_client import EmbeddingClient
|
||||
|
||||
from project import ProjectBuilder, ProjectToolkit
|
||||
from project_implementation import ProjectToolkitNeo4j
|
||||
from src.project import ProjectBuilder, ProjectToolkit
|
||||
from src.project_implementation import ProjectToolkitNeo4j
|
||||
|
||||
|
||||
def main():
|
||||
@@ -53,9 +53,9 @@ def main():
|
||||
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 = LLMClient(config.openai_coder)
|
||||
llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder)
|
||||
|
||||
prompt_manager = PromptManager()
|
||||
|
||||
|
||||
+48
-15
@@ -5,15 +5,15 @@ import logging
|
||||
import traceback
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from project import ProjectBuilder, ProjectToolkit
|
||||
from project_implementation import ProjectToolkitNeo4j
|
||||
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("test_runcode.log", encoding="utf-8"), logging.StreamHandler()],
|
||||
handlers=[logging.FileHandler("logs/test_runcode.log", encoding="utf-8"), logging.StreamHandler()],
|
||||
)
|
||||
|
||||
logger = logging.getLogger("test_runcode")
|
||||
@@ -24,13 +24,27 @@ def main():
|
||||
neo4j_conf = config.neo4j_conf
|
||||
|
||||
code_str = '''
|
||||
def project_get_calculate_function():
|
||||
project = ProjectBuilder.build()
|
||||
status, data, error, helper_info = project.get_division_item_by_path("安装/架空输电线路本体工程")
|
||||
if status == 'success':
|
||||
return status, data.get('单位', ''), error, helper_info
|
||||
return status, None, error, helper_info
|
||||
'''
|
||||
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")))
|
||||
|
||||
@@ -53,23 +67,42 @@ def main():
|
||||
if "project_get_calculate_function" not in namespace:
|
||||
raise ValueError("代码中未定义project_get_calculate_function函数")
|
||||
|
||||
result_tuple = namespace["project_get_calculate_function"]()
|
||||
result_dict = namespace["project_get_calculate_function"]()
|
||||
|
||||
sys.stdout = old_stdout
|
||||
output = redirected_output.getvalue().strip()
|
||||
|
||||
if not isinstance(result_tuple, tuple) or len(result_tuple) != 4:
|
||||
raise ValueError("函数应返回包含4个元素的元组(status, data, error, helper_info)")
|
||||
if not isinstance(result_dict, dict) or len(result_dict) != 4:
|
||||
raise ValueError("函数应返回包含4个元素的字典(status, data, error, helper_info)")
|
||||
|
||||
status, data, error, helper_info = result_tuple
|
||||
logger.debug(f"执行结果: {result_dict}")
|
||||
|
||||
logger.info(f"执行结果: status={status}, data={data}, error={error}")
|
||||
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__":
|
||||
|
||||
@@ -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__), "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"zhibiao_{now_str}.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()
|
||||
+1575
-429
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": {"type": "ProjectQuantity", "类型": "定额", "编码": "YX2-1~7"}}]}
|
||||
{"name": "角钢塔_塔材量", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【数量】之和", "result": [{"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": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含角钢"}}]}
|
||||
{"name": "角钢塔_其中:高强钢塔材费用_元", "query": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【单价】之和", "result": [{"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", "类型": "主材", "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": "包含钢管杆"}}]}
|
||||
{"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": {"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": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "包含高导电率"}}]}
|
||||
{"name": "导线及线材_导线装材费_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【线】的所有【主材】的【单价】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设"}}, {"name": "主材", "constraints": {"type": "ProjectQuantity", "类型": "主材", "name": "线"}}]}
|
||||
{"name": "导线及线材_其中:节能导线费用_元", "query": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【单价】之和", "result": [{"name": "架空输电线路本体工程/架线工程/导地线架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线架设"}}, {"name": "架空输电线路本体工程/架线工程/导地线跨越架设", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程/导地线跨越架设"}}, {"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", "name": "圆钢", "类型": "主材"}}]}
|
||||
{"name": "本体费用合计_元", "query": "从【工程费用】中获取【架空输电线路本体工程.合计费】的属性", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "Fee", "name": "架空输电线路本体工程", "code": "BTGC"}}, {"name": "合计费", "constraints": {"type": "Fee", "name": "合计费"}}]}
|
||||
{"name": "本体工程人工费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【本体工程人工费_本体_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "本体工程人工费_本体_元", "constraints": {"type": "CostItem", "name": "本体工程人工费_本体_元"}}]}
|
||||
{"name": "本体工程人工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【本体工程人工费_调试_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "本体工程人工费_调试_元", "constraints": {"type": "CostItem", "name": "本体工程人工费_调试_元"}}]}
|
||||
{"name": "本体工程机械费_本体_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【本体工程机械费_本体_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "本体工程机械费_本体_元", "constraints": {"type": "CostItem", "name": "本体工程机械费_本体_元"}}]}
|
||||
{"name": "本体工程机械费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【本体工程机械费_调试_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "本体工程机械费_调试_元", "constraints": {"type": "CostItem", "name": "本体工程机械费_调试_元"}}]}
|
||||
{"name": "基础工程费用_元", "query": "从【架空输电线路本体工程/基础工程】项目划分中获取名称属于【基础工程费用_元】的费用", "result": [{"name": "基础工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/基础工程", "name": "基础工程"}}, {"name": "基础工程费用_元", "constraints": {"type": "CostItem", "name": "基础工程费用_元"}}]}
|
||||
{"name": "杆塔工程费用_元", "query": "从【架空输电线路本体工程/杆塔工程】项目划分中获取名称属于【杆塔工程费用_元】的费用", "result": [{"name": "杆塔工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/杆塔工程", "name": "杆塔工程"}}, {"name": "杆塔工程费用_元", "constraints": {"type": "CostItem", "name": "杆塔工程费用_元"}}]}
|
||||
{"name": "接地工程费用_元", "query": "从【架空输电线路本体工程/接地工程】项目划分中获取名称属于【接地工程费用_元】的费用", "result": [{"name": "接地工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/接地工程", "name": "接地工程"}}, {"name": "接地工程费用_元", "constraints": {"type": "CostItem", "name": "接地工程费用_元"}}]}
|
||||
{"name": "架线工程费用_元", "query": "从【架空输电线路本体工程/架线工程】项目划分中获取名称属于【架线工程费用_元】的费用", "result": [{"name": "架线工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/架线工程", "name": "架线工程"}}, {"name": "架线工程费用_元", "constraints": {"type": "CostItem", "name": "架线工程费用_元"}}]}
|
||||
{"name": "附件工程费用_元", "query": "从【架空输电线路本体工程/附件安装工程】项目划分中获取名称属于【附件工程费用_元】的费用", "result": [{"name": "附件安装工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/附件安装工程", "name": "附件安装工程"}}, {"name": "附件工程费用_元", "constraints": {"type": "CostItem", "name": "附件工程费用_元"}}]}
|
||||
{"name": "辅助工程费用_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取名称属于【辅助工程费用_元】的费用", "result": [{"name": "辅助工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/辅助工程", "name": "辅助工程"}}, {"name": "辅助工程费用_元", "constraints": {"type": "CostItem", "name": "辅助工程费用_元"}}]}
|
||||
{"name": "辅助工程费用_调试_元", "query": "从【架空输电线路本体工程/辅助工程】项目划分中获取名称属于【辅助工程费用_调试_元】的费用", "result": [{"name": "辅助工程", "constraints": {"type": "ProjectDivisionItem", "path": "架空输电线路本体工程/辅助工程", "name": "辅助工程"}}, {"name": "辅助工程费用_调试_元", "constraints": {"type": "CostItem", "name": "辅助工程费用_调试_元"}}]}
|
||||
{"name": "辅助设施工程_元", "query": "从【工程费用】中获取【辅助设施工程.合计费】的属性", "result": [{"name": "辅助设施工程", "constraints": {"type": "ProjectDivisionItem", "name": "辅助设施工程"}}, {"name": "合计费", "constraints": {"type": "CostItem", "name": "合计费"}}]}
|
||||
{"name": "其他费用合计_元", "query": "从【工程费用】中获取【其他费用.合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "其他费用.合计费", "constraints": {"type": "Fee", "name": "合计费", "code": "其他费用"}}]}
|
||||
{"name": "建场费合计_元", "query": "从【其他费用】中获取【建设场地征用及清理费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "建设场地征用及清理费", "constraints": {"type": "Fee", "name": "建设场地征用及清理费"}}]}
|
||||
{"name": "项目建设管理费合计_元", "query": "从【其他费用】中获取【项目建设管理费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目建设管理费", "constraints": {"type": "Fee", "name": "项目建设管理费"}}]}
|
||||
{"name": "其中:工程监理费_元", "query": "从【其他费用】中获取【工程监理费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "工程监理费", "constraints": {"type": "Fee", "name": "工程监理费"}}]}
|
||||
{"name": "项目建设技术服务费合计_元", "query": "从【其他费用】中获取【项目建设技术服务费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目建设技术服务费", "constraints": {"type": "Fee", "name": "项目建设技术服务费"}}]}
|
||||
{"name": "其中:项目前期工作费_元", "query": "从【其他费用】中获取【项目前期工作费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "项目前期工作费", "constraints": {"type": "Fee", "name": "项目前期工作费"}}]}
|
||||
{"name": "其中:勘察费_元", "query": "从【其他费用】中获取【勘察费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "勘察费", "constraints": {"type": "Fee", "name": "勘察费"}}]}
|
||||
{"name": "其中:设计费_元", "query": "从【其他费用】中获取【设计费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "设计费", "constraints": {"type": "Fee", "name": "设计费"}}]}
|
||||
{"name": "其中:工程建设检测费_元", "query": "从【其他费用】中获取【工程建设检测费】的属性", "result": [{"name": "其他费用", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "工程建设检测费", "constraints": {"type": "Fee", "name": "工程建设检测费"}}]}
|
||||
{"name": "生产准备费_元", "query": "从【其他费用】中获取【生产准备费】的属性", "result": [{"name": "其他费用表", "constraints": {"type": "FeeScheduleItem", "name": "其他费用表"}}, {"name": "生产准备费", "constraints": {"type": "Fee", "name": "生产准备费"}}]}
|
||||
{"name": "其中:安全文明施工费_线路_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【其中:安全文明施工费_线路_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "其中:安全文明施工费_线路_元", "constraints": {"type": "CostItem", "name": "其中:安全文明施工费_线路_元"}}]}
|
||||
{"name": "其中:安全文明施工费_调试_元", "query": "从【架空输电线路本体工程】项目划分中获取名称属于【其中:安全文明施工费_调试_元】的费用", "result": [{"name": "架空输电线路本体工程", "constraints": {"type": "ProjectDivisionItem", "name": "架空输电线路本体工程"}}, {"name": "其中:安全文明施工费_调试_元", "constraints": {"type": "CostItem", "name": "其中:安全文明施工费_调试_元"}}]}
|
||||
{"name": "基本预备费_元", "query": "从【工程费用】中获取【基本预备费.合计费】的属性", "result": [{"name": "基本预备费.合计费", "constraints": {"type": "Fee", "name": "基本预备费.合计费"}}]}
|
||||
{"name": "静态投资_元", "query": "从【工程费用】中获取【工程静态投资(一~七项合计).合计费】的属性", "result": [{"name": "工程费用表", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "工程静态投资(一~七项合计).合计费", "constraints": {"type": "Fee", "name": "工程静态投资(一~七项合计).合计费"}}]}
|
||||
{"name": "建设期利息_元", "query": "从【工程费用】中获取【建设期贷款利息.合计费】的属性", "result": [{"name": "建设期贷款利息", "constraints": {"type": "Fee", "name": "建设期贷款利息", "code": "DKLX"}}, {"name": "合计费", "constraints": {"type": "Fee", "name": "合计费"}}]}
|
||||
{"name": "动态投资_元", "query": "从【工程费用】中获取【工程动态投资(一~八项合计).合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "工程动态投资(一~八项合计).合计费", "constraints": {"type": "Fee", "name": "工程动态投资(一~八项合计).合计费"}}]}
|
||||
{"name": "增值税抵扣税额_元", "query": "从【工程费用】中获取【其中:可抵扣增值税额.合计费】的属性", "result": [{"name": "工程费用", "constraints": {"type": "FeeScheduleItem", "name": "工程费用表"}}, {"name": "其中:可抵扣增值税额.合计费", "constraints": {"type": "Fee", "name": "其中:可抵扣增值税额.合计费"}}]}
|
||||
@@ -1,361 +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)
|
||||
|
||||
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,167 @@
|
||||
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")
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
global code_executor
|
||||
|
||||
"""运行JSON记录中的代码并返回结果"""
|
||||
if not data or 'code' not in data:
|
||||
return {
|
||||
"code": 40000,
|
||||
"message": "没有可执行的代码",
|
||||
"status": False,
|
||||
"data": None
|
||||
}
|
||||
|
||||
result = code_executor.execute_code(data['code'])
|
||||
|
||||
def main():
|
||||
st.set_page_config(layout="wide", page_title="JSONL查看器")
|
||||
|
||||
st.title("JSONL文件查看器")
|
||||
|
||||
# 设置默认文件路径
|
||||
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
|
||||
|
||||
# 创建两列布局
|
||||
col1, col2 = st.columns([1, 3])
|
||||
|
||||
# 左侧列表
|
||||
with col1:
|
||||
st.subheader("记录列表")
|
||||
selected_index = None
|
||||
|
||||
for i, record in enumerate(records):
|
||||
if st.button(record.get('name', f"记录 {i+1}"), key=f"btn_{i}"):
|
||||
selected_index = i
|
||||
|
||||
# 右侧详细信息
|
||||
with col2:
|
||||
if 'selected_index' not in st.session_state:
|
||||
st.session_state.selected_index = 0
|
||||
|
||||
if selected_index is not None:
|
||||
st.session_state.selected_index = selected_index
|
||||
|
||||
if st.session_state.selected_index < len(records):
|
||||
selected_record = records[st.session_state.selected_index]
|
||||
|
||||
st.subheader(f"查询问题: {selected_record.get('name', '无名称')}")
|
||||
st.info(selected_record.get('query', '无查询信息'))
|
||||
|
||||
st.subheader("代码")
|
||||
st.code(selected_record.get('code', '无代码'), language='python')
|
||||
|
||||
# 运行代码按钮
|
||||
if st.button("运行代码"):
|
||||
with st.spinner('正在执行代码...'):
|
||||
result = run_code(selected_record)
|
||||
|
||||
st.subheader("运行结果")
|
||||
if result.get('status'):
|
||||
st.success("执行成功")
|
||||
st.json(result.get('data'))
|
||||
else:
|
||||
st.error(f"执行失败: {result.get('message', '未知错误')}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,117 +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 = "架空_clean.json" # 输入 JSON 文件路径
|
||||
output_file = "output.json" # 输出 JSON 文件路径
|
||||
|
||||
# 使用类方法加载并处理 JSON 文件
|
||||
ExpenseProcessor.load_and_process_from_file(input_file, output_file)
|
||||
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,206 @@
|
||||
import chardet
|
||||
import xml.etree.ElementTree as ET
|
||||
import json
|
||||
import re
|
||||
|
||||
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}】项目划分中获取名称属于【{indicator_name}】的费用"
|
||||
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)
|
||||
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= "./tests/zhibiao.json")
|
||||
print("转换完毕!")
|
||||
|
||||
Reference in New Issue
Block a user