From 32136be5db2c22b1b8d983b79087ca8fd8fe509a Mon Sep 17 00:00:00 2001 From: paituo <330435863@qq.com> Date: Mon, 14 Jul 2025 15:36:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BEQA=E9=97=AE=E7=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yaml | 23 +++++ main_streamlt.py | 23 ++++- main_streamlt_qa.py | 98 +++++++++++++-------- src/config.py | 4 + src/engineeringqaagent.py | 169 +++++++++++++++++++++++++++++++++++ src/prompt_manager.py | 181 +------------------------------------- 6 files changed, 283 insertions(+), 215 deletions(-) create mode 100644 src/engineeringqaagent.py diff --git a/config.yaml b/config.yaml index a3b4308..d48147d 100644 --- a/config.yaml +++ b/config.yaml @@ -34,6 +34,29 @@ openai: #model_name: Qwen/Qwen3-8B #model_name: deepseek-ai/DeepSeek-V3 +openai_qa: + temperature: 0 + api_keys: + - sk-gycpzfjfxldpmqmqvrtgtvtpusnsrscowhscqfbppmjdlzoi + - sk-lhvfliriyohnaspzuoddgulmdiovpbjcdmfdfdvghapprfru + - sk-tzxcxzberpdpvszhjfyikepjlgslbtgssypxpmwllgithtwz + - sk-ybhbyjkzdydsllxredzntqdyrderxighdieiayudcubvhxsw + - sk-xtjzktvgrkzikkvetzygzozfyjxzgasednowyjqjeudfqiha + - sk-ufjdohciujtmpfwrrqhiocufexdzqpiotnjwzhbumcpvkpcw + - sk-nvnuzkdgxwzvibtuymwhatfajnerekgsbieqmqadjaqyhkob + - sk-erdnqkazveyiaojrpojawtwlxpnrikhedurlphmyviaixyrg + - sk-xhddshwxrhejbzitozgieightaecdeozqzdgypmedzdydthr + - sk-czilnjaxqwicwoyvfaxgzvqcjhmdzpwvzhbxvnqnvaeocojk + + api_key: sk-xlrnesfcuwrpevdwbuhthivpygwyzwbxxsyvhzzwrkpzjduk + api_base: https://api.siliconflow.cn/v1 + #api_version: "" # 可选,某些API版本需要指定 + #organization: your_organization_id # 可选 + api_type: openai # 可选,默认为 openai;如果用 Azure 则为 azure + #model_name: Qwen/Qwen2.5-72B-Instruct + #model_name: Qwen/Qwen3-8B + model_name: deepseek-ai/DeepSeek-V3 + openai_coder: temperature: 0 api_keys: diff --git a/main_streamlt.py b/main_streamlt.py index 06a4bff..615c128 100644 --- a/main_streamlt.py +++ b/main_streamlt.py @@ -1,3 +1,24 @@ +import os + +#os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = 'http://10.1.6.34:4318' +#os.environ["OTEL_SERVICE_NAME"] = 'web/worker' +#os.environ["OTEL_TRACE_SAMPLING_RATIO"] = '1' + +import phoenix as px +from phoenix.otel import register +from openinference.instrumentation.langchain import LangChainInstrumentor + +# configure the Phoenix tracer +tracer_provider = register( + endpoint="http://10.1.6.34:6006/v1/traces", + project_name="streamlt-app", # Default is 'default' + auto_instrument=True, # See 'Trace all calls made to a library' below + verbose=True +) +#tracer = tracer_provider.get_tracer(__name__) +LangChainInstrumentor(tracer_provider=tracer_provider).instrument(skip_dep_check=False) + + import streamlit as st import json import os @@ -230,7 +251,7 @@ def load_session_state(): # 测试指标查询 -@st.cache_data(ttl=60) # 缓存结果1分钟 +@st.cache_data(ttl=5) # 缓存结果5分钟 def generate_indicator_code(query: str) -> str: """生成指标代码""" if not query.strip(): diff --git a/main_streamlt_qa.py b/main_streamlt_qa.py index a56efb5..dae8d9b 100644 --- a/main_streamlt_qa.py +++ b/main_streamlt_qa.py @@ -45,9 +45,40 @@ logger_names = ["httpx", "openai", "langsmith.client", "neo4j", "urllib3", "http for name in logger_names: setup_logger(name) +#from langfuse import Langfuse +#from langfuse.langchain import CallbackHandler + + from src.code_executor import CodeExecutor from src.config import Config +#langfuse = Langfuse( +# public_key="pk-lf-4c2b8e97-dd44-41be-bd35-a643cdc8d778", +# secret_key="sk-lf-d00b0882-cd91-4df7-b3a7-658d9d394568", +# host="http://10.1.6.34:3000" +#) + +#langfuse_handler = CallbackHandler() +langfuse_handler = None + +import os + +# Update this with your self-hosted endpoint +os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "http://10.1.6.34:6006/v1/traces" +import phoenix as px +from phoenix.otel import register +from openinference.instrumentation.langchain import LangChainInstrumentor + +# configure the Phoenix tracer +tracer_provider = register( + endpoint="http://10.1.6.34:6006/v1/traces", + project_name="my-llm-app", # Default is 'default' + auto_instrument=True, # See 'Trace all calls made to a library' below + verbose=True +) +#tracer = tracer_provider.get_tracer(__name__) +LangChainInstrumentor(tracer_provider=tracer_provider).instrument(skip_dep_check=False) + # 页面配置 st.set_page_config( page_title="博微造价工程数据问答系统", @@ -74,10 +105,13 @@ def initialize_components(): from src.document_loader import load_file from src.neo4j_raw_retriever import Neo4jRawRetriever from src.embedding_client import EmbeddingClient + from langchain_neo4j import Neo4jGraph + from src.engineeringqaagent import EngineeringQAAgent # 初始化LLM客户端 llm_client = MultiAPIKeyChatOpenAI(config.openai) llm_client_coder = MultiAPIKeyChatOpenAI(config.openai_coder) + llm_client_qa = MultiAPIKeyChatOpenAI(config.openai_qa) # 初始化提示词管理器 prompt_manager = PromptManager() @@ -99,6 +133,16 @@ def initialize_components(): # 初始化知识检索器 knowledge_retriever = Neo4jRawRetriever(config.neo4j_conf) + # 初始化Neo4jGraph + graph = Neo4jGraph( + url=config.neo4j_conf.get("uri"), + username=config.neo4j_conf.get("username"), + password=config.neo4j_conf.get("password") + ) + + # 初始化EngineeringQAAgent + qa_agent = EngineeringQAAgent(llm_client_qa, embedding_client, graph, None, callbacks=[langfuse_handler] if langfuse_handler else None) + # 初始化对话管理器 dialog_manager = DialogManager( llm_client, @@ -108,8 +152,10 @@ def initialize_components(): knowledge_retriever, prompt_manager, ) - st.session_state.dialog_manager = dialog_manager + st.session_state.dialog_manager = dialog_manager + st.session_state.qa_agent = qa_agent + print('初始化组件完成') return code_executor, config # 初始化组件 @@ -161,45 +207,25 @@ async def process_user_question(user_question): thinking_placeholder = st.empty() thinking_placeholder.markdown("🤔 思考中...") - # 理解用户问题并获取重写结果 + # 使用EngineeringQAAgent处理用户问题 try: - # 获取对话管理器 - dialog_manager = st.session_state.get('dialog_manager') - if not dialog_manager: - raise Exception("对话管理器未初始化") + # 获取QA代理 + qa_agent = st.session_state.get('qa_agent') + if not qa_agent: + raise Exception("问答代理未初始化") - rewritten_results = await dialog_manager.understand_user_question_stream(user_question) - if not rewritten_results: - thinking_placeholder.markdown("抱歉,没有找到符合要求的数据,请尝试其他问题。") - st.session_state.messages.append({"role": "assistant", "content": "抱歉,没有找到符合要求的数据,请尝试其他问题。"}) - st.session_state.processing = False - return + # 使用EngineeringQAAgent处理问题 + answer = qa_agent.ask(user_question) - # 保存重写结果到会话状态 - st.session_state.rewritten_results = rewritten_results + import re + + cleaned_answer = re.sub(r'`{3}', '', answer, flags=re.DOTALL) # 跨行匹配 + + # 显示回答 + thinking_placeholder.markdown(cleaned_answer) - # 显示选择项 - thinking_placeholder.empty() - result_container = st.container() - with result_container: - st.markdown("**系统为您理解并改写了以下访问请求,请选择:**") - - # 为每个重写结果创建按钮 - cols = st.columns(min(len(rewritten_results), 3)) - for idx, (rewritten, knowledge) in enumerate(rewritten_results): - col_idx = idx % 3 - with cols[col_idx]: - if st.button(f"选项 {idx+1}: {rewritten[:50]}...", key=f"option_{idx}"): - st.session_state.selected_rewritten = rewritten - st.session_state.selected_knowledge = knowledge - st.rerun() - - # 将选择提示添加到会话历史 - options_text = "**系统为您理解并改写了以下访问请求,请选择:**\n\n" - for idx, (rewritten, _) in enumerate(rewritten_results): - options_text += f"{idx+1}. {rewritten}\n" - - st.session_state.messages.append({"role": "assistant", "content": options_text}) + # 将回答添加到会话历史 + st.session_state.messages.append({"role": "assistant", "content": answer}) except Exception as e: thinking_placeholder.markdown(f"处理问题时出错: {str(e)}") diff --git a/src/config.py b/src/config.py index 188f0e1..4b73ea5 100644 --- a/src/config.py +++ b/src/config.py @@ -21,6 +21,10 @@ class Config: def openai_coder(self): return self._config.get("openai_coder", {}) + @property + def openai_qa(self): + return self._config.get("openai_qa", {}) + @property def bowei_api_docs_path(self): return self._config.get("bowei_api_docs_path", "./data/bowei_api_docs.md") diff --git a/src/engineeringqaagent.py b/src/engineeringqaagent.py new file mode 100644 index 0000000..1cdee1e --- /dev/null +++ b/src/engineeringqaagent.py @@ -0,0 +1,169 @@ +from langchain_neo4j import GraphCypherQAChain, Neo4jGraph +from langchain.chains import RetrievalQA, LLMChain +from langchain_core.runnables import RunnableSequence +from langchain.agents import initialize_agent, Tool +from langchain.agents import AgentType +from langchain.memory import ConversationBufferMemory +from langchain_community.chat_message_histories import ChatMessageHistory +from langchain_community.chat_models import ChatOpenAI +from langchain.prompts import PromptTemplate +from typing import Any + +class EngineeringQAAgent: + def __init__(self, llm_client, embedding_client, graph: Neo4jGraph, business_retriever, callbacks=None): + """ + :param llm_client: 聊天大模型实例,如 ChatOpenAI + :param embedding_client: 嵌入模型实例,如 OpenAIEmbeddings(本类未直接使用,但保留) + :param graph: Neo4jGraph实例,连接工程知识图谱 + :param business_retriever: 通用业务知识库的Retriever对象 + """ + self.llm = llm_client.llm + self.embedding = embedding_client + self.business_retriever = business_retriever + self.callbacks = callbacks + self.kwargs = {} + + if self.callbacks: + self.kwargs = {"callbacks": self.callbacks } + + CYPHER_GENERATION_TEMPLATE = """Task:Generate Cypher statement to query a graph database. +Instructions: +Use only the provided relationship types and properties in the schema. +Do not use any other relationship types or properties that are not provided. +Schema: +{schema} +Note: Do not include any explanations or apologies in your responses. +Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. +Do not include any text except the generated Cypher statement. + +The question is: +{question}""" + + CYPHER_GENERATION_PROMPT = PromptTemplate( + input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE + ) + + # 工程知识图谱查询链 + self.engineering_qa = GraphCypherQAChain.from_llm( + llm=self.llm, + graph=graph, + verbose=True, + allow_dangerous_requests=True, + cypher_prompt=CYPHER_GENERATION_PROMPT, + **self.kwargs + ) + + # Chain of Thought提示模板和链 + cot_prompt = PromptTemplate( + input_variables=["question"], + template=( + "你是一个专业的电力造价工程助理。请先将用户的问题拆分成多个子问题," + "分别检索相关的通用业务知识和工程数据知识库,最后综合回答用户。\n\n" + "用户问题:{question}\n\n" + "请给出拆分的子问题列表和每个子问题的检索计划," + "然后给出最终综合回答。\n\n" + "思考过程:" + ) + ) + self.cot_chain = RunnableSequence( + cot_prompt | self.llm + ).with_config( + verbose=True, + **self.kwargs + ) + + # 定义Agent工具 + self.tools = [ + Tool( + name="EngineeringData", + func=self.engineering_qa.invoke, + description="用于查询具体工程数据相关问题" + ), + Tool( + name="BusinessKnowledge", + func=self._business_qa, + description="用于查询电力造价行业通用业务知识" + ), + ] + + # 多轮对话记忆 + message_history = ChatMessageHistory() + self.memory = ConversationBufferMemory( + chat_memory=message_history, memory_key="chat_history", return_messages=True) + + # 初始化Agent,支持多轮对话和工具调用 + self.agent = initialize_agent( + self.tools, + self.llm, + agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, + memory=self.memory, + verbose=True, + **self.kwargs + ) + + def _business_qa(self, query: str) -> str: + if not self.business_retriever: + return "通用业务知识库未配置。" + business_qa = RetrievalQA( + llm=self.llm, + retriever=self.business_retriever, + return_source_documents=False, + **self.kwargs + ) + return business_qa.invoke(query) + + def ask(self, question: str) -> str: + # 1. 使用CoT链拆分问题和规划检索 + cot_output = self.cot_chain.invoke(question) + + # 2. 简单示例:先调用业务知识库,再调用工程数据知识库 + business_answer = self._business_qa(question) + engineering_answer = self.engineering_qa.run(question) + + # 3. 综合回答 + final_answer = ( + "根据通用业务知识库,得到的信息是:\n" + f"{business_answer}\n\n" + "根据工程数据知识库,得到的信息是:\n" + f"{engineering_answer}\n\n" + "综合以上信息,回答用户问题如下:\n" + f"{cot_output}" + ) + return final_answer + + +# ------------------ 使用示例 ------------------ + +if __name__ == "__main__": + import os + import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + from src.config import Config + from src.multi_llm_client import MultiAPIKeyChatOpenAI + from src.embedding_client import EmbeddingClient + + config = Config() + + # 初始化图数据库连接 + graph = Neo4jGraph( + url=config.neo4j_conf.get("uri"), + username=config.neo4j_conf.get("username"), + password=config.neo4j_conf.get("password") + ) + + # 初始化LLM和Embedding + llm = MultiAPIKeyChatOpenAI(config.openai_qa) + embedding = EmbeddingClient(config.embedding) + + # 初始化通用业务知识向量库Retriever(示例中未配置) + business_vectorstore = None # 例如 Chroma(collection_name="business_knowledge") + business_retriever = None # business_vectorstore.as_retriever() if business_vectorstore else None + + # 创建Agent实例 + agent = EngineeringQAAgent(llm, embedding, graph, business_retriever) + + # 交互示例 + question = "工程里有几个项目划分,每个项目划分的名字分别是什么" + answer = agent.ask(question) + print("回答:", answer) diff --git a/src/prompt_manager.py b/src/prompt_manager.py index 356a6e2..47755b1 100644 --- a/src/prompt_manager.py +++ b/src/prompt_manager.py @@ -71,23 +71,13 @@ def project_get_calculate_function(): quantity = result_dict['数量'] if status: - return { - "code": 200, - "message": 'ok', - "status": True, - "data": quantity - } + return result_dict else: - return { - "code": 500, - "message": message, - "status": False, - "data": quantity - } + return result_dict # 执行规则 - 参数必须从用户问题或上下文信息中提取。 -- 在代码函数内部生成功能说明,在函数外禁止生成任何注释或解释或非代码内容。 +- 为函数内部代码生成流程注释,并使用logger进行日志输出,在函数外禁止生成任何注释或解释或非代码内容。 - 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数,该函数返回字典包含:'code'、'status'、'message'、'data'四个字段。 - 必须确保生成的代码可以直接执行,代码要注意进行各类容错检查。 - 'data'字段通常要求是浮点或整型值,除非用户要求返回其他类型,同时函数执行过程中发生错误,'data'字段也必须为0,并在message说明错误原因。 @@ -184,168 +174,3 @@ Cypher查询语句:MATCH (item:ProjectDivisionItem)\nWHERE item.name CONTAINS rewrite_prompt_template=rewrite_prompt_template, cypher_conversion_prompt=cypher_conversion_prompt, ) - -@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', []) - logger.info(f"status {{status}} message: {{message}}") - if status: - return result_dict - else: - return result_dict - -# 执行规则 -- 参数必须从用户问题或上下文信息中提取 -- 输出代码中必须以def project_get_calculate_function() -> dict函数作为入口函数 -- 必须确保生成的代码可以直接执行,代码要注意进行各类错误检查,出错采用抛出异常方式,说明详细信息 -- 为函数内部代码生成流程注释,并使用logger进行日志输出 -- 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”),只关注获取原始数据对象或属性。 -构建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, - )