12 KiB
12 KiB
无人机行为规划系统 - 项目流程详解
1. 整体架构
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
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):
{
"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
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 输出结构
{
"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
5.3 是否调用 RAG
否。Composer 不直接调用 RAG,只消费 Retriever 返回的 rag_context。
5.4 是否调用工具
否。
5.5 提示词组织
Prompt 由以下部分顺序拼接:
- 指令:
_instruction(来自 node_schema) - 基准点坐标:若
base_location_coords非空,输出## 基准点坐标 base_location_coords(ENU,单位米) - 可用节点:裁剪后的 schema JSON(actions、conditions、control_flow、decorators)
- RAG 地图上下文:
rag_context["map_context"] - RAG 规则约束:
rag_context["rule_context"] - RAG 示例行为树:
rag_context["few_shot_examples"]前 2 条 - 坐标计算规则:若用户只说绝对地点,可直接使用 base_location_coords;若用户说相对位置,必须调用 calculate_relative_coordinate,禁止心算
5.6 意图到 Actions 映射
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
6.3 是否调用 RAG
否。Planner 只消费 Composer 输出的 system_prompt,其中已包含 RAG 上下文。
6.4 是否调用工具
是。坐标计算仅由 LLM 负责,通过 OpenAI Function Calling 调用 calculate_relative_coordinate(mcp 工具)。Planner 执行工具并追加结果到 messages,继续下一轮推理。
工具定义(CALC_COORDINATE_TOOL):
{
"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 中的 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 - 依赖:
core/blackboard.py、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 负责)
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,字段示例:
{"document": "大门位于入口处,坐标 x=0, y=0", "location": "大门", "x": 0, "y": 0, "z": 0}
9.2 rule_db.jsonl
{"document": "起飞必须先做系统检查", "intent": "fly_task", "priority": "high"}
9.3 few_shot_db.jsonl
{"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 |