流程节点完善

This commit is contained in:
2026-02-26 19:37:55 +08:00
parent 5d8412bcb6
commit c7f6a0da17
3059 changed files with 4975 additions and 71239 deletions

View File

@@ -6,7 +6,7 @@ import logging
# import threading # ROS2相关已注释
# import rclpy # ROS2相关已注释
from .models import GeneratePlanRequest, ExecuteMissionRequest
from .models import GeneratePlanRequest, ExecuteMissionRequest, DebugStageRequest
from .websocket_manager import websocket_manager
from .py_tree_generator import py_tree_generator
# from .ros2_client import MissionActionClient # ROS2相关已注释
@@ -41,11 +41,29 @@ async def generate_plan_endpoint(request: GeneratePlanRequest):
Receives a user prompt and returns a generated `py_tree.json` with a visualization URL.
"""
try:
pytree_dict = await py_tree_generator.generate(request.user_prompt)
pytree_dict = await py_tree_generator.generate(request.user_prompt, drone_state=request.drone_state)
return pytree_dict
except RuntimeError as e:
return {"error": str(e)}
@app.post("/debug_stage", response_model=dict)
async def debug_stage_endpoint(request: DebugStageRequest):
"""
Stage 分阶段调试:运行到指定 stage 并返回该 stage 的输出。
target_stage: 1=TaskUnderstanding, 2=ContextBinding, 3=BTDraft, 4=MiddlewareResolution, 5=MicroFilling, 6=ValidateAndPostprocess
"""
try:
result = py_tree_generator.run_debug_stage(
user_prompt=request.user_prompt,
drone_state=request.drone_state,
target_stage=request.target_stage,
)
return result
except Exception as e:
logging.exception("debug_stage 执行异常")
return {"error": str(e)}
@app.post("/execute_mission", response_model=dict)
async def execute_mission_endpoint(request: ExecuteMissionRequest):
"""

View File

@@ -3,6 +3,7 @@ from typing import Dict, Any
class GeneratePlanRequest(BaseModel):
user_prompt: str
drone_state: str = "on_ground" # "on_ground" or "in_air"
class ExecuteMissionRequest(BaseModel):
py_tree: Dict[str, Any]
@@ -10,3 +11,10 @@ class ExecuteMissionRequest(BaseModel):
class StatusUpdate(BaseModel):
node_id: str
status: int
class DebugStageRequest(BaseModel):
"""Stage 分阶段调试请求"""
user_prompt: str
drone_state: str = "on_ground"
target_stage: int = 1 # 1-6

View File

@@ -6,10 +6,12 @@ from pydantic import BaseModel, Field
SceneMode = Literal["simple", "scene1", "scene4"]
DroneState = Literal["on_ground", "in_air"]
class TaskUnderstanding(BaseModel):
scene_mode: SceneMode = "scene1"
drone_state: DroneState = "on_ground"
intent_type: str = "generic_mission"
requires_relative_target: bool = False
entities: Dict[str, Any] = Field(default_factory=dict)
@@ -33,6 +35,7 @@ class BTDraft(BaseModel):
user_prompt: str
allowed_nodes: Dict[str, List[str]] = Field(default_factory=dict)
llm_raw_json: Dict[str, Any]
macro_tree: Dict[str, Any]
parameter_requests: List[Dict[str, Any]]
reasoning_text: Optional[str] = None
final_prompt: str

View File

@@ -12,6 +12,20 @@ class GenerationOrchestrator:
async def generate(self, user_prompt: str) -> Dict:
understanding = self.stages.stage1_task_understanding(user_prompt)
context = self.stages.stage2_context_binding(user_prompt, understanding)
draft = self.stages.stage3_bt_planning(user_prompt, understanding, context)
return self.stages.stage4_validate_and_postprocess(user_prompt, understanding, context, draft)
if understanding.scene_mode == "simple":
draft = self.stages.stage3_macro_planning(user_prompt, understanding, context)
return self.stages.stage6_validate_and_postprocess(user_prompt, understanding, context, draft, draft.llm_raw_json)
# Round 1: 宏观规划
draft = self.stages.stage3_macro_planning(user_prompt, understanding, context)
# Middleware: 动态依赖解析
resolved_data = self.stages.stage4_middleware_resolution(draft)
# Round 2: 微观填参
final_tree = self.stages.stage5_micro_filling(draft, resolved_data, understanding)
# 验证与后处理
return self.stages.stage6_validate_and_postprocess(user_prompt, understanding, context, draft, final_tree)

View File

@@ -72,13 +72,24 @@ class PipelineStages:
self.generator = generator
def stage1_task_understanding(self, user_prompt: str) -> TaskUnderstanding:
logging.info("========== [Stage 1] Task Understanding ==========")
scene_mode = self.generator.llm_gateway.classify_scene(user_prompt)
intent_type = _infer_intent_type(user_prompt, scene_mode)
risk_flags = _extract_risk_flags(user_prompt)
requires_relative = "relative_reference_detected" in risk_flags
# 简单提取状态
drone_state: DroneState = "on_ground"
if "在空中" in user_prompt or "已起飞" in user_prompt:
drone_state = "in_air"
entities = {"raw_prompt": user_prompt}
logging.info(f"Task Understanding Results: mode={scene_mode}, state={drone_state}, intent={intent_type}, risks={risk_flags}")
return TaskUnderstanding(
scene_mode=scene_mode,
drone_state=drone_state,
intent_type=intent_type,
requires_relative_target=requires_relative,
entities=entities,
@@ -87,6 +98,7 @@ class PipelineStages:
)
def stage2_context_binding(self, user_prompt: str, understanding: TaskUnderstanding) -> ContextBinding:
logging.info("========== [Stage 2] Context Binding ==========")
scopes = ["location"]
if understanding.scene_mode != "simple":
scopes.append("pattern")
@@ -101,6 +113,8 @@ class PipelineStages:
relative_refs = _extract_relative_refs(user_prompt) if understanding.requires_relative_target else []
required_actions = _derive_required_actions(understanding.intent_type, understanding.scene_mode, understanding.risk_flags)
logging.info(f"Context Binding Results: Required Actions={required_actions}, RAG Scopes={scopes}")
return ContextBinding(
location_context=retrieved.get("location_context", ""),
pattern_context=retrieved.get("pattern_context", ""),
@@ -112,10 +126,12 @@ class PipelineStages:
required_actions=required_actions,
)
def stage3_bt_planning(self, user_prompt: str, understanding: TaskUnderstanding, context: ContextBinding) -> BTDraft:
def stage3_macro_planning(self, user_prompt: str, understanding: TaskUnderstanding, context: ContextBinding) -> BTDraft:
logging.info("========== [Stage 3] Macro Planning (Round 1) ==========")
include_extra_examples = understanding.intent_type == "generic_mission"
package = self.generator.prompt_composer.compose(
package = self.generator.prompt_composer.compose_macro(
scene_mode=understanding.scene_mode,
drone_state=understanding.drone_state,
intent_type=understanding.intent_type,
required_actions=context.required_actions,
risk_flags=understanding.risk_flags,
@@ -127,23 +143,132 @@ class PipelineStages:
include_extra_examples=include_extra_examples,
)
final_user_prompt = user_prompt + (package.user_augmentation or "")
logging.info(f"[Round 1] System Prompt Preview (first 500 chars):\n{package.system_prompt[:500]}...")
logging.info(f"[Round 1] User Prompt:\n{final_user_prompt}")
payload, reasoning_text, _raw_text = self.generator.llm_gateway.generate_json(
scene_mode=understanding.scene_mode,
system_prompt=package.system_prompt,
user_prompt=final_user_prompt,
)
final_prompt = f"=== System Prompt ===\n{package.system_prompt}\n\n=== User Prompt ===\n{final_user_prompt}"
final_prompt = f"=== System Prompt (Macro) ===\n{package.system_prompt}\n\n=== User Prompt ===\n{final_user_prompt}"
macro_tree = payload.get("macro_tree", {})
parameter_requests = payload.get("parameter_requests", [])
logging.info(f"[Round 1] Output Macro Tree:\n{json.dumps(macro_tree, ensure_ascii=False, indent=2)}")
logging.info(f"[Round 1] Output Parameter Requests:\n{json.dumps(parameter_requests, ensure_ascii=False, indent=2)}")
return BTDraft(
system_prompt=package.system_prompt,
user_prompt=final_user_prompt,
allowed_nodes=package.allowed_nodes,
llm_raw_json=payload,
macro_tree=macro_tree,
parameter_requests=parameter_requests,
reasoning_text=reasoning_text,
final_prompt=final_prompt,
)
def stage4_validate_and_postprocess(self, user_prompt: str, understanding: TaskUnderstanding, context: ContextBinding, draft: BTDraft) -> Dict[str, Any]:
payload = dict(draft.llm_raw_json)
def stage4_middleware_resolution(self, draft: BTDraft) -> Dict[str, Any]:
"""
中间层解析:读取 parameter_requests代理执行查询现有 RAG / 未来 MCP
"""
logging.info("========== [Stage 4] Middleware Resolution ==========")
resolved_data = {}
for req in draft.parameter_requests:
node = req.get("node")
entities = req.get("extracted_entities", {})
if not entities:
continue
# 如果包含 landmark说明是位置查询调用位置RAG
if "landmark" in entities:
query = entities["landmark"]
if "direction" in entities:
query += " " + entities["direction"]
if "distance" in entities:
query += " " + entities["distance"]
# 简单复用现有的地点检索逻辑
retrieved = self.generator.retriever.retrieve(query, scopes=["location"], n_results=1)
waypoint = self.generator.tool_runtime.build_precomputed_waypoint(query, retrieved.get("location_context", ""))
if waypoint:
resolved_data[f"{node}_location"] = waypoint
# 其他实体直接透传作为后续填参参考
resolved_data[f"{node}_entities"] = entities
logging.info(f"中间层已解析实体依赖数据:\n{json.dumps(resolved_data, ensure_ascii=False, indent=2)}")
return resolved_data
def stage5_micro_filling(self, draft: BTDraft, resolved_data: Dict[str, Any], understanding: TaskUnderstanding) -> Dict[str, Any]:
"""
Round 2 微观参数填空加载原子Schema让大模型只做填空题
"""
logging.info("========== [Stage 5] Micro Parameter Filling (Round 2) ==========")
import os
import json
# 从 nodes_schema.json 文件加载原子 Schema
schema_path = os.path.join(self.generator.prompts_dir, "atomic", "nodes_schema.json")
with open(schema_path, "r", encoding="utf-8") as f:
payload = json.load(f)
atomic_schema = {"actions": [], "conditions": []}
# 提取 Macro 树里所有的节点名称
used_nodes = set()
def _extract_nodes(node):
if not isinstance(node, dict): return
if "name" in node:
used_nodes.add(node["name"])
for child in node.get("children", []):
_extract_nodes(child)
if "child" in node:
_extract_nodes(node["child"])
_extract_nodes(draft.macro_tree.get("root", {}))
for item in payload.get("actions", []):
if item.get("name") in used_nodes:
atomic_schema["actions"].append(item)
for item in payload.get("conditions", []):
if item.get("name") in used_nodes:
atomic_schema["conditions"].append(item)
logging.info(f"按需动态注入的原子节点 Schema 列表: {list(used_nodes)}")
# 组装 Prompt
micro_prompt = self.generator.prompt_composer.compose_micro(
macro_tree=draft.macro_tree,
resolved_data=resolved_data,
atomic_schema=atomic_schema
)
logging.info(f"[Round 2] Micro Prompt Preview (first 300 chars):\n{micro_prompt[:300]}...")
# 再次调用模型
final_payload, reasoning, _ = self.generator.llm_gateway.generate_json(
scene_mode=understanding.scene_mode,
system_prompt=micro_prompt,
user_prompt="请直接输出完整的带有 params 参数的 JSON 树结构。",
)
logging.info(f"[Round 2] Final Micro Tree Output:\n{json.dumps(final_payload, ensure_ascii=False, indent=2)}")
# 将原始推理文本记录下来
if draft.reasoning_text:
reasoning_full = f"=== Round 1 Reasoning ===\n{draft.reasoning_text}\n\n=== Round 2 Reasoning ===\n{reasoning}"
draft.reasoning_text = reasoning_full
return final_payload
def stage6_validate_and_postprocess(self, user_prompt: str, understanding: TaskUnderstanding, context: ContextBinding, draft: BTDraft, final_tree: Dict[str, Any]) -> Dict[str, Any]:
logging.info("========== [Stage 6] Validate and Postprocess ==========")
payload = dict(final_tree)
if context.relative_refs and understanding.scene_mode != "simple":
payload.setdefault("context", {})
payload["context"]["relative_refs"] = context.relative_refs
@@ -160,6 +285,6 @@ class PipelineStages:
if draft.reasoning_text:
self.generator.save_reasoning_content(draft.reasoning_text)
self.generator._save_history(user_prompt, payload)
logging.info("✅ 成功生成并验证了PytreePipeline")
logging.info("✅ 成功生成并验证了Pytree两阶段Pipeline")
return payload

View File

@@ -4,7 +4,7 @@ import json
import os
from dataclasses import dataclass
from functools import lru_cache
from typing import Dict, List, Sequence, Set
from typing import Any, Dict, List, Sequence, Set
from .manifest_loader import ManifestPromptLoader
@@ -23,13 +23,14 @@ class PromptComposer:
self._simple_prompt = self.loader.load_text_file("simple_mode_prompt.txt")
@lru_cache(maxsize=64)
def _cached_scene_parts(self, scene_mode: str) -> tuple[str, str, str, str]:
# 固定骨架
header = self._load_partial("header.txt")
required_fields = self._load_partial("required_fields.txt")
standard_template = self._load_partial("standard_template.txt")
examples = self._load_partial("scene1_examples.txt" if scene_mode == "scene1" else "scene4_examples.txt")
return header, required_fields, standard_template, examples
def _cached_macro_scene_parts(self, drone_state: str) -> tuple[str, str]:
header = self._load_partial("macro_header.txt")
template = self._load_partial("template_ground.txt" if drone_state == "on_ground" else "template_air.txt")
return header, template
@lru_cache(maxsize=64)
def _cached_micro_scene_parts(self) -> str:
return self._load_partial("micro_header.txt")
def _load_partial(self, file_name: str) -> str:
path = os.path.join(self.prompts_dir, "partials", file_name)
@@ -87,9 +88,10 @@ class PromptComposer:
]
)
def compose(
def compose_macro(
self,
scene_mode: str,
drone_state: str,
intent_type: str,
required_actions: Sequence[str],
risk_flags: Sequence[str],
@@ -100,11 +102,11 @@ class PromptComposer:
user_aug = self._build_user_augmentation(context_blocks)
return PromptPackage(system_prompt=self._simple_prompt, user_augmentation=user_aug, allowed_nodes={})
header, required_fields, standard_template, examples = self._cached_scene_parts(scene_mode)
header, template = self._cached_macro_scene_parts(drone_state)
selected_nodes = self._slice_nodes(required_actions, risk_flags, scene_mode)
node_snippet = self._build_nodes_snippet(selected_nodes)
parts: List[str] = [header, node_snippet, required_fields, standard_template, examples]
parts: List[str] = [header, node_snippet, template]
common_rules = self._load_partial("common_rules.txt")
if common_rules:
parts.append(common_rules)
@@ -116,6 +118,19 @@ class PromptComposer:
system_prompt = "\n\n".join(p for p in parts if p).strip()
return PromptPackage(system_prompt=system_prompt, user_augmentation=self._build_user_augmentation(context_blocks), allowed_nodes=selected_nodes)
def compose_micro(self, macro_tree: Dict[str, Any], resolved_data: Dict[str, Any], atomic_schema: Dict[str, Any]) -> str:
header = self._cached_micro_scene_parts()
parts = [
header,
"## 1. 原宏观骨架树 (macro_tree)",
"```json\n" + json.dumps(macro_tree, ensure_ascii=False, indent=2) + "\n```",
"## 2. 确切数据字典 (resolved_data)",
"```json\n" + json.dumps(resolved_data, ensure_ascii=False, indent=2) + "\n```",
"## 3. 原子节点规范 (atomic_schema)",
"```json\n" + json.dumps(atomic_schema, ensure_ascii=False, indent=2) + "\n```",
]
return "\n\n".join(parts)
def _build_user_augmentation(self, context_blocks: Dict[str, str]) -> str:
ordered = [("地点知识", "location"), ("任务模式", "pattern"), ("规则知识", "rules")]
chunks: List[str] = []

View File

@@ -0,0 +1,157 @@
{
"actions": [
{
"name": "takeoff",
"desc": "起飞并达到指定高度",
"params": {
"altitude": "float,默认2.0"
}
},
{
"name": "land",
"desc": "降落",
"params": {
"mode": "'current'/'home'"
}
},
{
"name": "fly_to_waypoint",
"desc": "飞往指定坐标",
"params": {
"x": "float,±10000",
"y": "float,±10000",
"z": "float,[1,5000]",
"acceptance_radius": "float,默认2.0"
}
},
{
"name": "fly_sequence",
"desc": "按顺序飞往多个航点",
"params": {
"waypoints": "list[dict] (e.g. [{'x':10,'y':20,'depth':5}, ...]depth可选)",
"coordinate_frame": "'global'/'local_enu'",
"speed": "float,可选"
}
},
{
"name": "move_direction",
"desc": "向指定方向移动",
"params": {
"direction": "north/south/east/west/forward/backward/left/right/up/down",
"distance": "float,默认0",
"speed": "float,可选"
}
},
{
"name": "approach_target",
"desc": "靠近目标",
"params": {
"target_class": "string,要趋近的目标类别",
"description": "string,可选,目标属性描述",
"stop_distance": "float,默认2.0",
"speed": "float,可选,期望的逼近速度"
}
},
{
"name": "rotate",
"desc": "旋转",
"params": {
"angle": "float,无人机自身旋转角度(正数逆时针,负数顺时针)",
"angular_velocity": "float,默认1.0"
}
},
{
"name": "rotate_search",
"desc": "旋转并搜索目标",
"params": {
"target_class": "要搜索的目标类别",
"description": "string,可选,目标属性描述",
"step_angle": "float,可选,每一步旋转的角度",
"total_rotation": "float,可选,总共旋转搜索的角度"
}
},
{
"name": "manual_confirmation",
"desc": "等待人工确认",
"params": {}
},
{
"name": "loiter",
"desc": "悬停",
"params": {
"duration": "int,秒,默认0"
}
},
{
"name": "object_detect",
"desc": "检测目标",
"params": {
"target_class": "检测的目标类别",
"description": "可选",
"count": 1
}
},
{
"name": "search_pattern",
"desc": "按模式搜索",
"params": {
"pattern_type": "spiral/grid",
"center_x": "float,±10000",
"center_y": "float,±10000",
"center_z": "float,[1,5000]",
"radius": "float,[5,1000]",
"target_class": "目标类别",
"description": "可选",
"count": 1
}
},
{
"name": "track_object",
"desc": "跟踪目标",
"params": {
"target_class": "目标类别",
"description": "可选",
"track_time": "int,秒,默认10",
"min_confidence": "float,默认0.7",
"safe_distance": "int,默认10"
}
},
{
"name": "deliver_payload",
"desc": "投放物资",
"params": {
"payload_type": "string",
"release_altitude": "[2,100]默认5"
}
},
{
"name": "return_emergency",
"desc": "紧急返航",
"params": {
"reason": "string"
}
},
{
"name": "take_photos",
"desc": "拍照",
"params": {
"target_class": "目标类别",
"description": "可选",
"track_time": "int,秒,默认10",
"min_confidence": "float,默认0.7",
"safe_distance": "int,默认10"
}
}
],
"conditions": [
{
"name": "object_detected",
"desc": "是否检测到目标",
"params": {
"target_class": "目标类别(必传)",
"description": "可选",
"count": 1
}
}
]
}

View File

@@ -1,46 +1,55 @@
## 六、高频错误规避
1. 控制流节点的 `type` 必须是 `"Sequence"`, `"Selector"` 或 `"Parallel"`
2. **人工确认节点 (`manual_confirmation`) 使用原则**
- **必须添加**:仅当指令中明确包含“我确认”、“等待确认”、“经允许”、“我通过后”等人工介入关键词时,**必须**在相应动作前添加此节点。
- **严禁添加**:若指令未提及上述关键词,**严禁**主动添加此节点(即使是拍照、返航或降落等动作,只要用户没说要确认,就直接执行)。
3. 在条件节点 `object_detected` 执行前,必须先安排搜索类动作节点(优先使用 `rotate_search`,仅当需大范围移动时用 `search_pattern`),确保无人机主动寻找目标。
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
10. **无地点名词禁止飞点**:当指令仅包含“方向 + 距离”且**没有具体地点名词**时,**无论无人机在地面或空中**,都必须使用 `move_direction`,严禁使用 `fly_to_waypoint` 或任何地点坐标。
示例(必须遵守)
- “无人机当前在地面快速去往东边60米有身穿白色衣服头戴帽子的男子在挟持他人对其进行拍照。” → 必须使用 `move_direction`east, 60不得生成 `fly_to_waypoint` 或引用任何地点坐标。
- “无人机当前在空中快速去往东边60米有身穿白色衣服头戴帽子的男子在挟持他人对其进行拍照。” → 同样必须使用 `move_direction`east, 60不得生成 `fly_to_waypoint` 或引用任何地点坐标。
## 六、高频错误规避(硬规则,必须遵守)
0. 严格 JSON 输出:
- 只能输出一个 JSON 对象。
- 禁止输出 Markdown、解释、注释、代码块标记。
- 禁止在 JSON 中出现 // 或 /* */ 注释,禁止尾随逗号。
1. 控制流节点的 type 只能是 "Sequence" / "Selector" / "Parallel"。
2. 人工确认节点manual_confirmation
- 必须添加:仅当指令明确包含“我确认 / 等待确认 / 经允许 / 我通过后 / 允许后”等人工介入关键词时,必须在相应动作前添加 manual_confirmation。
- 严禁添加:指令未提及人工确认时,严禁主动添加 manual_confirmation。
3. 搜索-条件-拍照链路:
- 若出现 condition: object_detected则在其前必须有搜索类 action优先 rotate_search仅当需大范围移动时用 search_pattern
- 若使用 rotate_search 或 object_detect则后续必须出现 object_detected同 target_class
4. 当前位置任务:
- 指令明确“在当前位置 / 原地 / 不用过去 / 就在这”时,无需 fly_to_waypoint。
5. 严格区分无人机状态:
- 指令明确“当前在空中”:严禁使用 system_checks 与 takeoff。
- 指令明确“当前在地面”:如需要飞行任务,优先 system_checks -> takeoff 再执行后续动作。
6. fly_to_waypoint 与 return_emergency 辨析:
- 若指令包含具体目的地(例如“回到广场 / 飞回大门 / 去机库”(即使包含“紧急”二字)),必须使用 fly_to_waypoint绝对禁止使用 return_emergency。
- return_emergency 仅用于“无明确目的地”的立即返航(默认回起飞点)。
7. approach_target 使用:
- 指令提及“靠近 / 飞近 / 贴近 / 距离XX米拍清楚”时必须在 take_photos 之前使用 approach_target。
- 未提及上述关键词时,严禁添加 approach_target。
8. 方向移动优先:
- 当指令仅为“方向 + 距离”且没有具体地点名词时(例如“往东 60 米”),必须使用 move_direction严禁使用 fly_to_waypoint 或任何地点坐标。
9. loiter 参数名:
- loiter 的时间参数一律使用 params.duration单位禁止使用 time。
## 七、坐标计算规则(东南天坐标系 ENU
本系统统一使用东南天ENU坐标系
- **X轴**:正方向为**东** (East),负方向为**西** (West)
- **Y轴**:正方向为**南** (South)向为**北** (North)
- **Z轴**:正方向为**天** (Up),负方向为**地** (Down)
- X轴正方向 东(East),负方向 西(West)
- Y轴正方向 南(South),负方向 北(North)
- Z轴正方向 上(Up),负方向 (Down)
**仅当指令涉及前往“具体地点”(如广场、大门)的偏移位置时,才计算绝对坐标并使用`fly_to_waypoint`**
仅当指令涉及“具体地点 + 方向 + 距离”的偏移位置时,才计算绝对坐标并使用 fly_to_waypoint
- 单一方向(东/西/南/北/上/下):必须先调用 calc_offset_enu再使用 fly_to_waypoint。
- 中文复合方位(东南/西北/南偏东10度 等):必须先调用 calc_offset_esu_direction_text再使用 fly_to_waypoint。
- 禁止将复合方位简化为单一方向。
当指令包含“具体地点 + 方向 + 距离”的偏移时,按方向类型选择工具:
- **单一方向**(东/西/南/北/上/下如“广场西边200米”**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数:
- `base`: 参考地点的ENU坐标含x/y/z
- `direction`: east/west/north/south/up/down
- `distance`: 偏移距离(米)
- **中文复合方位**(东南/西北/东北/西南/南偏东10度/北偏西15度等**必须**先调用工具`calc_offset_esu_direction_text`计算绝对坐标,再使用`fly_to_waypoint`。工具参数:
- `base`: 参考地点的ENU坐标含x/y/z
- `direction_text`: 中文方位原文如“东南”“南偏东10度”
- `distance`: 偏移距离(米)
- **禁止简化**:当出现“东南/西北/东北/西南/南偏东/北偏西”等复合方位时,禁止将其简化为单一方向(如仅“东”或仅“南”)。
示例(必须遵守):
- “飞到广场东南方向100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"东南"``distance` 为 `100`,再使用 `fly_to_waypoint`。
- “飞到广场南偏东10度100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"南偏东10度"``distance` 为 `100`。
当指令只有“方向 + 距离”且**没有具体地点名词**时,**禁止**调用`calc_offset_enu`,必须使用`move_direction`。
当指令描述“附近/边上/区域内”等模糊位置且**无方向+距离**时,视为到该地点本身,不做偏移计算。
当指令只有“方向 + 距离”且没有具体地点名词时,禁止调用 calc_offset_enu必须使用 move_direction。
## 八、输出要求
仅输出1个严格符合上述所有规则的JSON对象。
仅输出 1 个严格符合上述所有规则的 JSON 对象。

View File

@@ -2,184 +2,93 @@
"actions": [
{
"name": "takeoff",
"params": {
"altitude": "float[1,100]默认2"
}
"desc": "起飞并达到指定高度"
},
{
"name": "land",
"params": {
"mode": "'current'/'home'"
}
"desc": "降落到地面"
},
{
"name": "fly_to_waypoint",
"params": {
"x": "±10000",
"y": "±10000",
"z": "[1,5000]",
"acceptance_radius": "默认2.0",
"desc": "仅当指令提及具体地点(如'去广场'、'去大门')或需计算明确坐标时使用"
}
"desc": "飞往指定坐标"
},
{
"name": "fly_sequence",
"params": {
"waypoints": "list[dict] (e.g. [{'x':10,'y':20,'depth':5}, ...]depth可选不填则保持当前高度)",
"coordinate_frame": "'global'/'local_enu'global:经纬度, local_enu:以起飞点为原点的东南天坐标系)",
"speed": "float,可选"
}
"desc": "按顺序飞往多个航点"
},
{
"name": "move_direction",
"params": {
"direction": "north/south/east/west/forward/backward/left/right",
"distance": "[1,10000],缺省则持续移动",
"speed": "float,可选",
"desc": "当指令仅包含'往东/西...飞xx米'且无具体地点名词时,必须使用此节点"
}
"desc": "向指定方向移动(东西南北等)"
},
{
"name": "approach_target",
"params": {
"target_class": "string,要趋近的目标类别",
"description": "string,可选,目标属性描述",
"stop_distance": "float,期望的最终停止距离",
"speed": "float,可选,期望的逼近速度"
}
"desc": "靠近已发现的目标"
},
{
"name": "rotate",
"params": {
"angle": "float,无人机自身旋转角度(正数逆时针,负数顺时针)",
"angular_velocity": "rad/s,旋转角速度"
}
"desc": "原地旋转自身"
},
{
"name": "rotate_search",
"params": {
"target_class": "同object_detect",
"description": "string,可选,目标属性描述",
"step_angle": "float,可选,每一步旋转的角度",
"total_rotation": "float,可选,总共旋转搜索的角度"
}
"desc": "原地旋转并搜索目标"
},
{
"name": "manual_confirmation",
"params": {}
"desc": "等待人工确认"
},
{
"name": "loiter",
"params": {
"duration": "[1,600]秒/until_condition:可选"
}
"desc": "原地悬停等待"
},
{
"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"
}
"desc": "检测视野内的目标"
},
{
"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"
}
"desc": "按螺旋或网格模式移动并搜索目标"
},
{
"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"
}
"desc": "持续跟踪移动的目标"
},
{
"name": "deliver_payload",
"params": {
"payload_type": "string",
"release_altitude": "[2,100]默认5"
}
"desc": "投放物资"
},
{
"name": "system_checks",
"params": {
"check_level": "basic/comprehensive只能在起飞takeoff节点前使用空中无需使用该节点"
}
},
{
"name": "return_emergency",
"params": {
"reason": "string此节点仅用于【无明确目的地】的立即返航默认回起飞点。若指令包含“回到xx地”、“去xx地”即使包含“紧急”二字**严禁**使用此节点必须使用fly_to_waypoint"
}
"name": "return",
"desc": "返航,回到起飞点"
},
{
"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"
}
"desc": "对目标进行拍照"
}
],
"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"
}
"desc": "判断视野中是否检测到目标"
}
],
"control_flow": [
{
"name": "Sequence",
"params": {},
"children": "子节点数组(按序执行,全成功则成功)"
"desc": "顺序执行子节点,全成功则成功"
},
{
"name": "Selector",
"params": {
"memory": "默认true"
},
"children": "子节点数组(执行到成功为止)"
"desc": "执行子节点到第一个成功为止"
},
{
"name": "Parallel",
"params": {
"policy": "all_success/success_on_one"
},
"children": "子节点数组同时执行默认all_success"
"desc": "同时执行子节点"
}
],
"decorators": [
{
"name": "SuccessIsFailure",
"params": {},
"child": "单一子节点(将子节点的成功结果反转为失败)"
"desc": "将子节点的成功反转为失败"
}
]
}
}

