# 无人机行为规划后端系统(重建版)需求说明 > 目标:基于端侧 Qwen3-4B(llama-server)和行为树,重建一个从自然语言到无人机行为树 JSON 的后端系统,支持未来扩展 RAG 和动态语义地图。 ## 一、总体架构理念(务必遵守) 1. **大小脑解耦** - 大模型(4B,小模型) = 「大脑」:只做语义理解、意图拆解、行为树结构生成,不直接管起飞/降落/避障。 - Python + py_trees + PX4/仿真 = 「小脑」:负责状态机管理、物理安全、控制执行(起飞、悬停、返航等)。 2. **四层流水线结构** - 🔴 Layer 0:感知层(Perception & State) 纯代码。维护一个全局状态黑板 `DroneStateBlackboard`,至少包含: - `is_in_air: bool` - `position: {x, y, z}`(ENU) - 后续可扩展电量、模式等。 - 🟡 Layer 1:意图路由层(Stage 1 Intent Router) 调用 LLM 做**极简意图分类和实体抽取**,只输出: - `intents: list[str]` - `entities: dict` 并实现 **Fast-Path 短路机制**: - 若指令是纯原子控制(如“起飞”、“降落”、“悬停”),直接由代码执行,不进入规划阶段。 - 🟢 Layer 2:动态组装与外部计算(Dynamic Composer & MCP/RAG) 纯代码。 - 根据 `intents` 和 `entities`,从本地 `node_schema.json` 裁剪出**最小必要的节点定义**(行为树节点)。 - 使用工具(后续接 RAG)解析地点到坐标(ENU),大模型不做算术。 - 生成发给 Stage 2 LLM 的 **精简 System Prompt**。 - 🔵 Layer 3:宏观行为规划(Stage 2 Macro Planner) 调用 LLM(Qwen3-4B): - 输入:Layer 2 生成的 System Prompt + 用户原文(含 RAG 上下文)。 - 输出:业务行为树 JSON(**不包含起飞/降落**)。 - 🟣 Layer 4:执行包装与安全逻辑(Execution Wrapper) 纯代码 + py_trees: - 将业务树解析为 py_trees 对象。 - 如果 `is_in_air == False`,自动在业务树前面**插入 `[SystemCheck -> Takeoff]` 子树**。 - 提供 tick 循环和未来的中断(用户打断、动态重规划)预留。 --- ## 二、节点定义(给 LLM 的行为树节点池) 请在 `config/node_schema.json` 定义一个简洁的行为节点池(LLM 可见的动作),结构类似: ```json { "_instruction": "作为行为树规划器,你只能使用以下定义的节点和参数。", "actions": { "fly_to_waypoint": { "desc": "...", "params": { "x": "float", "y": "float", "z": "float" } }, "fly_sequence": { "desc": "...", "params": { "waypoints": "array of {x,y,z}" } }, "move_direction": { "desc": "...", "params": { "direction": "front|back|left|right|up|down", "distance": "float" } }, "search_pattern": { "desc": "...", "params": { "pattern_type": "spiral|grid", "radius": "float", "target_class": "string" } }, "rotate_search": { "desc": "...", "params": { "target_class": "string" } }, "track_object": { "desc": "...", "params": { "target_class": "string", "track_time": "float" } }, "take_photos": { "desc": "...", "params": { "target_class": "string", "count": "int" } }, "report_message": { "desc": "...", "params": { "message": "string" } }, "manual_confirmation": { "desc": "...", "params": { "prompt_message": "string" } } }, "conditions": { "object_detected": { "desc": "...", "params": { "target_class": "string" } } }, "control_flow": { "Sequence": { "desc": "...", "params": {}, "requires_children": true }, "Selector": { "desc": "...", "params": {}, "requires_children": true }, "Parallel": { "desc": "...", "params": { "policy": "success_on_one|success_on_all" }, "requires_children": true } }, "decorators": { "Timeout": { "desc": "...", "params": { "max_time": "float" }, "requires_child": true }, "Repeat": { "desc": "...", "params": { "times": "int" }, "requires_child": true } } } ``` > 注意:**起飞/降落等底层动作不要出现在这里**,由 Layer 4 包装硬编码处理。 --- ## 三、Stage 1 Intent Router 的设计(重点) ### 1. LLM 提示词(Router System Prompt) 要求使用简洁、正交的 Intent 集合(避免同义词过多): - 原子意图(Atomic Intents):仅用于 Fast-Path - `atomic_takeoff` - `atomic_land` - `atomic_hover` - 业务意图(Business Intents) - `fly_task`:所有涉及空间移动/路径/巡逻 - `search_task`:搜索/侦查 - `track_task`:跟踪 - `photo_task`:拍照 - `interact_task`:上报/请求确认 **实体提取要求:** - 地点实体统一用 `"locations"`,且必须为列表: - `{"locations": ["大门"]}` - `{"locations": ["大门", "广场"]}`(支持“先去A再去B”) - 目标实体用 `"targets"` 列表: - `{"targets": ["汽车", "行人"]}` - 其他参数如 `"direction"`, `"distance"` 直接提取。 ### 2. Python 层的“硬编码冲突处理”逻辑 在 `pipeline/router.py` 中: - 定义集合: ```python ATOMIC_INTENTS = {"atomic_takeoff", "atomic_land", "atomic_hover"} BUSINESS_INTENTS = {"fly_task", "search_task", "track_task", "photo_task", "interact_task"} ``` - 处理流程: - 若 `intents` 非空且 **全部属于 ATOMIC_INTENTS**: → 允许 Fast-Path(直接执行原子命令)。 - 若 `intents` 同时包含 atomic 和 business(如 `["atomic_takeoff", "fly_task"]`): → **删除所有 atomic**,仅保留 business,走正常规划(起飞由 Layer 4 自动包装)。 - 若 `intents` 为空或包含未识别标签(小模型幻觉): → 兜底为 `["fly_task", "search_task"]`,保证系统不崩。 --- ## 四、RAG 模块设计(Layer 2 可选扩展) ### 1. 目录结构 `src/drone_planning/rag/`: - `embedding_client.py`:封装 Qwen Embedding(llama-server 8090)。 - `vector_store.py`:ChromaDB 客户端,管理三个 Collection: - `map_db` - `rule_db` - `few_shot_db` - `retriever.py`:`RAGRetriever`: - `dynamic_memory`:热数据表占位(未来语义地图)。 - `retrieve_context(intents, entities, user_text) -> dict` - `ingestion.py`:从 `data/knowledge/*.jsonl` 读 NDJSON 灌入 ChromaDB。 - `schemas.py`(可选):约定 JSONL 字段结构。 ### 2. JSONL 格式约定(统一用 `.jsonl`,一行一 JSON) - `data/knowledge/map_db.jsonl`: ```json {"document": "喷泉在广场正中央,坐标 x=10, y=20", "location": "喷泉", "x": 10, "y": 20, "z": 0} {"document": "A区是禁飞区,不得进入", "location": "A区", "zone_type": "no_fly"} ``` - `data/knowledge/rule_db.jsonl`: ```json {"document": "夜间巡逻必须开启热成像", "intent": "search_task", "scene": "night"} {"document": "起飞必须先做系统检查", "intent": "fly_task", "priority": "high"} ``` - `data/knowledge/few_shot_db.jsonl`: ```json {"document": "飞到大门然后拍照", "intent": "fly_task", "tree_json": "{\"root\": {...}}"} ``` ### 3. 检索策略(在 `retriever.py`) - 热数据优先(Dynamic Semantic Map): - `dynamic_memory = {"locations": {...}, "targets": {...}}` - 若某 location 在热内存中有坐标,直接返回,**不要查 ChromaDB**。 - 冷库次之(ChromaDB): - 根据 `entities["locations"]` 和 `intents` 用 metadata + 向量检索,从 `map_db` 和 `rule_db` 获取上下文。 - 使用 `user_text` 在 `few_shot_db` 中查 1–2 个相似任务的行为树,作为 few-shot 示例。 RAG 返回统一结构: ```python { "map_context": "若干行文本或结构化信息", "rule_context": "规则文本汇总", "few_shot_examples": [ {"instruction": "...", "tree_json": {...}}, ... ] } ``` ### 4. 与 Composer 集成(`pipeline/composer.py`) - 为 `build_system_prompt(...)` 增加参数 `rag_context: dict | None`。 - 在 Prompt 中插入三个区块(若非空): - `## RAG 地图上下文` - `## RAG 规则约束` - `## RAG 示例行为树` - 坐标解析优先级: 1. 动态内存(语义地图,未来接入)。 2. RAG 地图(map_db 的 x,y,z)。 3. geo.py 的硬编码 `landmark_to_enu()`(兜底,不删除,作为最后一层 fallback)。 --- ## 五、执行包装层(Layer 4,`execution`) ### 1. 行为节点实现(`execution/nodes.py`) 实现继承自 `py_trees.behaviour.Behaviour` 的 Mock 节点: - `SystemCheckCondition` - `TakeoffAction` - `LandAction` - `FlyToWaypointAction` - `GenericAction`(兜底) 目前只打印日志并返回 `SUCCESS`,后续预留 ROS2/PX4 接口。 ### 2. 树包装与 Tick 循环(`execution/tree_wrapper.py`) - `parse_json_to_tree(node_dict)`:递归将 Planner 的 JSON 转换为 py_trees 树。 - `wrap_and_build_tree(business_tree)`: - 读取 `DroneStateBlackboard.is_in_air`: - 若 `False`:创建 `Sequence("Safe_Execution")`,依次添加: - `SystemCheckCondition()` - `TakeoffAction()` - `business_tree` - 若 `True`:直接返回 `business_tree`。 --- ## 六、API 接口与 Orchestrator - 使用 FastAPI 暴露 `POST /api/plan`: - 输入:`{"text": "用户自然语言指令"}`。 - 调用顺序: 1. Layer 1 Router:若 Fast-Path,直接返回执行结果。 2. 否则,调用 RAG → Composer → Planner → Tree Wrapper,返回行为树 JSON 和日志。 --- ## 七、请 Cursor 的工作顺序建议 1. 生成项目目录和基础依赖(`requirements.txt`,`pyproject.toml`)。 2. 实现: - `core/blackboard.py` - `llm_client/client.py`(指向 `http://localhost:8080/v1` for chat,`http://localhost:8090/v1` for embeddings 可在 embedding_client 中配置) 3. 实现 `config/node_schema.json`。 4. 实现 `pipeline/router.py`(含 intents/entities 提取与 Fast-Path 短路 + 冲突处理)。 5. 实现 `tools/geo.py`(仅作为 fallback 静态坐标表)。 6. 实现 `rag/*`(embedding_client, vector_store, ingestion, retriever)。 7. 实现 `pipeline/composer.py` 和 `pipeline/planner.py`(加入 rag_context)。 8. 实现 `execution/nodes.py` 和 `execution/tree_wrapper.py`。 9. 实现 `api/routes.py` 和 `main.py`。 10. 最后补充简单的 `tests/` 或手动 curl 示例。 请从目录结构 + 模块职责开始规划,确认后再逐步生成代码。 ```