Browse Source

找图找数打开图层

liutao 2 months ago
parent
commit
dacfb07995

+ 8 - 4
aiAgent_gd/agent_config.py

@@ -25,7 +25,7 @@ from qwen_agent.sub_agent.doc.expand_write_agent import ExpandWriteAgent
 from qwen_agent.sub_agent.doc.continue_write_agent import ContinueWriteAgent
 from qwen_agent.sub_agent.doc.error_check_agent import ErrorCheckAgent
 from qwen_agent.sub_agent.doc.sensitive_check_agent import SensitiveCheckAgent
-
+from qwen_agent.sub_agent.gis.gis_layer_operation import GisLayerOperationAgent
 ActionDict = {
     "generate_chart": ChartAgent,
     "summary": SummaryAgent,
@@ -53,7 +53,8 @@ ActionDict = {
     "ExpandWriteAgent": ExpandWriteAgent,
     "ContinueWriteAgent": ContinueWriteAgent,
     "ErrorCheckAgent": ErrorCheckAgent,
-    "SensitiveCheckAgent": SensitiveCheckAgent
+    "SensitiveCheckAgent": SensitiveCheckAgent,
+    "GisLayerOperationAgent":GisLayerOperationAgent
 }
 
 AgentCNNameDict = {
@@ -96,7 +97,9 @@ AgentCNNameDict = {
     "ExpandWriteAgent": "用于文章扩写的Agent",
     "ContinueWriteAgent": "用于文章续写的Agent",
     "ErrorCheckAgent": "用于检查文章内容是否有错误的Agent",
-    "SensitiveCheckAgent": "用于文章内敏感词检查的Agent"
+    "SensitiveCheckAgent": "用于文章内敏感词检查的Agent",
+    "layer_operation_planner": "用于GIS图层控制系统的Agent",
+    "GisLayerOperationAgent": "Gis 图层控制",
 }
 
 LLMDict_Qwen72 = {
@@ -197,5 +200,6 @@ LLMDict_Qwen_72B_1211 = {
     "ExpandWriteAgent": "qwen2.5-instruct",
     "ContinueWriteAgent": "qwen2.5-instruct",
     "ErrorCheckAgent": "qwen2.5-instruct",
-    "SensitiveCheckAgent": "qwen2.5-instruct"
+    "SensitiveCheckAgent": "qwen2.5-instruct",
+    "GisLayerOperationAgent": "qwen2.5-instruct"
 }

+ 8 - 0
aiAgent_gd/qwen_agent/planning/plan_dispatcher.py

@@ -18,12 +18,14 @@ from qwen_agent.sub_agent import ChartAgent
 from qwen_agent.sub_agent.ChatAgent import ChatAgent
 from qwen_agent.sub_agent.KnowledgeChatAgent import KnowledgeChatAgent
 from qwen_agent.planning.plans.land_find_plan import LandFindPlan
+from qwen_agent.planning.plans.layer_operation_plan import LayerOperationPlan
 BIDDING_PLANS = {
     "Chat": "如果用户的问题和自然资源的分析无关,可以选择闲聊接口和用户闲聊",
     "Gis": "gis图形相关的分析和arcgis server图层查询和空间分析,擅长进行图形的相交等空间叠加分析计算。如: 上传的shp与工业用地图层空间分析的相交结果",
     "KnowledgeChat": "如果用户的问题和自然资源的知识有关,可以选择知识库问答接口",
     "LandSiteSelectionPlan": "智能选址分析,如:请帮我推荐杭州市50亩左右的工业用地?",
     "LandFindPlan": "找图找数,如:请帮我查一下萧山区永久基本农田面积大于100亩的地块?",
+    "LayerOperationPlan": "图层控制系统,如打开永久基本农田图层",
     "LandSupplyPlan": "土地供应合同分析,用于Question中包含了[一个具体的]区域名称选择、土地供应情况。如:请分析近几年杭州市住宅用地出让情况?",
     "LandUsePlan": "土地利用现状,用于Question中包含了[一个具体的]区域名称选择、土地利用现状情况,包括土地的耕地面积、湿地面积等。如:2022年浙江省土地利用现状情况?",
     "LandApprovalPlan": "土地报批项目,用于Question中包含了[一个具体的]区域名称选择、土地报批项目情况。如:瑞安市2023年报批项目总面积?",
@@ -38,6 +40,7 @@ PLAN_DICT = {
     "GisPlan": GisPlan,
     "LandSiteSelectionPlan": LandSiteSelectionPlan,
     "LandFindPlan": LandFindPlan,
+    "LayerOperationPlan": LayerOperationPlan,
     "LandSupplyPlan": LandSupplyPlan,
     "KfqEvalPlan": KfqEvalPlan,
     "LandApprovalPlan": LandApprovalPlan,
@@ -118,6 +121,11 @@ Question: 帮我在萧山区找出面积大于100亩的永久基本农田图斑
 Thought: 用户想要找出永久基本农田地块,调用 LandFindPlan 分析模块
 Plan Agent: LandFindPlan
 
+Example #12:
+Question: 帮我打开永久基本农田图层
+Thought: 用户想要打开永久基本农田图层,调用 LayerOperationPlan 分析模块
+Plan Agent: LayerOperationPlan
+
 注意:
 1.Plan Agent 返回的都是单一的,不要出现多个plan,不要出现多个plan, 比如以下情况:Plan Agent: LandUsePlan, ReportPlan
 """

+ 0 - 1
aiAgent_gd/qwen_agent/planning/planner.py

@@ -85,7 +85,6 @@ class Planner:
                     await asyncio.sleep(2**self.max_retry_times + 2**(self.max_retry_times - retry_times + 1))
                 
                 # plans, msg = self.parse_plan(self.default_plan)
-            
         yield ChatResponseChoice(role='info', content=msg)
         self.plans = plans
         self.exec_res = msg

+ 62 - 0
aiAgent_gd/qwen_agent/planning/plans/layer_operation_plan.py

@@ -0,0 +1,62 @@
+import json
+
+from qwen_agent.planning.planner import Planner
+from qwen_agent.memory.plan_memory import PlanExampleRetrieval
+
+actions_list = {
+    "GisLayerOperationAgent": "用户控制图层的打开和关闭",
+    "summary": "对查询的结果,进行总结摘要,提炼用户关注的信息"
+}
+
+PROMPT_TEMPLATE = """
+你是一个GIS图层控制系统,需要将自然语言指令解析为结构化操作
+如果用户历史回答中已经相应的答案,请直接使用summary对历史回答进行总结
+下面是动作选项列表,可以选择有助于完成用户需求的一个或多个动作:
+动作列表
+{actions_list}
+
+请依据参考资料,制定计划完成用户需求,按照如下格式返回:
+Question: 用户针区域土地利用现状问题的提问
+Thought: 生成Plan的思考过程,如果提到多个区域名称,需要多次调用[GisLayerOperationAgent]接口,给用户一些依据。
+Plan: 生成的计划,包含函数名和执行的目标。以JSON格式返回,所有的Action会以执行的先后顺序保存在list中,例如:
+[{{"action_name": 第一步执行的Action名字, "instruction": 执行需要达到的预期,请给出详细有效的指示}}, {{"action_name": 第二步执行的Action名字, "instruction": 执行需要达到的预期,请给出详细有效的指示}}]
+
+举例:
+{example_list}
+
+注意0:最后一步必须调用[summary]这个action,对查询的结果,进行总结摘要,提炼用户关注的信息;
+"""
+
+examples = """
+Examples #1:
+Question: 打开永久基本农田图层
+Thought:  用户问题中想要打开永久基本农田图层,所以需要通过[GisLayerOperationAgent]将问题转换为具体的结构化指令
+Plan: ```json\n    [{\"action_name\": \"GisLayerOperationAgent\", \"instruction\": \"你需要调用 [GisLayerOperationAgent]将问题转换为具体的结构化指令\"}, {\"action_name\": \"summary\",\"instruction\": \"对查询的结果,进行总结摘要,提炼用户关注的信息\"}]
+
+```
+"""
+
+
+
+DEFAULT_PLAN = """使用默认执行计划:
+Plan: ```json\n    [{\"action_name\": \"GisLayerOperationAgent\", \"instruction\": \"你需要调用 [GisLayerOperationAgent]将问题转换为具体的结构化指令\"}]
+"""
+
+
+class LayerOperationPlan(Planner):
+    def __init__(self, llm_name, name='layer_operation', **kvargs):
+        self.actions_list_str = json.dumps(actions_list, ensure_ascii=False)
+        # retriever = PlanExampleRetrieval(query_type=name)
+        super(LayerOperationPlan, self).__init__(
+            llm_name=llm_name,
+            name=f'{name}_planner',
+            system_prompt=PROMPT_TEMPLATE,
+            retriever=None,
+            default_plan=DEFAULT_PLAN, **kvargs
+        )
+
+    def get_system_prompt(self, user_request):
+        # similar_exams = self.retriever.get_relevant_documents(user_request, top_k=3)
+        # examples = '\n'.join([f"Example #{i + 1}:\n{q}" for i, (n, q) in enumerate(similar_exams[::-1])])
+        # print(f'user request:{user_request} \nRetrivaled Examples: {examples}\n')
+        return PROMPT_TEMPLATE.format(actions_list=self.actions_list_str, example_list=None)

+ 84 - 0
aiAgent_gd/qwen_agent/sub_agent/gis/gis_layer_operation.py

@@ -0,0 +1,84 @@
+import asyncio
+import copy
+import json
+import re
+from urllib import parse
+
+import requests
+from shapely import Polygon, wkt
+
+from qwen_agent.messages.context_message import ChatResponseChoice, ChatResponseStreamChoice
+from qwen_agent.sub_agent.BaseSubAgent import BaseSubAgent
+from qwen_agent.tools.gis.geocoder.geocoder_tdt import GeocoderTDT
+from qwen_agent.utils.gis_util import get_area
+from qwen_agent.utils.util import extract_json
+
+
+class GisLayerOperationAgent(BaseSubAgent):
+    def __init__(self, llm=None, llm_name=None, stream=False, name='gis_geocoder_agent'):
+        super(GisLayerOperationAgent, self).__init__(llm, llm_name, stream, name=name)
+        self.tool_list = []
+        self.REACT_INSTRUCTION = ""
+        self.SubAgent_PROMPT = """你是一个GIS图层控制系统,需要将自然语言指令解析为结构化操作。请严格按以下规则处理:
+        <处理规则>
+        1. 只关注图层开关指令,忽略无关内容
+        2. 支持的同义动词:
+           - 打开:开启/显示/激活
+           - 关闭:隐藏/禁用/取消
+        3. 图层名称可能是字母、数字或中文组合(如"道路层"/"Layer1")
+        4. 多个操作按指令顺序保持
+        5. 未提及的图层保持原状
+        6. 生成的操作命令结果中,layer图层名称字段中不包含'图层'后缀
+        </处理规则>
+        
+        <输出格式>
+        必须返回如下JSON结构:
+        {{
+          "operations": [
+            {{"action": "open/close", "layer": "图层名称"}},
+            ...
+          ]
+        }}
+        </输出格式>       
+        <示例>
+        用户问:请把永久基本农田图层和道路层显示出来,关掉旧版标注
+        回应:
+        {{
+          "operations": [
+            {{"action": "open", "layer": "永久基本农田"}},
+            {{"action": "open", "layer": "道路层"}},
+            {{"action": "close", "layer": "旧版标注"}}
+          ]
+        }}
+        </示例>
+        请严格按上述要求生成JSON响应,不要包含任何额外内容:
+        """
+
+    async def run(self, plan_context, messages=[]):
+        query = plan_context.user_request
+        _messages = copy.deepcopy(messages)
+        _messages.insert(0, {'role': 'system', 'content': self.SubAgent_PROMPT})
+        _messages.append({'role': 'user', 'content': query})
+        for msg in _messages:
+            if 'history' not in msg:
+                yield ChatResponseChoice(
+                    role=msg['role'],
+                    content=msg['content']
+                )
+        rsp = await self.llm.chat(model=self.llm_name, messages=_messages, stream=self.stream)
+
+        await asyncio.sleep(0.2)
+        if self.stream:
+            res = ""
+            async for chunk in rsp:
+                res += chunk
+                yield ChatResponseStreamChoice(role='assistant', delta=chunk)
+            yield ChatResponseStreamChoice(role='assistant', finish_reason='stop')
+        else:
+            res = rsp
+            yield ChatResponseChoice(role='assistant', content=rsp)
+
+        self.exec_res = f"\n```json\n{res}\n```\n"
+
+
+

+ 4 - 0
aiAgent_gd/qwen_agent/sub_agent/summary_agent.py

@@ -13,6 +13,7 @@ SYSTEM_PROMPT = """
 注意:
 1. 如果用户查询的Question通过数据库查询,没有返回结果,请直接回答“数据库中没有查询到相关的数据”,不允许胡编乱造。如果在数据库中查询到相关的结果,请根据结果回答用户问题。
 2. 请不要对show_case的查询到的记录做过与详细的描述;
+3. 如果用户查询的Question没有设计到数据库查询,请直接回答没有相关数据。
 """
 
 agents_prompt = dict({
@@ -36,6 +37,9 @@ agents_prompt = dict({
     'SpatialAnalysisAgent': """
         1. 总结时不要输出图形的wkt信息或者其他坐标点信息
     """,
+    'GisLayerOperationAgent':"""
+        总结操作的是哪个图层
+    """,
     'LandSiteSelectionSqlAgent': """
     
     必须按照markdown格式输出的地块信息。以下是输出信息的参考,请将<>替换成真实的内容: