392 lines
12 KiB
Markdown
392 lines
12 KiB
Markdown
# 无人机行为规划系统 - 项目流程详解
|
||
|
||
---
|
||
|
||
## 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` |
|