# 无人机行为规划系统 - 项目流程详解 --- ## 1. 整体架构 ```mermaid flowchart TB subgraph Input [输入] UserText[用户自然语言指令] end subgraph Layer1 [Layer 1 - Router] Router[意图路由] FastPath{Fast-Path?} end subgraph RAG [RAG 检索] MapDB[map_db] RuleDB[rule_db] FewShotDB[few_shot_db] Retriever[RAGRetriever] end subgraph Layer2 [Layer 2 - Composer] Composer[动态组装 Prompt] end subgraph Layer3 [Layer 3 - Planner] Planner[LLM 规划] ToolCall[Function Calling] MCPCalc[calculate_relative_coordinate] end subgraph Layer4 [Layer 4 - Execution] Parse[parse_json_to_tree] Wrap[wrap_and_build_tree] Tree[py_trees 行为树] end UserText --> Router Router --> FastPath FastPath -->|是| Output1[直接返回原子意图] FastPath -->|否| Retriever Retriever --> MapDB Retriever --> RuleDB Retriever --> FewShotDB Retriever --> Composer Composer --> Planner Planner --> ToolCall ToolCall --> MCPCalc MCPCalc --> Planner Planner --> Parse Parse --> Wrap Wrap --> Tree ``` --- ## 2. 数据流与各 Stage 总览 | Stage | 模块 | 对应代码 | 是否调用 RAG | 是否调用工具 | 是否调用 LLM | |-------|------|----------|--------------|--------------|--------------| | Layer 1 | Router | `pipeline/router.py` | 否 | 否 | 是 | | RAG | Retriever | `rag/retriever.py` | 是(map/rule/few_shot) | 否 | 否 | | Layer 2 | Composer | `pipeline/composer.py` | 否(消费 RAG 结果) | 否 | 否 | | Layer 3 | Planner | `pipeline/planner.py` | 否(消费 Composer 输出) | 是(LLM 调用 mcp) | 是 | | Layer 4 | Execution | `execution/tree_wrapper.py` | 否 | 否 | 否 | --- ## 3. Stage 1:Layer 1 - Router(意图路由) ### 3.1 职责 - 对用户自然语言做意图分类和实体抽取 - 判断是否为 Fast-Path(仅原子意图:起飞/降落/悬停) - 冲突处理:同时含原子+业务意图时,保留业务意图 ### 3.2 对应代码 - **入口函数**:`route(user_text: str) -> RouterResult` - **文件**:[`src/drone_planning/pipeline/router.py`](../src/drone_planning/pipeline/router.py) ### 3.3 是否调用 RAG **否**。Router 仅依赖 LLM,不访问 RAG。 ### 3.4 是否调用工具 **否**。仅调用 LLM 的 `chat_completion_json`,无 Function Calling。 ### 3.5 提示词组织 **System Prompt**(`ROUTER_SYSTEM_PROMPT`): - 角色:指令意图分类器 - 意图集合:原子意图(atomic_takeoff/atomic_land/atomic_hover)、业务意图(fly_task/search_task/track_task/photo_task/interact_task) - 实体要求:locations、targets、direction、distance - 规则:相对描述(如「广场东边500米」)必须拆分为 `locations=["广场"]`, `direction="东"`, `distance=500` **User Message**:用户原始指令。 **输出 Schema**(`ROUTER_JSON_SCHEMA`): ```json { "intents": ["string"], "entities": { "locations": ["string"], "targets": ["string"], "direction": "string", "distance": number } } ``` ### 3.6 输出与短路逻辑 - `RouterResult.intents`:解析后的意图列表 - `RouterResult.entities`:实体字典 - `RouterResult.is_fast_path`:若 `intents` 非空且全部属于 `ATOMIC_INTENTS`,则为 `True`,后续 RAG/Composer/Planner/Execution 不执行 --- ## 4. Stage 2:RAG 检索 ### 4.1 职责 - 根据 Router 的 `intents`、`entities`、`user_text` 检索上下文 - 地图坐标:热表优先,冷库(ChromaDB map_db)兜底 - 规则约束:按 intent 查 rule_db - Few-shot 示例:用 user_text 向量检索 few_shot_db - **仅提供基准点坐标**(`base_location_coords`),不负责相对坐标计算;相对描述由 LLM 通过 mcp 工具计算 ### 4.2 对应代码 - **类**:`RAGRetriever` - **方法**:`retrieve_context(intents, entities, user_text) -> dict` - **文件**:[`src/drone_planning/rag/retriever.py`](../src/drone_planning/rag/retriever.py) ### 4.3 调用的 RAG 库 | 库名 | 用途 | 检索方式 | |------|------|----------| | **map_db** | 基准点坐标 | metadata 精确匹配 `where={"location": base_loc}` | | **rule_db** | 规则约束 | metadata 精确匹配 `where={"intent": intent}` | | **few_shot_db** | Few-shot 示例 | 向量相似度 `query(query_texts=[user_text], n_results=2)` | ### 4.4 是否调用工具 **否**。RAG 不调用 mcp_calc,仅提供 `base_location_coords` 和 `relative_descriptions`,坐标计算由 LLM 通过 Function Calling 完成。 ### 4.5 输出结构 ```python { "map_context": str, "rule_context": str, "few_shot_examples": [...], "base_location_coords": {loc: {x,y,z}}, # 基准点坐标,不计算相对位置 "relative_descriptions": [ # 相对描述,供 LLM 调用工具 {"target": "广场东边500米", "base": "广场", "direction": "东", "distance": 500}, ... ] } ``` --- ## 5. Stage 3:Layer 2 - Composer(动态组装) ### 5.1 职责 - 根据 `intents` 裁剪 `node_schema.json`,只保留相关 actions - 将 RAG 的 `base_location_coords`、`relative_descriptions`、`map_context`、`rule_context`、`few_shot_examples` 拼入 System Prompt - 生成发给 Planner LLM 的精简 Prompt ### 5.2 对应代码 - **函数**:`build_system_prompt(intents, entities, schema_json, rag_context) -> (str, dict)` - **文件**:[`src/drone_planning/pipeline/composer.py`](../src/drone_planning/pipeline/composer.py) ### 5.3 是否调用 RAG **否**。Composer 不直接调用 RAG,只消费 Retriever 返回的 `rag_context`。 ### 5.4 是否调用工具 **否**。 ### 5.5 提示词组织 Prompt 由以下部分顺序拼接: 1. **指令**:`_instruction`(来自 node_schema) 2. **基准点坐标**:若 `base_location_coords` 非空,输出 `## 基准点坐标 base_location_coords(ENU,单位米)` 3. **可用节点**:裁剪后的 schema JSON(actions、conditions、control_flow、decorators) 4. **RAG 地图上下文**:`rag_context["map_context"]` 5. **RAG 规则约束**:`rag_context["rule_context"]` 6. **RAG 示例行为树**:`rag_context["few_shot_examples"]` 前 2 条 7. **坐标计算规则**:若用户只说绝对地点,可直接使用 base_location_coords;若用户说相对位置,必须调用 calculate_relative_coordinate,禁止心算 ### 5.6 意图到 Actions 映射 ```python INTENT_TO_ACTIONS = { "fly_task": ["fly_to_waypoint", "fly_sequence", "move_direction"], "search_task": ["search_pattern", "rotate_search"], "track_task": ["track_object"], "photo_task": ["take_photos"], "interact_task": ["report_message", "manual_confirmation"], } ``` --- ## 6. Stage 4:Layer 3 - Planner(宏观规划) ### 6.1 职责 - 根据 Composer 的 System Prompt 和用户文本,生成行为树 JSON - 支持 Function Calling:LLM 可自主调用 `calculate_relative_coordinate` 计算相对坐标 - 最多 5 轮 Tool 循环,无工具调用时解析最终 JSON 返回 ### 6.2 对应代码 - **函数**:`plan(system_prompt, user_text, tool_call_log) -> dict` - **文件**:[`src/drone_planning/pipeline/planner.py`](../src/drone_planning/pipeline/planner.py) ### 6.3 是否调用 RAG **否**。Planner 只消费 Composer 输出的 `system_prompt`,其中已包含 RAG 上下文。 ### 6.4 是否调用工具 **是**。坐标计算**仅由 LLM 负责**,通过 OpenAI Function Calling 调用 `calculate_relative_coordinate`(mcp 工具)。Planner 执行工具并追加结果到 messages,继续下一轮推理。 **工具定义**(`CALC_COORDINATE_TOOL`): ```json { "type": "function", "function": { "name": "calculate_relative_coordinate", "description": "根据基准点坐标和方向、距离,计算目标点的绝对 ENU 坐标...", "parameters": { "base_x": "number", "base_y": "number", "direction_str": "string", "distance": "number" } } } ``` **工具实现**:[`src/drone_planning/tools/mcp_calc.py`](../src/drone_planning/tools/mcp_calc.py) 中的 `calculate_relative_coordinate` ### 6.5 提示词组织 - **System**:Composer 生成的完整 Prompt(含 schema、坐标、RAG 上下文、工具说明) - **User**:用户原始指令 - 若 LLM 返回 `tool_calls`:追加 assistant message + 各 tool 结果,继续调用 `chat_completion_with_tools` - 若 LLM 返回纯文本:解析 JSON 作为行为树 ### 6.6 回退逻辑 - 若模型不支持 tools:使用 `chat_completion_json` + `PLANNER_JSON_SCHEMA` 无工具模式 - 若 JSON 解析失败:使用 `chat_completion` + `response_format={"type": "json_object"}` --- ## 7. Stage 5:Layer 4 - Execution(执行包装) ### 7.1 职责 - 将 Planner 的 JSON 递归解析为 py_trees 对象 - 根据 Blackboard `is_in_air` 判断:若在地面,自动在业务树前插入 `[SystemCheck -> Takeoff]` 子树 - 输出 ASCII 树结构供展示 ### 7.2 对应代码 - **函数**:`parse_json_to_tree`, `wrap_and_build_tree`, `tree_to_ascii` - **文件**:[`src/drone_planning/execution/tree_wrapper.py`](../src/drone_planning/execution/tree_wrapper.py) - **依赖**:[`core/blackboard.py`](../src/drone_planning/core/blackboard.py)、[`execution/nodes.py`](../src/drone_planning/execution/nodes.py) ### 7.3 是否调用 RAG **否**。 ### 7.4 是否调用工具 **否**。 ### 7.5 节点映射 | JSON type | py_trees 实现 | |-----------|---------------| | Sequence | `py_trees.composites.Sequence` | | Selector | `py_trees.composites.Selector` | | Parallel | `py_trees.composites.Parallel` | | fly_to_waypoint | `FlyToWaypointAction` | | 其他 action | `GenericAction` | --- ## 8. 坐标计算流程(仅 LLM 负责) ```mermaid flowchart LR subgraph RAG [RAG 仅提供基准点] Router1[Router 抽取 direction+distance] Retriever1[Retriever 解析复合地点] MapDB1[查 map_db 得基准坐标] Composer1[Composer 将 base_location_coords 写入 Prompt] end subgraph LLM [LLM 调用 mcp 计算] Planner1[Planner 收到 Prompt] LLM1[LLM 调用 calculate_relative_coordinate] Tool1[mcp 工具] Result1[工具结果写回 messages] end Router1 --> Retriever1 Retriever1 --> MapDB1 MapDB1 --> Composer1 Composer1 --> Planner1 Planner1 --> LLM1 LLM1 --> Tool1 Tool1 --> Result1 ``` - **RAG**:仅提供 `base_location_coords`(基准点坐标)和 `relative_descriptions`(相对描述列表),不调用 mcp_calc - **LLM**:若用户说绝对地点,直接使用 base_location_coords;若用户说相对位置,必须调用 calculate_relative_coordinate,禁止心算 --- ## 9. RAG 数据格式 ### 9.1 map_db.jsonl 每行一个 JSON,字段示例: ```json {"document": "大门位于入口处,坐标 x=0, y=0", "location": "大门", "x": 0, "y": 0, "z": 0} ``` ### 9.2 rule_db.jsonl ```json {"document": "起飞必须先做系统检查", "intent": "fly_task", "priority": "high"} ``` ### 9.3 few_shot_db.jsonl ```json {"document": "飞到大门然后拍照", "intent": "fly_task", "tree_json": "{\"root\": {...}}"} ``` --- ## 10. 环境变量与配置 | 变量 | 默认值 | 说明 | |------|--------|------| | `LLM_CHAT_BASE_URL` | `http://localhost:8081/v1` | Chat API | | `LLM_EMBEDDING_BASE_URL` | `http://localhost:8090/v1` | Embedding API(RAG) | | `ENABLE_THINKING` | `false` | 模型思考模式 | | `CHROMA_PERSIST_PATH` | `./data/chroma` | ChromaDB 存储路径 | --- ## 11. 文件索引 | 功能 | 文件路径 | |------|----------| | Router | `src/drone_planning/pipeline/router.py` | | Composer | `src/drone_planning/pipeline/composer.py` | | Planner | `src/drone_planning/pipeline/planner.py` | | RAG Retriever | `src/drone_planning/rag/retriever.py` | | RAG 向量存储 | `src/drone_planning/rag/vector_store.py` | | RAG 灌入 | `src/drone_planning/rag/ingestion.py` | | 坐标计算工具 | `src/drone_planning/tools/mcp_calc.py` | | 执行包装 | `src/drone_planning/execution/tree_wrapper.py` | | 节点实现 | `src/drone_planning/execution/nodes.py` | | 黑板 | `src/drone_planning/core/blackboard.py` | | LLM 客户端 | `src/drone_planning/llm_client/client.py` | | API 路由 | `src/drone_planning/api/routes.py` | | 节点 Schema | `config/node_schema.json` | | 知识库 | `data/knowledge/*.jsonl` |