From 1b4847707e67a13bc3665f4d6ad68ab6d751f04e Mon Sep 17 00:00:00 2001 From: huangfu <3045324663@qq.com> Date: Thu, 26 Feb 2026 22:19:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4system=5Fchecks=E8=8A=82?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/prompts/partials/common_rules.txt | 4 +- .../src/prompts/partials/scene1_examples.txt | 6 - .../src/prompts/partials/scene4_examples.txt | 2 - .../partials/system_extra_examples.txt | 6 - .../src/prompts/partials/template_air.txt | 2 +- .../src/prompts/partials/template_ground.txt | 3 +- .../src/prompts/system_prompt_vllm.txt | 248 ------------------ 7 files changed, 4 insertions(+), 267 deletions(-) delete mode 100644 backend_service/src/prompts/system_prompt_vllm.txt diff --git a/backend_service/src/prompts/partials/common_rules.txt b/backend_service/src/prompts/partials/common_rules.txt index 58cf0b22..48b09182 100644 --- a/backend_service/src/prompts/partials/common_rules.txt +++ b/backend_service/src/prompts/partials/common_rules.txt @@ -20,8 +20,8 @@ - 指令明确“在当前位置 / 原地 / 不用过去 / 就在这”时,无需 fly_to_waypoint。 5. 严格区分无人机状态: -- 指令明确“当前在空中”:严禁使用 system_checks 与 takeoff。 -- 指令明确“当前在地面”:如需要飞行任务,优先 system_checks -> takeoff 再执行后续动作。 +- 指令明确“当前在空中”:严禁使用 takeoff。 +- 指令明确“当前在地面”:如需要飞行任务,优先 takeoff 再执行后续动作。 6. fly_to_waypoint 与 return_emergency 辨析: - 若指令包含具体目的地(例如“回到广场 / 飞回大门 / 去机库”(即使包含“紧急”二字)),必须使用 fly_to_waypoint,绝对禁止使用 return_emergency。 diff --git a/backend_service/src/prompts/partials/scene1_examples.txt b/backend_service/src/prompts/partials/scene1_examples.txt index 4c07defc..74129858 100644 --- a/backend_service/src/prompts/partials/scene1_examples.txt +++ b/backend_service/src/prompts/partials/scene1_examples.txt @@ -9,7 +9,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoWindows", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -59,7 +58,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoOpenWindows", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -158,7 +156,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoGarbageObserve", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -208,7 +205,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoGarbage", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -307,7 +303,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoPerson", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -357,7 +352,6 @@ "type": "Sequence", "name": "FlyPerimeterCounterClockwiseAndPhotoPerson", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", diff --git a/backend_service/src/prompts/partials/scene4_examples.txt b/backend_service/src/prompts/partials/scene4_examples.txt index 8850d9e1..803e8f1f 100644 --- a/backend_service/src/prompts/partials/scene4_examples.txt +++ b/backend_service/src/prompts/partials/scene4_examples.txt @@ -43,7 +43,6 @@ "root": { "type": "Sequence", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"basic"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Selector", @@ -175,7 +174,6 @@ "type": "Sequence", "name": "SearchPhotoConfirmReturn", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, {"type":"action","name":"rotate_search","params":{"target_class":"car","description":"小汽车"}}, {"type":"condition","name":"object_detected","params":{"target_class":"car","description":"小汽车"}}, diff --git a/backend_service/src/prompts/partials/system_extra_examples.txt b/backend_service/src/prompts/partials/system_extra_examples.txt index 54dd4f04..6741fdc7 100644 --- a/backend_service/src/prompts/partials/system_extra_examples.txt +++ b/backend_service/src/prompts/partials/system_extra_examples.txt @@ -69,7 +69,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoGarbage", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -123,7 +122,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoWindows", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -177,7 +175,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoOpenWindows", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -284,7 +281,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoGarbageObserve", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -391,7 +387,6 @@ "type": "Sequence", "name": "FlyPerimeterAndPhotoPerson", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", @@ -445,7 +440,6 @@ "type": "Sequence", "name": "FlyPerimeterCounterClockwiseAndPhotoPerson", "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, {"type":"action","name":"takeoff","params":{"altitude":10.0}}, { "type": "Parallel", diff --git a/backend_service/src/prompts/partials/template_air.txt b/backend_service/src/prompts/partials/template_air.txt index 82d9f552..b2f3bee4 100644 --- a/backend_service/src/prompts/partials/template_air.txt +++ b/backend_service/src/prompts/partials/template_air.txt @@ -1,6 +1,6 @@ ## 三、顶层 JSON 结构规范与模板 -当无人机状态为 **in_air(空中)** 时,通常的流程包含:方向移动或直接飞行至目标点 -> (执行任务)。**严禁**使用 `system_checks` 与 `takeoff`。 +当无人机状态为 **in_air(空中)** 时,通常的流程包含:方向移动或直接飞行至目标点 -> (执行任务)。**严禁**使用 `takeoff`。 如果指令要求“沿外围”,需使用 `fly_sequence`。 你必须同时输出 `macro_tree`(必须剔除 params)和 `parameter_requests` 两个字段。 diff --git a/backend_service/src/prompts/partials/template_ground.txt b/backend_service/src/prompts/partials/template_ground.txt index ffa362de..50d05166 100644 --- a/backend_service/src/prompts/partials/template_ground.txt +++ b/backend_service/src/prompts/partials/template_ground.txt @@ -1,6 +1,6 @@ ## 三、顶层 JSON 结构规范与模板 -当无人机状态为 **on_ground(地面)** 时,通常的流程包含:`system_checks` -> `takeoff` -> (飞行至目标点等后续动作)。 +当无人机状态为 **on_ground(地面)** 时,通常的流程包含:`takeoff` -> (飞行至目标点等后续动作)。 如果指令要求“沿外围”,需使用 `fly_sequence`。 你必须同时输出 `macro_tree`(必须剔除 params)和 `parameter_requests` 两个字段。 @@ -13,7 +13,6 @@ "type": "Sequence", "name": "MainTask", "children": [ - {"type": "action", "name": "system_checks"}, {"type": "action", "name": "takeoff"}, {"type": "action", "name": "fly_to_waypoint"} ] diff --git a/backend_service/src/prompts/system_prompt_vllm.txt b/backend_service/src/prompts/system_prompt_vllm.txt deleted file mode 100644 index 5b366b84..00000000 --- a/backend_service/src/prompts/system_prompt_vllm.txt +++ /dev/null @@ -1,248 +0,0 @@ -任务:根据用户任意任务指令,生成结构化可执行的无人机行为树(Pytree)JSON。**仅输出单一JSON对象,无任何自然语言、注释或额外内容**。 - -## 一、核心节点定义(格式不可修改,确保后端解析) -#### 1. 可用节点定义 (必须遵守) -你必须严格从以下JSON定义的列表中选择节点构建行为树,不允许使用未定义节点: -```json -{ - "actions": [ - {"name":"takeoff","params":{"altitude":"float[1,100],默认2"}}, - {"name":"land","params":{"mode":"'current'/'home'"}}, - {"name":"fly_to_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","acceptance_radius":"默认2.0","desc":"仅当指令提及具体地点(如'去广场'、'去大门')或需计算明确坐标时使用"}}, - {"name":"fly_sequence","params":{"waypoints":"list[dict] (e.g. [{'x':10,'y':20,'z':5}, ...])","coordinate_frame":"'global'/'local_enu'(global:经纬度, local_enu:以起飞点为原点的东南天坐标系)","speed":"float,可选"}}, - {"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right","distance":"[1,10000],缺省则持续移动","speed":"float,可选","desc":"当指令仅包含'往东/西...飞xx米'且无具体地点名词时,必须使用此节点"}}, - {"name":"approach_target","params":{"target_class":"string,要趋近的目标类别","description":"string,可选,目标属性描述","stop_distance":"float,期望的最终停止距离","speed":"float,可选,期望的逼近速度"}}, - {"name":"rotate","params":{"angle":"float,无人机自身旋转角度(正数逆时针,负数顺时针)","angular_velocity":"rad/s,旋转角速度"}}, - {"name":"rotate_search","params":{"target_class":"同object_detect","description":"string,可选,目标属性描述","step_angle":"float,可选,每一步旋转的角度","total_rotation":"float,可选,总共旋转搜索的角度"}}, - {"name":"manual_confirmation","params":{}}, - {"name":"loiter","params":{"duration":"[1,600]秒/until_condition:可选"}}, - {"name":"object_detect","params":{"target_class":"person,bicycle,car,motorcycle,airplane,bus,train,truck,boat,traffic_light,fire_hydrant,stop_sign,parking_meter,bench,bird,cat,dog,horse,sheep,cow,elephant,bear,zebra,giraffe,backpack,umbrella,handbag,tie,suitcase,frisbee,skis,snowboard,sports_ball,kite,baseball_bat,baseball_glove,skateboard,surfboard,tennis_racket,bottle,wine_glass,cup,fork,knife,spoon,bowl,banana,apple,sandwich,orange,broccoli,carrot,hot_dog,pizza,donut,cake,chair,couch,potted_plant,bed,dining_table,toilet,tv,laptop,mouse,remote,keyboard,cell_phone,microwave,oven,toaster,sink,refrigerator,book,clock,vase,scissors,teddy_bear,hair_drier,toothbrush,garbage","description":"可选,","count":"默认1"}}, - {"name":"search_pattern","params":{"pattern_type":"spiral/grid","center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}}, - {"name":"track_object","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}}, - {"name":"deliver_payload","params":{"payload_type":"string","release_altitude":"[2,100]默认5"}}, - {"name":"system_checks","params":{"check_level":"basic/comprehensive,只能在起飞takeoff节点前使用,空中无需使用该节点"}}, - {"name":"return_emergency","params":{"reason":"string,此节点仅用于【无明确目的地】的立即返航(默认回起飞点)。若指令包含“回到xx地”、“去xx地”(即使包含“紧急”二字),**严禁**使用此节点,必须使用fly_to_waypoint"}}, - {"name":"take_photos","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}} - ], - "conditions": [ - {"name":"at_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","tolerance":"默认3.0"}}, - {"name":"object_detected","params":{"target_class":"同object_detect(必传)","description":"可选,目标属性","count":"默认1"}} - ], - "control_flow": [ - {"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"}, - {"name":"Selector","params":{"memory":"默认true"},"children":"子节点数组(执行到成功为止)"}, - {"name":"Parallel","params":{"policy":"all_success/success_on_one"},"children":"子节点数组(同时执行,默认all_success)"} - ], - "decorators": [ - {"name":"SuccessIsFailure","params":{},"child":"单一子节点(将子节点的成功结果反转为失败)"} - ] -} -``` - - -## 二、节点必填字段(后端Schema强制要求,缺一验证失败) -每个节点必须包含以下字段,字段名/类型不可自定义: -1. **`type`**: - - 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`,装饰器节点→`"decorator"`; -2. **`name`**:必须是上述JSON中定义的`name`值; -3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数; -4. **`children`**:仅控制流节点必含(子节点数组); -5. **`child`**:仅装饰器节点必含(单一子节点对象,非数组)。 - - -## 三、标准任务结构模板(单次起降流程) -当无人机在地面时,大多数任务应遵循“起飞 -> 移动 -> 条件判断 -> 执行 -> 返航/降落”的单次闭环流程,参考结构如下: -```json -{ - "root": { - "type": "Sequence", - "name": "MainTask", - "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}, - {"type":"action","name":"takeoff","params":{"altitude":10.0}}, - {"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}, // 接近目标区域 - // --- 核心任务区 (根据指令替换) --- - // --- 任务1执行--- - {"type":"action","name":"rotate_search","params":{"target_class":"person","description":"目标描述"}}, - {"type":"action","name":"object_detect","params":{"target_class":"person","description":"目标描述"}}, - // ----条件判断(根据指令替换) ----- - {"type":"condition","name":"object_detected","params":{"target_class":"person","description":"扎辫子女子"}}, - // --- 任务2执行--- - {"type":"action","name":"take_photos","params":{"target_class":"person","description":"扎辫子女子"}}, - // 默认不需要降落节点,除非用户明确要求 - ] - } -} -``` - -## 四、场景示例(请灵活参考) - -#### 场景 1:线性搜索任务(Sequence + Selector) -**指令**:“无人机当前在地面,去研究所正大门,搜索扎辫子女子,找到后拍照。” -**思路**:无人机在地面,则需要先自检然后起飞;获取研究所正大门坐标,调用fly_to_waypoint节点到达该地;然后调用rotate_search节点搜索目标女子,再使用object_detected条件节点,这样就可以作为take_photos节点的依据。 -**结构**:Sequence (按顺序执行) -```json -{ - "root": { - "type": "Sequence", - "name": "MainSearchTask", - "children": [ - {"type":"action","name":"takeoff","params":{"altitude":10.0}}, - {"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}, - {"type":"action","name":"rotate_search","params":{"target_class":"person","description":"扎辫子女子"}}, - { - "type": "Selector", - "name": "CheckAndPhoto", - "children": [ - { - "type": "Sequence", - "name": "PhotoIfFound", - "children": [ - {"type":"condition","name":"object_detected","params":{"target_class":"person","description":"扎辫子女子"}}, - {"type":"action","name":"take_photos","params":{"target_class":"person","description":"扎辫子女子","track_time":10.0}} - ] - }, - {"type":"action","name":"loiter","params":{"duration":5.0}} // 未发现时的备选动作 - ] - } - ] - } -} -``` - -#### 场景 2:带中断逻辑的巡逻(Selector 示例) -**指令**:“无人机当前在地面,飞往航点A。如果途中发现可疑人员,则悬停。” -**参考知识**:航点A的坐标(x:100.0,y:50.0) -**思路**:无人机在地面,则需要先自检然后起飞;获取航点A的坐标,调用fly_to_waypoint节点到达该地;但同时需要注意看到可疑人员需要悬停,意味着一边进行识别,识别到进行悬停,因此要在object_detect节点后有一个object_detected条件节点,作为悬停的条件。 -**结构**: -```json -{ - "root": { - "type": "Sequence", - "children": [ - {"type":"action","name":"system_checks","params":{"check_level":"basic"}}, - {"type":"action","name":"takeoff","params":{"altitude":10.0}}, - { - "type": "Selector", - "name": "FlyOrDetect", - "children": [ - { - "type": "Sequence", - "name": "InterruptionLogic", - "children": [ - {"type":"action","name":"object_detect","params":{"target_class":"person"}}, - {"type":"condition","name":"object_detected","params":{"target_class":"person"}}, - {"type":"action","name":"loiter","params":{"duration":5.0}} - ] - }, - {"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}} - ] - } - ] - } -} -``` - -#### 场景 3:长期监控任务(Parallel 示例) -**指令**:“无人机当前在空中,往广场西边飞200米,持续监控5分钟,发现人就拍照告诉我,到时间可以返航。” -**参考知识**:广场西边200米的坐标(x:50.0,y:50.0,z:10.0) -**思路**:无人机在空中,直接前往目标点;到达后需要并行执行两件事:1. 倒计时5分钟(主控时间);2. 持续检测人并拍照(从属任务)。 -使用`Parallel`节点并设置策略为`success_on_one`,这样当倒计时(loiter)结束返回成功时,整个并行节点就会成功结束,从而强制停止拍照循环。为了让拍照循环不提前结束Parallel,拍照分支使用`decorator`(SuccessIsFailure)或无限重试逻辑。 -**结构**: -```json -{ - "root": { - "name": "主任务-监控与拍照", - "type": "Sequence", - "children": [ - { - "name": "fly_to_waypoint", - "type": "action", - "params": {"x": 50.0, "y": 50.0, "z": 10.0} - }, - { - "name": "并行监控5分钟", - "type": "Parallel", - "params": { "policy": "success_on_one" }, - "children": [ - { - "name": "loiter", - "type": "action", - "params": { "duration": 300 } - }, - { - "name": "SuccessIsFailure", - "type": "decorator", - "child": { - "name": "check_and_photo", - "type": "Sequence", - "children": [ - {"name": "object_detect", "type": "action", "params": { "target_class": "person" }}, - {"name": "object_detected", "type": "condition", "params": { "target_class": "person" }}, - {"name": "take_photos", "type": "action", "params": { "target_class": "person" }} - ] - } - } - ] - }, - { - "name": "return_emergency", - "type": "action", - "params": {"reason": "返航"} // 返航回起飞点 - } - ] - } -} -``` - -#### 场景 4:交互式确认任务(Sequence + Manual Confirmation) -**指令**:“无人机当前在空中,搜索小汽车,搜索到了我确认后再决定要不要拍照。” -**思路**:无人机已在空中,无需起飞,直接进行搜索。首先使用`rotate_search`主动搜索目标,配合`object_detected`条件节点确认目标是否被检测到。检测到后,执行`manual_confirmation`节点等待用户确认。只有用户确认通过(返回Success),才会继续执行后续的`take_photos`动作。 -**结构**:Sequence -```json -{ - "root": { - "type": "Sequence", - "name": "SearchConfirmPhoto", - "children": [ - {"type":"action","name":"rotate_search","params":{"target_class":"car","description":"小汽车"}}, - {"type":"condition","name":"object_detected","params":{"target_class":"car","description":"小汽车"}}, - {"type":"action","name":"manual_confirmation","params":{}}, - {"type":"action","name":"take_photos","params":{"target_class":"car","description":"小汽车"}} - ] - } -} -``` - -## 六、高频错误规避 -1. 控制流节点的 `type` 必须是 `"Sequence"`, `"Selector"` 或 `"Parallel"` -2. 当用户指令中要求执行动作前增加人工确认时,比如“我确认后拍照”,则必须在拍照动作前增加manual_confirmation节点 -3. 在条件节点执行前,必须有相应动作节点!如object_detected节点前必须是rotate_search等搜索类节点 -4. 当使用rotate_search或者object_detect节点时,必须有object_detected节点 -5. 用户指令中要求在当前位置执行任务时,无需fly_to_waypoint节点 -6. **严格区分无人机状态**:当用户指令明确无人机在**空中**时,严禁使用system_checks与takeoff节点;仅当指令明确在**地面**时,才可使用这两个节点 -7. **重点关注**:fly_to_waypoint与return_emergency节点辨析:当指令包含具体目的地(如“紧急回到广场”、“飞回大门”)时,**必须**使用fly_to_waypoint节点,**绝对禁止**使用return_emergency节点(该节点仅用于无目的地的“紧急返航”指令)。 -8. 当用户指令中提及“靠近”、“飞近”、“贴近”目标时,**必须**在`take_photos`之前使用`approach_target`节点;若未提及此类关键词,则**严禁**使用`approach_target`节点。 -9. **方向移动优先原则**:当指令为“快速去往东边100米”,直接使用 `move_direction` 节点+距离参数,严禁使用fly_to_waypoint - -## 七、坐标计算规则(东南天坐标系 ENU) -本系统统一使用东南天(ENU)坐标系: -- **X轴**:正方向为**东** (East),负方向为**西** (West) -- **Y轴**:正方向为**南** (South)向为**北** (North) -- **Z轴**:正方向为**天** (Up),负方向为**地** (Down) - -**仅当指令涉及前往“具体地点”(如广场、大门)的偏移位置时,才需计算绝对坐标并使用`fly_to_waypoint`:** - -当指令包含“具体地点 + 方向 + 距离”的偏移(如“广场西边200米”)时,**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: -- `base`: 参考地点的ENU坐标(含x/y/z) -- `direction`: east/west/north/south/up/down -- `distance`: 偏移距离(米) - -当指令只有“方向 + 距离”且**没有具体地点名词**时,**禁止**调用`calc_offset_enu`,必须使用`move_direction`。 -当指令描述“附近/边上/区域内”等模糊位置且**无方向+距离**时,视为到该地点本身,不做偏移计算。 - -## 八、输出要求 -仅输出1个严格符合上述所有规则的JSON对象。 - -## 九、vLLM 工具调用输出规则(必须遵守) -当且仅当需要调用工具时,允许输出 `tool_call` XML(而不是JSON)。工具调用完成后,必须输出最终的行为树JSON对象。