From b352571e171cd08ede23e5a823421afe709bf4f0 Mon Sep 17 00:00:00 2001
From: paituo <330435863@qq.com>
Date: Mon, 7 Jul 2025 11:25:17 +0800
Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 2 +-
main_streamlt.py | 852 +++++++++++++++++++++------------
requirements.txt | 3 +-
src/code_executor.py | 4 +-
src/project.py | 13 -
src/prompt_manager.py | 24 +-
tests/test_runcode.py | 2 +-
tests/test_user_interaction.py | 184 -------
tests/zhibiao.json | 92 ++--
tools/xml_to_json.py | 10 +-
10 files changed, 634 insertions(+), 552 deletions(-)
delete mode 100644 tests/test_user_interaction.py
diff --git a/main.py b/main.py
index 3012a3c..7386d6d 100644
--- a/main.py
+++ b/main.py
@@ -76,7 +76,7 @@ def main():
knowledge_retriever = Neo4jRawRetriever(neo4j_conf)
- code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder)
+ code_executor = CodeExecutor(prompt_manager.prompts, llm_client_coder, config.max_retries)
dialog_manager = DialogManager(
llm_client,
diff --git a/main_streamlt.py b/main_streamlt.py
index 41c25e0..b556101 100644
--- a/main_streamlt.py
+++ b/main_streamlt.py
@@ -7,6 +7,7 @@ from typing import Dict, List, Optional, Any
import logging
import sys
from datetime import datetime
+from textcomplete import textcomplete, StrategyProps
# 添加项目根目录到路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -350,6 +351,183 @@ def init_app_data():
}
+# 自动补全策略相关函数
+def create_project_division_strategy():
+ """创建项目划分自动补全策略"""
+ return StrategyProps(
+ id="projectDivision",
+ match="从项目划分【(.*)】",
+ search="""async (term, callback) => {
+ const divisions = [
+ "架空输电线路本体工程/基础工程",
+ "架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立",
+ "架空输电线路本体工程/架线工程",
+ "架空输电线路本体工程/附件安装工程"
+ ];
+ const matches = divisions.filter(div => div.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(division) => `从项目划分【${division}】`",
+ template="(division) => `📁 ${division}`",
+ )
+
+def create_search_type_strategy():
+ """创建搜索类型自动补全策略"""
+ return StrategyProps(
+ id="searchType",
+ match="中查找(名称|编码)(包含|等于)【(.*)】",
+ search="""async (term, callback) => {
+ const types = ["名称包含", "编码包含", "名称等于", "编码等于"];
+ const matches = types.filter(type => type.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(type) => `中查找${type}【`",
+ template="(type) => `🔍 ${type}`",
+ )
+
+def create_item_type_strategy():
+ """创建项目类型自动补全策略"""
+ return StrategyProps(
+ id="itemType",
+ match="的所有【(.*)】",
+ search="""async (term, callback) => {
+ const types = ["定额", "主材", "取费名称"];
+ const matches = types.filter(type => type.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(type) => `的所有【${type}】`",
+ template="(type) => `📋 ${type}`",
+ )
+
+def create_attribute_strategy():
+ """创建属性自动补全策略"""
+ return StrategyProps(
+ id="attribute",
+ match="的【(.*)】",
+ search="""async (term, callback) => {
+ const attributes = ["数量", "单价", "合计费", "属性"];
+ const matches = attributes.filter(attr => attr.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(attr) => `的【${attr}】`",
+ template="(attr) => `📊 ${attr}`",
+ )
+
+def create_fee_type_strategy():
+ """创建费用类型自动补全策略"""
+ return StrategyProps(
+ id="feeType",
+ match="从【(.*)】中",
+ search="""async (term, callback) => {
+ const types = ["工程费用", "其他费用"];
+ const matches = types.filter(type => type.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(type) => `从【${type}】中`",
+ template="(type) => `💰 ${type}`",
+ )
+
+def create_project_name_strategy():
+ """创建项目名称自动补全策略"""
+ return StrategyProps(
+ id="projectName",
+ match="获取【(.*)】的",
+ search="""async (term, callback) => {
+ const names = [
+ "架空输电线路本体工程",
+ "建设场地征用及清理费"
+ ];
+ const matches = names.filter(name => name.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(name) => `获取【${name}】的`",
+ template="(name) => `🏗️ ${name}`",
+ )
+
+def create_code_value_strategy():
+ """创建编码值自动补全策略"""
+ return StrategyProps(
+ id="codeValue",
+ match="编码包含【(.*)】",
+ search="""async (term, callback) => {
+ const codes = ["YX2-1~7", "YX5-9"];
+ const matches = codes.filter(code => code.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(code) => `编码包含【${code}】`",
+ template="(code) => `🔢 ${code}`",
+ )
+
+def create_name_value_strategy():
+ """创建名称值自动补全策略"""
+ return StrategyProps(
+ id="nameValue",
+ match="名称包含【(.*)】",
+ search="""async (term, callback) => {
+ const names = ["角钢", "钢管杆"];
+ const matches = names.filter(name => name.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(name) => `名称包含【${name}】`",
+ template="(name) => `📝 ${name}`",
+ )
+
+def create_name_equal_strategy():
+ """创建名称等于自动补全策略"""
+ return StrategyProps(
+ id="nameEqual",
+ match="取费名称等于【(.*)】",
+ search="""async (term, callback) => {
+ const names = ["合计"];
+ const matches = names.filter(name => name.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(name) => `取费名称等于【${name}】`",
+ template="(name) => `📌 ${name}`",
+ )
+
+def create_query_template_strategy():
+ """创建查询模板自动补全策略"""
+ return StrategyProps(
+ id="queryTemplate",
+ match="^$",
+ search="""async (term, callback) => {
+ const templates = [
+ "从项目划分【架空输电线路本体工程/基础工程】中查找编码包含【YX2-1~7】的所有【定额】的【数量】之和",
+ "从项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】中查找名称包含【角钢】的所有【主材】的【数量】之和",
+ "从项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】中查找名称包含【钢管杆】的所有【主材】的【单价】之和",
+ "从项目划分【架空输电线路本体工程/架线工程】中查找编码中包含【YX5-9】的所有【定额】的【数量】之和",
+ "从【工程费用】中获取【架空输电线路本体工程】的【合计费】属性",
+ "从【其他费用】中获取【建设场地征用及清理费】的属性",
+ "从项目划分【架空输电线路本体工程/附件安装工程】中获取取费名称等于【合计】的费用"
+ ];
+ const matches = templates.filter(template => template.toLowerCase().includes(term.toLowerCase()));
+ callback(matches);
+ }""",
+ replace="(template) => template",
+ template="(template) => `📋 ${template}`",
+ )
+
+def apply_autocomplete_to_query_input(area_label="输入查询语句", max_count=5):
+ """应用自动补全到查询输入框"""
+ # 初始化textcomplete组件
+ textcomplete(
+ area_label=area_label,
+ strategies=[
+ create_query_template_strategy(),
+ create_project_division_strategy(),
+ create_search_type_strategy(),
+ create_item_type_strategy(),
+ create_attribute_strategy(),
+ create_fee_type_strategy(),
+ create_project_name_strategy(),
+ create_code_value_strategy(),
+ create_name_value_strategy(),
+ create_name_equal_strategy()
+ ],
+ max_count=max_count
+ )
+
# Streamlit 应用界面
def main():
st.set_page_config(page_title="造价工程指标管理器", layout="wide")
@@ -364,6 +542,17 @@ def main():
st.session_state[key] = value
# 初始化会话状态
+ initialize_session_state()
+
+ # 侧边栏
+ render_sidebar()
+
+ # 主区域
+ render_main_area()
+
+
+def initialize_session_state():
+ """初始化会话状态"""
if "indicator_manager" not in st.session_state:
st.session_state.indicator_manager = IndicatorManager()
@@ -372,314 +561,391 @@ def main():
# 加载保存的会话状态
load_session_state()
-
- # 侧边栏
+
+
+def render_sidebar():
+ """渲染侧边栏内容"""
with st.sidebar:
-
# 第一个expander:指标管理
- with st.expander("新建指标工具", expanded=True):
-
- # 显示清除指标链接
- col1, col2, col3 = st.columns([2, 1, 1])
- with col1:
- st.subheader("指标列表")
- with col2:
- # 新建指标按钮
- if st.button("新建"):
- try:
- # 生成新指标名称
- indicator_count = len(st.session_state.indicator_manager.get_all_indicators())
- new_name = f"指标 {indicator_count + 1}"
-
- # 创建新指标
- new_indicator = st.session_state.indicator_manager.add_indicator(name=new_name)
- st.session_state.current_indicator_id = new_indicator.id
- # 清除已生成的代码
- if "generated_code" in st.session_state:
- del st.session_state.generated_code
- # 清除第二个expander的状态
- st.session_state.view_indicator_detail = False
- save_session_state()
- st.rerun()
- except ValueError as e:
- st.error(str(e))
- with col3:
- if st.button("清空", help="删除所有指标", type="secondary"):
- st.session_state.indicator_manager.clear_all_indicators()
- st.session_state.current_indicator_id = None
- # 清除已生成的代码
- if "generated_code" in st.session_state:
- del st.session_state.generated_code
- # 清除第二个expander的状态
- st.session_state.view_indicator_detail = False
- save_session_state()
- st.rerun()
-
- # 显示指标列表
- indicators = st.session_state.indicator_manager.get_all_indicators()
- if not indicators:
- st.info("暂无指标,请点击\"新建指标\"按钮创建")
- else:
- for indicator in sorted(indicators, key=lambda x: x.created_at):
- if st.button(
- indicator.name,
- key=f"btn_{indicator.id}",
- use_container_width=True,
- type="primary" if st.session_state.current_indicator_id == indicator.id else "secondary"
- ):
- st.session_state.current_indicator_id = indicator.id
- # 更新显示的代码
- if indicator.code:
- st.session_state.generated_code = indicator.code
- elif "generated_code" in st.session_state:
- # 如果当前指标没有代码,清除session中的代码
- del st.session_state.generated_code
- # 清除第二个expander的状态
- st.session_state.view_indicator_detail = False
- save_session_state()
- st.rerun()
+ render_indicator_management_expander()
# 第二个expander:指标库浏览
- with st.expander("查看指标库", expanded=False):
+ render_indicator_library_expander()
- # 设置默认文件路径
- file_path = os.path.join(DATA_DIR, "code.jsonl")
-
- # 文件路径输入
- #file_path = st.text_input("JSONL文件路径", value=default_file_path, key="jsonl_path")
-
- # 加载JSONL文件的函数
- @st.cache_data
- def load_jsonl(file_path):
- """加载JSONL文件并返回JSON指标列表"""
- records = []
- try:
- with open(file_path, "r", encoding="utf-8") as file:
- for line in file:
- if line.strip():
- records.append(json.loads(line))
- return records
- except Exception as e:
- st.error(f"加载文件失败: {str(e)}")
- return []
-
- # 检查文件是否存在
- if not os.path.exists(file_path):
- st.warning(f"文件不存在: {file_path}")
- else:
- # 加载JSONL文件
- records = load_jsonl(file_path)
-
- if not records:
- st.warning("没有找到有效的指标")
- else:
- # 初始化会话状态
- if "selected_index" not in st.session_state:
- st.session_state.selected_index = 0
-
- # 显示指标列表
- st.subheader("指标列表")
- for i, record in enumerate(records):
- if st.button(record.get("name", f"指标 {i+1}"), key=f"lib_btn_{i}", use_container_width=True):
- st.session_state.selected_index = i
- # 清除第一个expander的选中状态
- st.session_state.current_indicator_id = None
- # 设置查看指标详情的逻辑
- st.session_state.view_indicator_detail = True
- st.session_state.current_library_indicator = record
- save_session_state()
- st.rerun()
-
- # 主区域
- if "view_indicator_detail" in st.session_state and st.session_state.view_indicator_detail and "current_library_indicator" in st.session_state:
- # 显示从指标库中选择的指标详细信息
- library_indicator = st.session_state.current_library_indicator
-
- # 指标名称和操作按钮
- col1, col2, col3 = st.columns([1, 4, 2])
+
+def render_indicator_management_expander():
+ """渲染指标管理扩展面板"""
+ with st.expander("新建指标工具", expanded=True):
+ # 显示清除指标链接
+ col1, col2, col3 = st.columns([2, 1, 1])
with col1:
- st.markdown('
指标名称:
', unsafe_allow_html=True)
+ st.subheader("指标列表")
with col2:
- st.markdown(f'{library_indicator.get("name", "未命名指标")}
', unsafe_allow_html=True)
+ render_new_indicator_button()
+ with col3:
+ render_clear_indicators_button()
+
+ # 显示指标列表
+ render_indicator_list()
-
- # 查询语句部分
- title_col, run_btn_col, result_col = st.columns([1, 1, 4])
- with title_col:
- st.subheader("查询语句")
- with result_col:
- title_query_result = st.subheader("")
-
- # 显示查询语句
- st.text_area(
- "查询语句",
- value=library_indicator.get('query', '无查询语句'),
- height=70,
- key="lib_query_display",
- disabled=True,
- label_visibility="collapsed"
- )
-
- # 指标代码部分
- title_col, run_btn_col, result_col = st.columns([1, 1, 4])
- with title_col:
- st.subheader("指标代码")
- with result_col:
- title_code_result = st.subheader("")
- with run_btn_col:
- if st.button("执行代码", key="execute_lib_indicator", use_container_width=True):
- with st.spinner('正在执行代码...'):
- # 确保code_executor已初始化
- if "code_executor" in st.session_state:
- result = st.session_state.code_executor.execute_code(library_indicator.get('code', ''))
- if result and hasattr(result, 'get') and result.get('status'):
- title_code_result.success("执行成功")
- title_code_result.json(result.get('data', {}))
- else:
- title_code_result.error(f"执行失败: {result.get('message', '未知错误') if hasattr(result, 'get') else str(result)}")
- else:
- title_code_result.error("代码执行器未初始化")
-
- # 显示代码
- st.code(library_indicator.get('code', '无代码'), language='python')
-
- # 移除导入按钮部分
+def render_new_indicator_button():
+ """渲染新建指标按钮"""
+ if st.button("新建"):
+ try:
+ # 生成新指标名称
+ indicator_count = len(st.session_state.indicator_manager.get_all_indicators())
+ new_name = f"指标 {indicator_count + 1}"
- elif st.session_state.current_indicator_id is not None:
- current_indicator = st.session_state.indicator_manager.get_indicator(st.session_state.current_indicator_id)
+ # 创建新指标
+ new_indicator = st.session_state.indicator_manager.add_indicator(name=new_name)
+ st.session_state.current_indicator_id = new_indicator.id
+ # 清除已生成的代码
+ if "generated_code" in st.session_state:
+ del st.session_state.generated_code
+ # 清除第二个expander的状态
+ st.session_state.view_indicator_detail = False
+ save_session_state()
+ st.rerun()
+ except ValueError as e:
+ st.error(str(e))
+
+
+def render_clear_indicators_button():
+ """渲染清空指标按钮"""
+ if st.button("清空", help="删除所有指标", type="secondary"):
+ st.session_state.indicator_manager.clear_all_indicators()
+ st.session_state.current_indicator_id = None
+ # 清除已生成的代码
+ if "generated_code" in st.session_state:
+ del st.session_state.generated_code
+ # 清除第二个expander的状态
+ st.session_state.view_indicator_detail = False
+ save_session_state()
+ st.rerun()
+
+
+def render_indicator_list():
+ """渲染指标列表"""
+ indicators = st.session_state.indicator_manager.get_all_indicators()
+ if not indicators:
+ st.info("暂无指标,请点击\"新建指标\"按钮创建")
+ else:
+ for indicator in sorted(indicators, key=lambda x: x.created_at):
+ if st.button(
+ indicator.name,
+ key=f"btn_{indicator.id}",
+ use_container_width=True,
+ type="primary" if st.session_state.current_indicator_id == indicator.id else "secondary"
+ ):
+ st.session_state.current_indicator_id = indicator.id
+ # 更新显示的代码
+ if indicator.code:
+ st.session_state.generated_code = indicator.code
+ elif "generated_code" in st.session_state:
+ # 如果当前指标没有代码,清除session中的代码
+ del st.session_state.generated_code
+ # 清除第二个expander的状态
+ st.session_state.view_indicator_detail = False
+ save_session_state()
+ st.rerun()
+
+
+def render_indicator_library_expander():
+ """渲染指标库浏览扩展面板"""
+ with st.expander("查看指标库", expanded=False):
+ # 设置默认文件路径
+ file_path = os.path.join(DATA_DIR, "code.jsonl")
- if current_indicator:
- # 指标名称和保存按钮
- col1, col2, col3 = st.columns([1, 4, 2])
-
- with col1:
- st.markdown('指标名称:
', unsafe_allow_html=True)
-
- with col2:
- new_name = st.text_input("指标名称", value=current_indicator.name, key="indicator_name", label_visibility="collapsed")
- if new_name != current_indicator.name:
- if not new_name.strip():
- st.error("指标名称不能为空")
- else:
- try:
- st.session_state.indicator_manager.update_indicator(
- current_indicator.id, name=new_name
- )
- except ValueError as e:
- st.error(str(e))
-
- with col3:
- if st.button("💾 保存指标", use_container_width=True):
- if not new_name.strip():
- st.error("指标名称不能为空")
- elif not current_indicator.query.strip():
- st.error("请先输入查询语句")
- elif not current_indicator.code:
- st.error("请先生成指标代码")
- else:
- try:
- # 保存到指标管理器
- st.session_state.indicator_manager.save_indicators()
-
- # 同时保存到指标库
- library_path = os.path.join(DATA_DIR, "code.jsonl")
- new_record = {
- "name": current_indicator.name,
- "query": current_indicator.query,
- "code": current_indicator.code,
- "created_at": time.time()
- }
-
- # 追加到文件
- with open(library_path, "a", encoding="utf-8") as f:
- f.write(json.dumps(new_record, ensure_ascii=False) + "\n")
-
- # 清除指标库的缓存,以便重新加载
- load_jsonl.clear()
-
- st.toast("指标已保存")
- st.rerun() # 刷新页面以更新指标库列表
- except Exception as e:
- st.error(f"保存失败: {str(e)}")
-
- # 查询语句
- title_col, test_btn_col, result_col = st.columns([1, 1, 4])
- with title_col:
- st.subheader("查询语句")
- with result_col:
- title_query_result = st.subheader("")
-
- # 查询输入框
- query = st.text_area(
- "输入查询语句",
- value=current_indicator.query,
- height=70,
- key="query_input",
- placeholder="在此输入查询语句...",
- label_visibility="collapsed"
- )
-
- if query != current_indicator.query:
- st.session_state.indicator_manager.update_indicator(
- current_indicator.id, query=query
- )
-
- # 测试按钮改为生成指标代码按钮
- with test_btn_col:
- if st.button("🔄 生成代码", key="generate_code_btn", use_container_width=True):
- with st.spinner("正在生成指标代码..."):
- # 使用缓存的测试函数
- result = generate_indicator_code(query)
-
- # 根据结果显示不同的状态
- if result.startswith("✅"):
- title_query_result.success(result)
- # 生成成功后,立即保存代码到指标对象中
- if "generated_code" in st.session_state:
- st.session_state.indicator_manager.update_indicator(
- current_indicator.id,
- code=st.session_state.generated_code
- )
- elif result.startswith("❌"):
- title_query_result.error(result)
- else:
- # 显示实体识别结果或其他信息
- title_query_result.info(result)
-
- # 添加代码执行部分
- title_col, run_btn_col, result_col = st.columns([1, 1, 4])
- with title_col:
- st.subheader("指标代码")
- with result_col:
- title_code_result = st.subheader("")
- with run_btn_col:
- if st.button("执行代码", key="execute_code_btn", use_container_width=True):
- with st.spinner("正在执行代码..."):
- if "code_executor" in st.session_state and "generated_code" in st.session_state:
- result = st.session_state.code_executor.execute_code(st.session_state.generated_code)
- if result and hasattr(result, 'get') and result.get('status'):
- title_code_result.success("执行成功")
- title_code_result.json(result.get('data', {}))
- # 更新指标的代码
- st.session_state.indicator_manager.update_indicator(
- current_indicator.id,
- code=st.session_state.generated_code
- )
- else:
- title_code_result.error(f"执行失败: {result.get('message', '未知错误') if hasattr(result, 'get') else str(result)}")
- else:
- title_code_result.error("代码执行器未初始化或没有生成的代码")
-
- # 显示代码
- st.code(st.session_state.get("generated_code", "# 暂无代码,请输入有效的查询语句并点击测试按钮"), language="python")
-
- # 移除保存到指标库按钮部分
+ # 检查文件是否存在
+ if not os.path.exists(file_path):
+ st.warning(f"文件不存在: {file_path}")
else:
- st.warning("找不到当前指标,可能已被删除")
+ # 加载JSONL文件
+ records = load_jsonl(file_path)
+
+ if not records:
+ st.warning("没有找到有效的指标")
+ else:
+ # 初始化会话状态
+ if "selected_index" not in st.session_state:
+ st.session_state.selected_index = 0
+
+ # 显示指标列表
+ render_library_indicator_list(records)
+
+
+@st.cache_data
+def load_jsonl(file_path):
+ """加载JSONL文件并返回JSON指标列表"""
+ records = []
+ try:
+ with open(file_path, "r", encoding="utf-8") as file:
+ for line in file:
+ if line.strip():
+ records.append(json.loads(line))
+ return records
+ except Exception as e:
+ st.error(f"加载文件失败: {str(e)}")
+ return []
+
+
+def render_library_indicator_list(records):
+ """渲染指标库列表"""
+ st.subheader("指标列表")
+ for i, record in enumerate(records):
+ if st.button(record.get("name", f"指标 {i+1}"), key=f"lib_btn_{i}", use_container_width=True):
+ st.session_state.selected_index = i
+ # 清除第一个expander的选中状态
+ st.session_state.current_indicator_id = None
+ # 设置查看指标详情的逻辑
+ st.session_state.view_indicator_detail = True
+ st.session_state.current_library_indicator = record
+ save_session_state()
+ st.rerun()
+
+
+def render_main_area():
+ """渲染主区域内容"""
+ if "view_indicator_detail" in st.session_state and st.session_state.view_indicator_detail and "current_library_indicator" in st.session_state:
+ render_library_indicator_detail()
+ elif st.session_state.current_indicator_id is not None:
+ render_current_indicator_detail()
else:
st.info("请在左侧创建或选择一个指标")
+def render_library_indicator_detail():
+ """渲染指标库详情"""
+ library_indicator = st.session_state.current_library_indicator
+
+ # 指标名称和操作按钮
+ col1, col2, col3 = st.columns([1, 4, 2])
+ with col1:
+ st.markdown('指标名称:
', unsafe_allow_html=True)
+ with col2:
+ st.markdown(f'{library_indicator.get("name", "未命名指标")}
', unsafe_allow_html=True)
+
+
+ # 查询语句部分
+ title_col, run_btn_col, result_col = st.columns([1, 1, 4])
+ with title_col:
+ st.subheader("查询语句")
+ with result_col:
+ title_query_result = st.subheader("")
+
+ # 显示查询语句
+ st.text_area(
+ "查询语句",
+ value=library_indicator.get('query', '无查询语句'),
+ height=70,
+ key="lib_query_display",
+ disabled=True,
+ label_visibility="collapsed"
+ )
+
+ # 指标代码部分
+ render_library_indicator_code(library_indicator)
+
+
+def render_library_indicator_code(library_indicator):
+ """渲染指标库代码部分"""
+ title_col, run_btn_col, result_col = st.columns([1, 1, 4])
+ with title_col:
+ st.subheader("指标代码")
+ with result_col:
+ title_code_result = st.subheader("")
+ with run_btn_col:
+ if st.button("执行代码", key="execute_lib_indicator", use_container_width=True):
+ execute_indicator_code(library_indicator.get('code', ''), title_code_result)
+
+ # 显示代码
+ st.code(library_indicator.get('code', '无代码'), language='python')
+
+
+def render_current_indicator_detail():
+ """渲染当前指标详情"""
+ current_indicator = st.session_state.indicator_manager.get_indicator(st.session_state.current_indicator_id)
+
+ if current_indicator:
+ # 指标名称和保存按钮
+ render_indicator_header(current_indicator)
+
+ # 查询语句
+ render_query_input_section(current_indicator)
+
+ # 代码执行部分
+ render_code_execution_section(current_indicator)
+ else:
+ st.warning("找不到当前指标,可能已被删除")
+
+
+def render_indicator_header(current_indicator):
+ """渲染指标头部(名称和保存按钮)"""
+ col1, col2, col3 = st.columns([1, 4, 2])
+
+ with col1:
+ st.markdown('指标名称:
', unsafe_allow_html=True)
+
+ with col2:
+ new_name = st.text_input("指标名称", value=current_indicator.name, key="indicator_name", label_visibility="collapsed")
+ if new_name != current_indicator.name:
+ if new_name is None or not new_name.strip():
+ st.error("指标名称不能为空")
+ else:
+ try:
+ st.session_state.indicator_manager.update_indicator(
+ current_indicator.id, name=new_name
+ )
+ except ValueError as e:
+ st.error(str(e))
+
+ with col3:
+ render_save_indicator_button(current_indicator, new_name)
+
+
+def render_save_indicator_button(current_indicator, new_name):
+ """渲染保存指标按钮"""
+ if st.button("💾 保存指标", use_container_width=True):
+ if new_name is None or not new_name.strip():
+ st.error("指标名称不能为空")
+ elif not current_indicator.query.strip():
+ st.error("请先输入查询语句")
+ elif not current_indicator.code:
+ st.error("请先生成指标代码")
+ else:
+ try:
+ save_indicator_to_library(current_indicator)
+ st.toast("指标已保存")
+ st.rerun() # 刷新页面以更新指标库列表
+ except Exception as e:
+ st.error(f"保存失败: {str(e)}")
+
+
+def save_indicator_to_library(current_indicator):
+ """保存指标到指标库"""
+ # 保存到指标管理器
+ st.session_state.indicator_manager.save_indicators()
+
+ # 同时保存到指标库
+ library_path = os.path.join(DATA_DIR, "code.jsonl")
+ new_record = {
+ "name": current_indicator.name,
+ "query": current_indicator.query,
+ "code": current_indicator.code,
+ "created_at": time.time()
+ }
+
+ # 追加到文件
+ with open(library_path, "a", encoding="utf-8") as f:
+ f.write(json.dumps(new_record, ensure_ascii=False) + "\n")
+
+ # 清除指标库的缓存,以便重新加载
+ load_jsonl.clear()
+
+
+def render_query_input_section(current_indicator):
+ """渲染查询输入部分"""
+ title_col, test_btn_col, result_col = st.columns([1, 1, 4])
+ with title_col:
+ st.subheader("查询语句")
+ with result_col:
+ title_query_result = st.subheader("")
+
+ # 查询输入框
+ query = st.text_area(
+ "输入查询语句",
+ value=current_indicator.query,
+ height=70,
+ key="query_input",
+ placeholder="在此输入查询语句...",
+ label_visibility="collapsed"
+ )
+
+ # 应用自动补全
+ apply_autocomplete_to_query_input()
+
+ if query != current_indicator.query:
+ st.session_state.indicator_manager.update_indicator(
+ current_indicator.id, query=query
+ )
+
+ # 生成代码按钮
+ with test_btn_col:
+ render_generate_code_button(query, title_query_result, current_indicator)
+
+
+def render_generate_code_button(query, title_query_result, current_indicator):
+ """渲染生成代码按钮"""
+ if st.button("🔄 生成代码", key="generate_code_btn", use_container_width=True):
+ with st.spinner("正在生成指标代码..."):
+ # 使用缓存的测试函数
+ result = generate_indicator_code(query)
+
+ # 根据结果显示不同的状态
+ if result.startswith("✅"):
+ title_query_result.success(result)
+ # 生成成功后,立即保存代码到指标对象中
+ if "generated_code" in st.session_state:
+ st.session_state.indicator_manager.update_indicator(
+ current_indicator.id,
+ code=st.session_state.generated_code
+ )
+ elif result.startswith("❌"):
+ title_query_result.error(result)
+ else:
+ # 显示实体识别结果或其他信息
+ title_query_result.info(result)
+
+
+def render_code_execution_section(current_indicator):
+ """渲染代码执行部分"""
+ title_col, run_btn_col, result_col = st.columns([1, 1, 4])
+ with title_col:
+ st.subheader("指标代码")
+ with result_col:
+ title_code_result = st.subheader("")
+ with run_btn_col:
+ if st.button("执行代码", key="execute_code_btn", use_container_width=True):
+ with st.spinner("正在执行代码..."):
+ if "code_executor" in st.session_state and "generated_code" in st.session_state:
+ execute_and_update_code(current_indicator, title_code_result)
+ else:
+ title_code_result.error("代码执行器未初始化或没有生成的代码")
+
+ # 显示代码
+ st.code(st.session_state.get("generated_code", "# 暂无代码,请输入有效的查询语句并点击测试按钮"), language="python")
+
+
+def execute_and_update_code(current_indicator, title_code_result):
+ """执行并更新代码"""
+ result = st.session_state.code_executor.execute_code(st.session_state.generated_code)
+ if result and hasattr(result, 'get') and result.get('status'):
+ title_code_result.success("执行成功")
+ title_code_result.json(result.get('data', {}))
+ # 更新指标的代码
+ st.session_state.indicator_manager.update_indicator(
+ current_indicator.id,
+ code=st.session_state.generated_code
+ )
+ else:
+ title_code_result.error(f"执行失败: {result.get('message', '未知错误') if hasattr(result, 'get') else str(result)}")
+
+
+def execute_indicator_code(code, result_container):
+ """执行指标代码"""
+ with st.spinner('正在执行代码...'):
+ # 确保code_executor已初始化
+ if "code_executor" in st.session_state:
+ result = st.session_state.code_executor.execute_code(code)
+ if result and hasattr(result, 'get') and result.get('status'):
+ result_container.success("执行成功")
+ result_container.json(result.get('data', {}))
+ else:
+ result_container.error(f"执行失败: {result.get('message', '未知错误') if hasattr(result, 'get') else str(result)}")
+ else:
+ result_container.error("代码执行器未初始化")
+
+
if __name__ == "__main__":
main()
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 78d697e..37fc6bf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,5 @@ pyyaml
neo4j
langchain-neo4j
langgraph
-chardet
\ No newline at end of file
+chardet
+streamlit-textcomplete
\ No newline at end of file
diff --git a/src/code_executor.py b/src/code_executor.py
index 533a4d2..8961c7f 100644
--- a/src/code_executor.py
+++ b/src/code_executor.py
@@ -123,8 +123,8 @@ class CodeExecutor:
sys.stdout = old_stdout
output = redirected_output.getvalue().strip()
- if not isinstance(result_dict, dict) or len(result_dict) != 4:
- raise ValueError("函数应返回包含4个元素的字典(status, data, error, helper_info)")
+ if not isinstance(result_dict, dict) or len(result_dict) < 3:
+ raise ValueError("函数应返回至少包含3个元素的字典(status, message, data)")
logger.debug(f"执行结果: {result_dict}")
diff --git a/src/project.py b/src/project.py
index b759691..7b28cd0 100644
--- a/src/project.py
+++ b/src/project.py
@@ -17,8 +17,6 @@ class ProjectToolkit(ABC):
def __init__(self, config: Any):
pass
- # 项目划分查询方法
-
@abstractmethod
def get_division_by_name(self, name_part):
"""
@@ -71,7 +69,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 工程量查询方法
@abstractmethod
def get_quantities_by_paths(self, paths_str):
"""
@@ -128,7 +125,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 材机查询方法
@abstractmethod
def get_material_equipment_by_path(self, paths_str):
"""
@@ -164,7 +160,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 取费查询方法
@abstractmethod
def get_fee_template_by_path(self, paths_str):
"""
@@ -202,7 +197,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 费用表查询方法
@abstractmethod
def get_fee_schedule_on_auxiliary_expense_table(self, table_name, fee_name, fee_attribute: str):
"""
@@ -298,7 +292,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 工程属性查询方法
@abstractmethod
def get_project_property(self, property_name):
"""
@@ -316,7 +309,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 项目划分查找取费表
@abstractmethod
def get_fee_table_by_project_division(self, project_division_path, fee_name):
"""
@@ -335,7 +327,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 清单查找取费表
@abstractmethod
def get_fee_table_by_list(self, parent_path, list_code, list_unit, fee_name):
"""
@@ -356,7 +347,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 定额节点查找合价
@abstractmethod
def get_fee_table_by_quoto_code(self, parent_path, quantity_type, code, fee_name):
"""
@@ -377,7 +367,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 工程量节点查找合价
@abstractmethod
def get_fee_table_by_quantities_name(self, parent_path, quantity_type, quantity_name, fee_name):
"""
@@ -398,7 +387,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 材机节点查找市场价
@abstractmethod
def get_fee_by_material_equipment_code(self, parent_path, material_equipment_code, fee_name):
"""
@@ -418,7 +406,6 @@ class ProjectToolkit(ABC):
"""
pass
- # 查找清单节点
@abstractmethod
def get_fee_table_by_list_name(self, parent_path, list_code, unit):
"""
diff --git a/src/prompt_manager.py b/src/prompt_manager.py
index bbfd7bb..83381e0 100644
--- a/src/prompt_manager.py
+++ b/src/prompt_manager.py
@@ -66,19 +66,31 @@ def project_get_calculate_function():
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', [])
+ quantity = 0.0
+ if isinstance(result_dict, dict) and '数量' in result_dict:
+ quantity = result_dict['数量']
+
if status:
- return result_dict
+ return {
+ "code": 200,
+ "message": 'ok',
+ "status": True,
+ "data": quantity
+ }
else:
- return result_dict
+ return {
+ "code": 500,
+ "message": message,
+ "status": False,
+ "data": quantity
+ }
# 执行规则
- 参数必须从用户问题或上下文信息中提取。
- 在代码函数内部生成功能说明,在函数外禁止生成任何注释或解释或非代码内容。
-- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数,该函数成功时返回的'data'通常是浮点或整型值,除非用户要求返回其他类型。
+- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数,该函数返回字典包含:'code'、'status'、'message'、'data'四个字段。
- 必须确保生成的代码可以直接执行,代码要注意进行各类容错检查。
-- 如果project_get_calculate_function函数需要返回浮点整形数,函数中间发生错误或找不到对象也必须返回成功,data为0,并在message说明错误原因。
+- 'data'字段通常要求是浮点或整型值,除非用户要求返回其他类型,同时函数执行过程中发生错误,'data'字段也必须为0,并在message说明错误原因。
- ProjectToolkit 类中涉及项目划分的函数已考虑在其及其子孙项目划分下查找,所以无需生成递归子项目划分的代码。
- 如果文本中包含范围编码格式则需要进行编码展开,如'YX2-1~7'展开为‘YX2-1/YX2-2/YX2-3/YX2-4/YX2-5/YX2-6/YX2-7’
"""
diff --git a/tests/test_runcode.py b/tests/test_runcode.py
index a476882..2129122 100644
--- a/tests/test_runcode.py
+++ b/tests/test_runcode.py
@@ -73,7 +73,7 @@ def project_get_calculate_function():
output = redirected_output.getvalue().strip()
if not isinstance(result_dict, dict) or len(result_dict) != 4:
- raise ValueError("函数应返回包含4个元素的字典(status, data, error, helper_info)")
+ raise ValueError("函数应返回包含4个元素的字典(code, status, message, data)")
logger.debug(f"执行结果: {result_dict}")
diff --git a/tests/test_user_interaction.py b/tests/test_user_interaction.py
deleted file mode 100644
index 38fd1b1..0000000
--- a/tests/test_user_interaction.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# 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()
diff --git a/tests/zhibiao.json b/tests/zhibiao.json
index 13cda14..201556a 100644
--- a/tests/zhibiao.json
+++ b/tests/zhibiao.json
@@ -169,7 +169,7 @@
"指标类型": "工程量指标",
"数据来源": "定额数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/基础工程】及其子孙项目划分中查找编码中包含【YX2-1~7】的所有【定额】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/基础工程】下编码包含【YX2-1~7】的所有【定额】的【数量】之和",
"映射规则": "YX2-1~7"
}
},
@@ -199,7 +199,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢】的所有【主材】的【数量】之和",
"映射规则": "角钢"
}
},
@@ -213,7 +213,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢、高强】的所有【主材】的【数量】之和",
"映射规则": "角钢、高强"
}
},
@@ -243,7 +243,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢】的所有【主材】的【单价】之和",
"映射规则": "角钢"
}
},
@@ -273,7 +273,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【角钢、高强】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【角钢、高强】的所有【主材】的【单价】之和",
"映射规则": "角钢、高强"
}
},
@@ -303,7 +303,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管塔】的所有【主材】的【数量】之和",
"映射规则": "钢管塔"
}
},
@@ -333,7 +333,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管塔】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管塔】的所有【主材】的【单价】之和",
"映射规则": "钢管塔"
}
},
@@ -363,7 +363,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管杆】的所有【主材】的【数量】之和",
"映射规则": "钢管杆"
}
},
@@ -393,7 +393,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及其子孙项目划分中查找名称中包含【钢管杆】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程/杆塔组立/铁塔、钢管杆组立】及名称包含【钢管杆】的所有【主材】的【单价】之和",
"映射规则": "钢管杆"
}
},
@@ -473,7 +473,7 @@
"指标类型": "技术指标",
"数据来源": "定额参数",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程】及其子孙项目划分中查找编码中包含【['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']】的所有【定额】的【参数】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程】下编码包含【['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|-------|-------|\n| YX5-67~69、YX5-78~79 | 8 |\n| YX5-59~66、YX5-74~77 | 6 |\n| YX5-52~58、YX5-70~73 | 4 |\n| YX5-14~17、YX5-43~51 | 2 |\n| YX5-10~13、YX5-38~42、YX5-8~9 | 1 |"
}
},
@@ -519,7 +519,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【高导电率】的所有【主材】的【数量】之和",
"映射规则": "高导电率"
}
},
@@ -549,7 +549,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【线】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【线】的所有【主材】的【单价】之和",
"映射规则": "线"
}
},
@@ -579,7 +579,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及其子孙项目划分中查找名称中包含【高导电率】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设】及名称包含【高导电率】的所有【主材】的【单价】之和",
"映射规则": "高导电率"
}
},
@@ -593,7 +593,7 @@
"指标类型": "技术指标",
"数据来源": "主材参数",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设@@架空输电线路本体工程/架线工程/其他架线工程】及其子孙项目划分中查找名称中包含【['钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', '扩径导线', '耐热导线', '碳纤维导线']】的所有【主材】的【参数】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程/导地线架设@@架空输电线路本体工程/架线工程/导地线跨越架设@@架空输电线路本体工程/架线工程/其他架线工程】及名称包含【['钢芯铝绞线', '铝包钢芯铝绞线', '中强度铝合金绞线', '铝合金芯铝绞线', '铝合金芯高导电率铝绞线', '钢芯高导电率铝绞线', '特高强度钢芯铝合金绞线', '扩径导线', '耐热导线', '碳纤维导线']】的所有【主材】的【参数】之和",
"映射规则": "| 资源识别规则 | 指标值 |\n|-------|-------|\n| 钢芯铝绞线 | 钢芯铝绞线 |\n| 铝包钢芯铝绞线 | 铝包钢芯铝绞线 |\n| 中强度铝合金绞线 | 中强度铝合金绞线 |\n| 铝合金芯铝绞线 | 铝合金芯铝绞线 |\n| 铝合金芯高导电率铝绞线 | 铝合金芯高导电率铝绞线 |\n| 钢芯高导电率铝绞线 | 钢芯高导电率铝绞线 |\n| 特高强度钢芯铝合金绞线 | 特高强度钢芯铝合金绞线 |\n| 扩径导线 | 扩径导线 |\n| 耐热导线 | 耐热导线 |\n| 碳纤维导线 | 碳纤维导线 |"
}
},
@@ -1431,7 +1431,7 @@
"指标类型": "工程量指标",
"数据来源": "主材数量",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【数量】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/基础工程/基础砌筑】及名称包含【圆钢】的所有【主材】的【数量】之和",
"映射规则": "圆钢"
}
},
@@ -1445,7 +1445,7 @@
"指标类型": "单价指标",
"数据来源": "主材单价",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/基础工程/基础砌筑】及其子孙项目划分中查找名称中包含【圆钢】的所有【主材】的【单价】之和",
+ "指标映射": "获取项目划分【架空输电线路本体工程/基础工程/基础砌筑】及名称包含【圆钢】的所有【主材】的【单价】之和",
"映射规则": "圆钢"
}
},
@@ -1879,7 +1879,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【架空输电线路本体工程】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【架空输电线路本体工程】的【合计费】的值",
"映射规则": "@工程费用.架空输电线路本体工程.合计费"
}
},
@@ -1893,7 +1893,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【人工费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【人工费】的费用",
"映射规则": "人工费"
}
},
@@ -1907,7 +1907,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【人工费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【人工费】的费用",
"映射规则": "人工费"
}
},
@@ -1921,7 +1921,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【施工机械使用费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【施工机械使用费】的费用",
"映射规则": "施工机械使用费"
}
},
@@ -1935,7 +1935,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【施工机械使用费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【施工机械使用费】的费用",
"映射规则": "施工机械使用费"
}
},
@@ -1949,7 +1949,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/基础工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/基础工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -1963,7 +1963,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/杆塔工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/杆塔工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -1977,7 +1977,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/接地工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/接地工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -1991,7 +1991,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/架线工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/架线工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -2005,7 +2005,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/附件安装工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/附件安装工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -2019,7 +2019,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/辅助工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/辅助工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -2033,7 +2033,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程/辅助工程】项目划分中获取取费费用名称属于【合计】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程/辅助工程】下取费名称等于【合计】的费用",
"映射规则": "合计"
}
},
@@ -2047,7 +2047,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【辅助设施工程】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【辅助设施工程】的【合计费】的值",
"映射规则": "@工程费用.辅助设施工程.合计费"
}
},
@@ -2061,7 +2061,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【其他费用】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【其他费用】的【合计费】的值",
"映射规则": "@工程费用.其他费用.合计费"
}
},
@@ -2075,7 +2075,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【建设场地征用及清理费】的属性",
+ "指标映射": "从【其他费用】中获取【建设场地征用及清理费】的值",
"映射规则": "@其他费用.建设场地征用及清理费"
}
},
@@ -2089,7 +2089,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【项目建设管理费】的属性",
+ "指标映射": "从【其他费用】中获取【项目建设管理费】的值",
"映射规则": "@其他费用.项目建设管理费"
}
},
@@ -2103,7 +2103,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【工程监理费】的属性",
+ "指标映射": "从【其他费用】中获取【工程监理费】的值",
"映射规则": "@其他费用.工程监理费"
}
},
@@ -2117,7 +2117,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【项目建设技术服务费】的属性",
+ "指标映射": "从【其他费用】中获取【项目建设技术服务费】的值",
"映射规则": "@其他费用.项目建设技术服务费"
}
},
@@ -2131,7 +2131,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【项目前期工作费】的属性",
+ "指标映射": "从【其他费用】中获取【项目前期工作费】的值",
"映射规则": "@其他费用.项目前期工作费"
}
},
@@ -2145,7 +2145,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【勘察费】的属性",
+ "指标映射": "从【其他费用】中获取【勘察费】的值",
"映射规则": "@其他费用.勘察费"
}
},
@@ -2159,7 +2159,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【设计费】的属性",
+ "指标映射": "从【其他费用】中获取【设计费】的值",
"映射规则": "@其他费用.设计费"
}
},
@@ -2173,7 +2173,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【工程建设检测费】的属性",
+ "指标映射": "从【其他费用】中获取【工程建设检测费】的值",
"映射规则": "@其他费用.工程建设检测费"
}
},
@@ -2187,7 +2187,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【其他费用】中获取【生产准备费】的属性",
+ "指标映射": "从【其他费用】中获取【生产准备费】的值",
"映射规则": "@其他费用.生产准备费"
}
},
@@ -2201,7 +2201,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【安全文明施工费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【安全文明施工费】的费用",
"映射规则": "安全文明施工费"
}
},
@@ -2215,7 +2215,7 @@
"指标类型": "造价指标",
"数据来源": "项目划分费用",
"指标描述": {
- "指标映射": "从【架空输电线路本体工程】项目划分中获取取费费用名称属于【安全文明施工费】的费用",
+ "指标映射": "获取项目划分【架空输电线路本体工程】下取费名称等于【安全文明施工费】的费用",
"映射规则": "安全文明施工费"
}
},
@@ -2229,7 +2229,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【基本预备费】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【基本预备费】的【合计费】的值",
"映射规则": "@工程费用.基本预备费.合计费"
}
},
@@ -2243,7 +2243,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【工程静态投资(一~七项合计)】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【工程静态投资(一~七项合计)】的【合计费】的值",
"映射规则": "@工程费用.工程静态投资(一~七项合计).合计费"
}
},
@@ -2257,7 +2257,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【建设期贷款利息】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【建设期贷款利息】的【合计费】的值",
"映射规则": "@工程费用.建设期贷款利息.合计费"
}
},
@@ -2271,7 +2271,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【工程动态投资(一~八项合计)】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【工程动态投资(一~八项合计)】的【合计费】的值",
"映射规则": "@工程费用.工程动态投资(一~八项合计).合计费"
}
},
@@ -2285,7 +2285,7 @@
"指标类型": "造价指标",
"数据来源": "工程费用",
"指标描述": {
- "指标映射": "从【工程费用】中获取【其中:可抵扣增值税额】的【合计费】的属性",
+ "指标映射": "从【工程费用】中获取【其中:可抵扣增值税额】的【合计费】的值",
"映射规则": "@工程费用.其中:可抵扣增值税额.合计费"
}
}
diff --git a/tools/xml_to_json.py b/tools/xml_to_json.py
index af37f19..03f7d59 100644
--- a/tools/xml_to_json.py
+++ b/tools/xml_to_json.py
@@ -145,7 +145,7 @@ def xml_to_json(xml_content, output_path):
result.append(base_item)
elif data_sources in project_division:
- mapping_desc = f"从【{index_extraction_scope}】项目划分中获取取费费用名称属于【{keyword}】的费用"
+ mapping_desc = f"获取项目划分【{index_extraction_scope}】下取费名称等于【{keyword}】的费用"
base_item["指标描述"] = {
"指标映射": mapping_desc,
"映射规则": parsed["映射规则"]
@@ -162,11 +162,11 @@ def xml_to_json(xml_content, output_path):
# 取数据来源的开头两个字(如"定额"、"清单"等)
temp3 = data_sources[0:2]
if temp3 in ["清单", "定额", "人材机"]:
- mapping_desc = f"从【{index_extraction_scope}】及其子孙项目划分中查找编码中包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
+ mapping_desc = f"获取项目划分【{index_extraction_scope}】下编码包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
elif temp3 in ["主材", "设备"]:
- mapping_desc = f"从【{index_extraction_scope}】及其子孙项目划分中查找名称中包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
+ mapping_desc = f"获取项目划分【{index_extraction_scope}】及名称包含【{temp0}】的所有【{temp3}】的【{temp2}】之和"
else:
- mapping_desc = f"从【{data_sources}】中获取{temp0}的属性"
+ mapping_desc = f"从【{data_sources}】中获取{temp0}的值"
base_item["指标描述"] = {
"指标映射": mapping_desc,
@@ -221,6 +221,6 @@ def xml_to_json(xml_content, output_path):
return "结果已保存"
xml_content = read_xml_as_string('dataset/主网架空线路造价分析指标.xml')
-json_output = xml_to_json(xml_content, output_path= "./tests/zhibiao.json")
+json_output = xml_to_json(xml_content, output_path= "./data/zhibiao.json")
print("转换完毕!")