View File

@@ -1 +0,0 @@
任务根据用户任意任务指令生成结构化可执行的无人机行为树PytreeJSON。**仅输出单一JSON对象无任何自然语言、注释或额外内容**。

View File

@@ -0,0 +1,23 @@
任务:根据用户的自然语言指令,规划无人机的宏观执行流程结构,并提取执行该流程所需的外部参数。
你现在是第一阶段“宏观规划与意图提取”AI。你只需要做两件事
1. 分析意图并排出正确的骨架树(不需要填充任何 parameters/params
2. 从用户指令中提取出需要查询确切位置或目标属性的实体清单(如地标、方向、距离、识别目标)。
**严格约束**:仅输出符合以下 JSON 格式的数据,**禁止**包含任何外部分析、Markdown 标记外的纯文本,或者多余的字段。
输出格式约定:
```json
{
"macro_tree": { ... 纯结构树 ... },
"parameter_requests": [
{
"node": "节点名称",
"intent": "对该节点意图的简短描述",
"extracted_entities": {
"实体key": "实体value"
}
}
]
}
```

View File

@@ -0,0 +1,5 @@
任务根据给定的一棵无参结构树macro_tree、具体的确切数据字典resolved_data以及所需原子节点的参数规范说明atomic_schema补充填满树中各个节点的 `params`。
你现在是第二阶段“微观参数填空”AI。你不需要大改结构你的核心任务是将 `resolved_data` 中的数值或字符串,按照 `atomic_schema` 的要求,填入到对应节点的 `params` 中。
**严格约束**:仅输出单一 JSON 对象(即完整的、包含 `params` 的 PyTree。**禁止**输出任何自然语言分析、前后文或额外注释。

View File

@@ -1,8 +0,0 @@
## 二、节点必填字段后端Schema强制要求缺一验证失败
每个节点必须包含以下字段,字段名/类型不可自定义:
1. **`type`**
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`,装饰器节点→`"decorator"`
2. **`name`**必须是上述JSON中定义的`name`值;
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数;
4. **`children`**:仅控制流节点必含(子节点数组);
5. **`child`**:仅装饰器节点必含(单一子节点对象,非数组)。

View File

@@ -1,18 +0,0 @@
## 三、标准任务结构模板(单次起降流程)
当无人机在地面时,大多数任务应遵循“起飞 -> 移动 -> 条件判断 -> 执行 -> 返航/降落”的单次闭环流程,参考结构如下:
```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}}, // 接近目标区域
// --- 核心任务区 (根据指令替换) ---
// 默认不需要降落节点,除非用户明确要求
]
}
}
```
而当无人机在空中时则无需system_checks与takeoff环节直接执行用户任务即可

View File

@@ -0,0 +1,31 @@
## 三、顶层 JSON 结构规范与模板
当无人机状态为 **in_air空中** 时,通常的流程包含:方向移动或直接飞行至目标点 -> (执行任务)。**严禁**使用 `system_checks` 与 `takeoff`。
如果指令要求“沿外围”,需使用 `fly_sequence`。
你必须同时输出 `macro_tree`(必须剔除 params和 `parameter_requests` 两个字段。
结构范例(注意:此仅为结构展示,不代表真实逻辑):
```json
{
"macro_tree": {
"root": {
"type": "Sequence",
"name": "MainTask",
"children": [
{"type": "action", "name": "fly_to_waypoint"},
{"type": "action", "name": "rotate_search"}
]
}
},
"parameter_requests": [
{
"node": "fly_to_waypoint",
"intent": "前往广场中心",
"extracted_entities": {
"landmark": "广场中心"
}
}
]
}
```

View File

@@ -0,0 +1,32 @@
## 三、顶层 JSON 结构规范与模板
当无人机状态为 **on_ground地面** 时,通常的流程包含:`system_checks` -> `takeoff` -> (飞行至目标点等后续动作)。
如果指令要求“沿外围”,需使用 `fly_sequence`。
你必须同时输出 `macro_tree`(必须剔除 params和 `parameter_requests` 两个字段。
结构范例(注意:此仅为结构展示,不代表真实逻辑):
```json
{
"macro_tree": {
"root": {
"type": "Sequence",
"name": "MainTask",
"children": [
{"type": "action", "name": "system_checks"},
{"type": "action", "name": "takeoff"},
{"type": "action", "name": "fly_to_waypoint"}
]
}
},
"parameter_requests": [
{
"node": "fly_to_waypoint",
"intent": "飞行到目标点",
"extracted_entities": {
"landmark": "广场西边"
}
}
]
}
```

View File

@@ -1,24 +1,24 @@
scenes:
system:
- header.txt
- core_nodes.json
- required_fields.txt
- standard_template.txt
- scene4_examples.txt
- system_extra_examples.txt
- common_rules.txt
- macro_header.txt
- core_nodes.json
- template_ground.txt
- template_air.txt
- common_rules.txt
scene1:
- header.txt
- core_nodes.json
- required_fields.txt
- standard_template.txt
- scene1_examples.txt
- macro_header.txt
- core_nodes.json
- template_ground.txt
- template_air.txt
- common_rules.txt
scene4:
- header.txt
- core_nodes.json
- required_fields.txt
- standard_template.txt
- scene4_examples.txt
- common_rules.txt
- macro_header.txt
- core_nodes.json
- template_ground.txt
- template_air.txt
- common_rules.txt
simple:
- simple_mode_prompt.txt
- simple_mode_prompt.txt

View File

@@ -1,64 +1,9 @@
你是一个严格的指令场景分类器。只输出一个JSON对象,不要输出解释或多余文本
根据用户指令与下述场景定义判断其属于“simple / scene1 / scene4”之一
你是指令分类器。只输出一个JSON,无其它内容
输入:无人机状态{on_ground/in_air}+指令
输出仅三选一:{"mode":"simple"}、{"mode":"scene1"}、{"mode":"scene4"}。
输出格式(严格遵守)
{"mode":"simple"} 或 {"mode":"scene1"} 或 {"mode":"scene4"}
判定规则(按顺序执行):
1. 先判断是否满足 scene1 或 scene4 的核心特征;若满足,输出对应模式。
2. 再判断是否满足 simple 的定义(见下);若满足,输出 simple。
3. simple 是“单节点即可完成”的正面定义,不是“既不是 scene1 也不是 scene4”的兜底只有明确符合 simple 定义时才输出 simple。
4. 仅允许以上三种取值,禁止输出其他字段或文本。
—— 场景定义(核心特征 + 任务类型)——
scene1方位态势感知巡查类
- 核心特征:指令依赖“当前可见建筑物”的实时方位/态势——即需要以“面前大楼/这栋楼/当前这栋”等为参照,知道“楼在哪、当前相对楼的位置与高度”,才能规划绕楼、沿外围、在某一高度等。典型表述:面前大楼、这栋楼、绕楼/沿着外围/绕着外围、在楼某高度如12米高处、先上升/下降再绕楼等。
- 任务类型:绕楼外围巡查;在楼外围或指定高度搜索(窗户/杂物/人员等)并拍照;先升降再绕楼侦察。对实时方位、相对建筑物的位置感知要求高。
- 判断要点:若指令中“去某地/飞某地”的“某地”是“面前大楼/这栋楼”或与之强绑定如楼12米高处、楼外围则归 scene1。
scene4命名地点序列复合任务类
- 核心特征指令以“命名地点或区域”为目标如广场、广场南边、施工区域、东边60米等不依赖“面前是哪栋楼”的实时方位感知任务多为“先到某地再在该地做某事”的序列或复合动作搜索、拍照、监控、返航、降落、确认后拍照等
- 任务类型:到某地查找/搜索目标并拍照;到某地后返航/降落;确认后再拍照/返航;持续监控一段时间;到某区域发现某类目标后靠近拍照;紧急回到某地后降落等。对“面前大楼”式的实时方位要求不高。
- 判断要点若指令中的目的地是具名区域或方位广场、广场边上、施工区域、南边40米等且包含“查找/搜索/拍照/返航/监控/确认后”等复合步骤,或需要“先到再做”,则归 scene4。
simple单节点简单指令
- 定义:整条指令有且仅需一个原子动作节点即可完成,无需控制流、无需多步序列。是否“单节点”必须结合无人机当前状态判断。
- 与无人机状态的关系:
- “飞到某地/去某地”:若当前状态为地面,则必须先起飞再飞抵,至少两步,不是 simple应归 scene4或按 scene1 特征判断是否 scene1若当前状态为空中则可直接 fly_to_waypoint为 simple。
- “起飞”“降落”“往某方向飞某距离”(空中时)等单一动作,为 simple。
- 判断要点:先看是否属于 scene1 或 scene4若不属于再看“在当前状态下是否真的只需一个动作”。不能仅凭“不是 scene1、不是 scene4”就判为 simple。
—— 场景指令样例 ——
scene1 示例(围绕“面前大楼/这栋楼”的方位感知与绕楼任务):
- 无人机当前在地面去面前大楼的12米高处绕着外围看有没有打开的窗户发现则进行拍照。
- 无人机当前在地面去面前大楼的12米高处沿着外围查找所有打开的窗户并拍照。
- 无人机当前在空中再往上飞3米接着绕这栋楼外围侦察有没有打开的窗户看到了就拍照传回来。
- 无人机当前在地面去面前大楼的12米高处绕着外围巡视杂物堆积现象发现则进行拍照。
- 无人机当前在地面去面前大楼的12米高处沿着外围查找所有的杂物堆积并拍照。
- 无人机当前在空中往下飞3米接着绕这栋楼外围侦察有没有杂物堆积看到了就拍照传回来。
- 无人机当前在地面去面前大楼的12米高处绕着外围看有没有人发现则进行拍照。
- 无人机当前在地面去面前大楼的12米高处沿着外围逆时针查找所有的人并拍照。
- 无人机当前在空中再往上飞3米接着绕这栋楼外围侦察有没有人看到了就拍照传回来。
scene4 示例(以命名地点/区域为目标的序列或复合任务):
- 无人机当前在地面,到广场查找穿红色衣服的人,找到后近距离拍照。
- 无人机当前在空中,回到广场,对戴帽子的人进行拍照。
- 无人机当前在空中去广场南边40米对过往的公交车拍张照然后返航。
- 无人机当前在地面,到广场查找绿色公交车,看见了拍个照片。
- 无人机当前在空中,搜索小汽车,搜索到了我确认后再决定要不要拍照。
- 无人机当前在空中,搜索小汽车,搜索到了拍张照,我确认后再决定要不要返航。
- 无人机当前在空中往广场南边飞40米持续监控5分钟发现人就拍照告诉我到时间可以返航。
- 无人机当前在地面,到广场边上的施工区域内,发现有没带安全帽的飞近后拍照。
- 无人机当前在空中,紧急回到广场,看见了红绿灯之后直接降落。
- 无人机当前在地面快速去往东边60米有身穿白色衣服头戴帽子的男子在挟持他人对其进行拍照。
- 无人机当前在空中离白色衣服戴帽子的人太远了照片看不清贴近到3米距离拍拍完可以直接返航。
simple 示例(单节点 + 注意无人机状态):
- 无人机当前在地面,起飞。
- 无人机当前在空中,飞到广场。(空中且仅“飞到某地” → 单节点)
- 无人机当前在空中,往北飞 50 米。(空中且仅方向+距离 → 单节点)
- 无人机当前在地面,起飞到 10 米。(仅起飞到高度 → 单节点)
非 simple对比
- 无人机当前在地面,飞到广场。(在地面“飞到某地”需先起飞再飞抵 → 非单节点,归 scene4
规则
1. 指令含“面前”→scene1
2. 状态=in_air指令是飞到某地/飞到某地+方位距离/往某方向飞X米/降落/旋转/悬停 →simple
3. 状态=on_ground指令含去/飞到/回到某地 →非simple
4. 多动作/序列任务→scene4

View File

@@ -1,54 +1,25 @@
你是一个无人机简单指令执行规划器。你的任务当用户给出“简单指令”单一原子动作即可完成输出一个严格的JSON对象。
你是一个无人机简单指令执行规划器。
假设输入一定是“单一原子动作即可完成”的简单指令。你的任务是输出一个严格的JSON对象。
说明:用户消息末尾可能附带【地点知识】等参考信息(来自 RAG 检索),用于解析"飞到某地"或"某地东边X米"类指令的坐标。请根据参考信息推断 fly_to_waypoint 的 x/y/z若无坐标则用合理估计值。
输出要求(必须遵守):
- 只输出一个JSON对象不要任何解释或多余文本。
- JSON结构
- JSON结构固定为
{"root":{"type":"action","name":"<action_name>","params":{...}}}
- root节点必须是action类型节点,不能是控制流节点。
- root 节点必须是 action,禁止输出 Sequence/Selector/Parallel 等控制流节点。
- params 只能包含该动作定义内的字段,禁止自定义字段。
- 数值请使用数字类型(例如 10.0)。
可用动作simple 模式只允许从下列动作中选择其一):
1) takeoff: {"altitude": float[1,100]}(仅当地面起飞)
2) land: {"mode": "current"|"home"}
3) fly_to_waypoint: {"x": number, "y": number, "z": number, "acceptance_radius": number(可选)}
4) move_direction: {"direction": "north"|"south"|"east"|"west"|"forward"|"backward"|"left"|"right"|"up"|"down", "distance": number}
5) rotate: {"angle": number, "angular_velocity": number(可选)}
6) loiter: {"duration": number}
示例:
- “起飞到10米” → {"root":{"type":"action","name":"takeoff","params":{"altitude":10.0}}}
- “移动到(120,80,20)” → {"root":{"type":"action","name":"fly_to_waypoint","params":{"x":120.0,"y":80.0,"z":20.0,"acceptance_radius":2.0}}}
- “飞机自检” → {"root":{"type":"action","name":"system_checks","params":{"check_level":"comprehensive"}}}
—— 可用节点定义——
```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"}},
{"name":"fly_sequence","params":{"waypoints":"list[dict] (e.g. [{'x':10,'y':20,'depth':5}, ...]depth可选不填则保持当前高度)","coordinate_frame":"'global'/'local_enu'global:经纬度, local_enu:以起飞点为原点的东南天坐标系)","speed":"float,可选"}},
{"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right/up/down","distance":"[1,10000],缺省持续移动","speed":"float,可选"}},
{"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":"string,要搜寻的目标类别","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":"strike_target","params":{"target_class":"同object_detect","description":"可选,目标属性","count":"默认1"}},
{"name":"battle_damage_assessment","params":{"target_class":"同object_detect","assessment_time":"[5,60]默认15"}},
{"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"}},
{"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"}},
{"name":"target_destroyed","params":{"target_class":"同object_detect","description":"可选,目标属性","confidence":"[0.5,1.0]默认0.8"}},
{"name":"time_elapsed","params":{"duration":"[1,2700]秒"}},
{"name":"gps_status","params":{"min_satellites":"int[6,15]必传如8"}}
]
}
```
—— 参数约束——
- takeoff.altitude: [1, 100]
- fly_to_waypoint.z: [1, 5000]
- fly_to_waypoint.x,y: [-10000, 10000]
- search_pattern.radius: [5, 1000]
- move_direction.distance: [1, 10000]
- 若参考知识提供坐标,必须使用并裁剪到约束范围内
- “往北飞50米” → {"root":{"type":"action","name":"move_direction","params":{"direction":"north","distance":50.0}}}
- “飞到(120,80,20)” → {"root":{"type":"action","name":"fly_to_waypoint","params":{"x":120.0,"y":80.0,"z":20.0,"acceptance_radius":2.0}}}

View File

@@ -226,12 +226,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"params": {
"type": "object",
"properties": {
"target_class": {"type": "string", "enum": target_classes},
"target_class": {"type": "string"},
"description": {"type": "string"},
"count": {"type": "integer", "minimum": 1}
"count": {"type": ["integer", "string"]}
},
"required": ["target_class"],
"additionalProperties": False
"required": ["target_class"]
}
}
}
@@ -249,12 +248,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"params": {
"type": "object",
"properties": {
"target_class": {"type": "string", "enum": target_classes},
"target_class": {"type": "string"},
"description": {"type": "string"},
"count": {"type": "integer", "minimum": 1}
"count": {"type": ["integer", "string"]}
},
"required": ["target_class"],
"additionalProperties": False
"required": ["target_class"]
}
}
}
@@ -275,7 +273,7 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"threshold": {"type": "number", "minimum": 0.0, "maximum": 1.0}
},
"required": ["threshold"],
"additionalProperties": False
"additionalProperties": True
}
}
}
@@ -296,7 +294,7 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"min_satellites": {"type": "integer", "minimum": 6, "maximum": 15}
},
"required": ["min_satellites"],
"additionalProperties": False
"additionalProperties": True
}
}
}
@@ -331,7 +329,7 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"speed": {"type": "number"}
},
"required": ["waypoints", "coordinate_frame"],
"additionalProperties": False
"additionalProperties": True
}
}
}
@@ -355,7 +353,7 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
"speed": {"type": "number"}
},
"required": ["target_class", "stop_distance"],
"additionalProperties": False
"additionalProperties": True
}
}
}
@@ -404,7 +402,7 @@ def _generate_simple_mode_schema(allowed_actions: set) -> dict:
}
},
"required": ["root"], # 顶层必须有root字段
"additionalProperties": False # 顶层只能有root字段不能有其他字段如mode等
"additionalProperties": True # 顶层只能有root字段不能有其他字段如mode等
}
return schema
@@ -720,6 +718,10 @@ class PyTreeGenerator:
return self._load_prompt(fallback_file)
scene_map = manifest.get("scenes", manifest)
if scene_map is None:
scene_map = manifest
logging.error(f"DEBUG: manifest={manifest} scene_map={scene_map} type={type(scene_map)}")
fragments = scene_map.get(scene_key)
if not fragments:
logging.warning(f"提示词清单缺少场景配置 -> {scene_key}")