Compare commits
36 Commits
main
...
fa5e5e7c23
| Author | SHA1 | Date | |
|---|---|---|---|
| fa5e5e7c23 | |||
| c7f6a0da17 | |||
| 5d8412bcb6 | |||
| 43d69d5a99 | |||
|
|
3c03749edf | ||
|
|
e164148c5c | ||
|
|
b12339ff73 | ||
| 410d2e01e4 | |||
| 070e4f579d | |||
| dd066057b0 | |||
| 59c52f6b99 | |||
| bce9203e01 | |||
| 333fad40ac | |||
| 9538757047 | |||
| ffb9aee730 | |||
| b68883e4e0 | |||
| 5d1c02fb5b | |||
| fb473dcf1a | |||
| 10c5bb5a8a | |||
| 3eba1f962b | |||
| 6f990e645d | |||
| c08cdfb339 | |||
| 43a0636913 | |||
| c4f851d387 | |||
| a6c2027caa | |||
| ab6e09423b | |||
| d32520d83f | |||
| afd170c451 | |||
| fd89745950 | |||
| 8e333ac03f | |||
| 7b9d05b306 | |||
| 781b490cdc | |||
| ce963ed7d6 | |||
| 9703f7cc10 | |||
| 7bf8210b80 | |||
| 3adf3985cb |
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# ============ 日志 ============
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# ============ 向量库(RAG 检索用) ============
|
||||||
|
**/vector_store/
|
||||||
|
vector_store/
|
||||||
|
|
||||||
|
# ============ 测试脚本的测试结果 ============
|
||||||
|
tools/test_validate/validation/
|
||||||
|
|
||||||
|
# ============ 生成的图片 ============
|
||||||
|
backend_service/generated_visualizations/
|
||||||
|
tools/test_validate/validation/**/*.png
|
||||||
|
tools/test_validate/validation/**/*.jpg
|
||||||
|
|
||||||
|
# ============ Python ============
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
.env/
|
||||||
|
|
||||||
|
# ============ 其他常见忽略 ============
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
607
PIPELINE_GUIDE.md
Normal file
607
PIPELINE_GUIDE.md
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
# DronePlanning 处理 Pipeline 说明
|
||||||
|
|
||||||
|
本文档说明当前后端从自然语言输入到计划 JSON 输出的完整处理流程,以及可自定义修改点。
|
||||||
|
|
||||||
|
## 1. 总体流程
|
||||||
|
|
||||||
|
入口为 `POST /generate_plan`,主链路由 `GenerationOrchestrator` 编排:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
api[main.py /generate_plan] --> gen[py_tree_generator.generate]
|
||||||
|
gen --> orch[GenerationOrchestrator]
|
||||||
|
orch --> s1[Stage1 TaskUnderstanding]
|
||||||
|
orch --> s2[Stage2 ContextBinding]
|
||||||
|
orch --> s3[Stage3 MacroPlanning]
|
||||||
|
s3 --> simple{simple?}
|
||||||
|
simple -->|是| s6[Stage6 Validate]
|
||||||
|
simple -->|否| s4[Stage4 Middleware]
|
||||||
|
s4 --> s5[Stage5 MicroFilling]
|
||||||
|
s5 --> s6
|
||||||
|
s6 --> out[返回 py_tree JSON]
|
||||||
|
```
|
||||||
|
|
||||||
|
对应代码:
|
||||||
|
|
||||||
|
- `backend_service/src/main.py`
|
||||||
|
- `backend_service/src/py_tree_generator.py`
|
||||||
|
- `backend_service/src/pipeline/orchestrator.py`
|
||||||
|
- `backend_service/src/pipeline/stages.py`
|
||||||
|
|
||||||
|
### 1.1 各 Stage 提示词模板一览
|
||||||
|
|
||||||
|
| Stage | 是否调用 LLM | System 模板/片段(按组合顺序) | User 内容 |
|
||||||
|
|-------|----------------|--------------------------------|------------|
|
||||||
|
| Stage1 | 是 | `prompts/scene_classifier_prompt.txt` | 原始 `user_prompt` |
|
||||||
|
| Stage2 | 否 | — | — |
|
||||||
|
| Stage3 | 是 | **simple**:`simple_mode_prompt.txt`;**complex**:`macro_header.txt` → 裁剪的 `core_nodes.json` → `template_ground.txt` / `template_air.txt` → `common_rules.txt` → 可选 `system_extra_examples.txt` → 意图标签 | `user_prompt` + 参考知识(地点/模式/规则) |
|
||||||
|
| Stage4 | 否 | — | — |
|
||||||
|
| Stage5 | 是 | `micro_header.txt` → macro_tree JSON → resolved_data JSON → 裁剪的 `atomic/nodes_schema.json` | 固定句:「请直接输出完整的带有 params 参数的 JSON 树结构。」 |
|
||||||
|
| Stage6 | 否 | — | — |
|
||||||
|
|
||||||
|
各阶段模板的详细组合顺序与条件见对应小节(如 2.3、3.3、4.3、5.3、6.3、7.3)。
|
||||||
|
|
||||||
|
### 1.2 提示词分配流程图
|
||||||
|
|
||||||
|
下图按 Stage 标出各阶段使用的提示词模板及组合关系(仅含涉及 LLM 的 Stage):
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph S1["Stage1 任务理解"]
|
||||||
|
direction TB
|
||||||
|
P1_sys["system: scene_classifier_prompt.txt"]
|
||||||
|
P1_usr["user: user_prompt"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph S2["Stage2 上下文绑定"]
|
||||||
|
P2["无 LLM / 无提示词"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph S3["Stage3 宏观规划"]
|
||||||
|
direction TB
|
||||||
|
P3a["simple: system = simple_mode_prompt.txt"]
|
||||||
|
P3b["complex: system = macro_header → core_nodes → template_ground/air → common_rules → 可选 system_extra_examples → 意图标签"]
|
||||||
|
P3_usr["user: user_prompt + 参考知识"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph S4["Stage4 中间层解析"]
|
||||||
|
P4["无 LLM / 无提示词"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph S5["Stage5 微观填参"]
|
||||||
|
direction TB
|
||||||
|
P5_sys["system: micro_header → macro_tree JSON → resolved_data JSON → atomic/nodes_schema 裁剪"]
|
||||||
|
P5_usr["user: 固定句"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph S6["Stage6 校验与后处理"]
|
||||||
|
P6["无 LLM / 无提示词"]
|
||||||
|
end
|
||||||
|
|
||||||
|
S1 --> S2 --> S3
|
||||||
|
S3 --> S4 --> S5 --> S6
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Stage1 / Stage3 / Stage5** 会调用 LLM,其 System/User 内容由上图对应框内模板或片段组合而成。
|
||||||
|
- **Stage2 / Stage4 / Stage6** 不调用 LLM,无提示词分配。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Stage1:任务理解(TaskUnderstanding)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- 场景分类:`simple / scene1 / scene4`
|
||||||
|
- 意图推断:`intent_type`
|
||||||
|
- 风险标记:`risk_flags`(例如是否需要人工确认、是否涉及相对方位)
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/llm/gateway.py::classify_scene()`
|
||||||
|
- `backend_service/src/pipeline/stages.py::_infer_intent_type()`
|
||||||
|
- `backend_service/src/pipeline/stages.py::_extract_risk_flags()`
|
||||||
|
- 数据契约:`backend_service/src/pipeline/contracts.py::TaskUnderstanding`
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 分类模型可启用 thinking(由 `STAGE1_ENABLE_THINKING` 控制)。
|
||||||
|
- 该阶段不依赖节点字典,避免循环依赖。
|
||||||
|
|
||||||
|
### 2.1 输入格式
|
||||||
|
|
||||||
|
`stage1_task_understanding(user_prompt: str)` 仅接受原始用户指令字符串。
|
||||||
|
|
||||||
|
示例输入:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_prompt": "无人机当前在地面,到广场查找绿色公交车,找到后拍照。"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 输出格式(TaskUnderstanding)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scene_mode": "scene4",
|
||||||
|
"intent_type": "search_and_photo",
|
||||||
|
"requires_relative_target": false,
|
||||||
|
"entities": {
|
||||||
|
"raw_prompt": "无人机当前在地面,到广场查找绿色公交车,找到后拍照。"
|
||||||
|
},
|
||||||
|
"risk_flags": [],
|
||||||
|
"constraints": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `scene_mode`: `simple | scene1 | scene4`
|
||||||
|
- `intent_type`: 当前规则推导出的任务意图标签
|
||||||
|
- `requires_relative_target`: 是否检测到相对方位需求
|
||||||
|
- `entities`: 当前为轻量占位(最小包含 `raw_prompt`)
|
||||||
|
- `risk_flags`: 风险标记列表(如 `needs_manual_confirmation`)
|
||||||
|
- `constraints`: 约束占位(当前为空对象)
|
||||||
|
|
||||||
|
### 2.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
| 角色 | 模板文件 | 路径 | 说明 |
|
||||||
|
|--------|----------|------|------|
|
||||||
|
| system | 场景分类 | `prompts/scene_classifier_prompt.txt` | 唯一 system 提示词,定义 simple/scene1/scene4 判定规则与示例 |
|
||||||
|
| user | 用户原文 | 调用方传入的 `user_prompt` | 不做拼接,直接作为 user 消息 |
|
||||||
|
|
||||||
|
组合方式:`messages = [ { "role": "system", "content": scene_classifier_prompt }, { "role": "user", "content": user_prompt } ]`。分类结果解析为 `scene_mode`,其余 `intent_type`、`risk_flags` 由本阶段规则函数从 `user_prompt` 推断,不读模板。
|
||||||
|
|
||||||
|
## 3. Stage2:上下文绑定(ContextBinding)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- 按场景动态决定检索范围(location/pattern/rules)
|
||||||
|
- 从多知识库并行检索并汇总
|
||||||
|
- 相对目标提取(`relative_refs`)
|
||||||
|
- 预计算航点(`precomputed_waypoints`,MVP)
|
||||||
|
- 基于意图与风险推导 `required_actions`
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/retrieval/retriever.py::UnifiedRetriever`
|
||||||
|
- `backend_service/src/retrieval/adapters/chroma_adapter.py`
|
||||||
|
- `backend_service/src/llm/tool_runtime.py`
|
||||||
|
- `backend_service/src/pipeline/stages.py::stage2_context_binding()`
|
||||||
|
- 数据契约:`backend_service/src/pipeline/contracts.py::ContextBinding`
|
||||||
|
|
||||||
|
多知识库策略:
|
||||||
|
|
||||||
|
- 主集合:`location_kb`、`pattern_kb`、`rules_kb`
|
||||||
|
- 兼容集合:`drone_docs`
|
||||||
|
- 若主集合无结果,会尝试从 `drone_docs` + `kb_type` 过滤回退查询
|
||||||
|
|
||||||
|
### 3.1 输入格式
|
||||||
|
|
||||||
|
该阶段接收:
|
||||||
|
|
||||||
|
- `user_prompt: str`
|
||||||
|
- Stage1 输出 `TaskUnderstanding`
|
||||||
|
|
||||||
|
示例输入:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_prompt": "无人机当前在空中,去广场南边40米,持续监控5分钟,发现人就拍照。",
|
||||||
|
"understanding": {
|
||||||
|
"scene_mode": "scene4",
|
||||||
|
"intent_type": "patrol_or_monitor",
|
||||||
|
"requires_relative_target": false,
|
||||||
|
"risk_flags": [],
|
||||||
|
"entities": {"raw_prompt": "无人机当前在空中,去广场南边40米,持续监控5分钟,发现人就拍照。"},
|
||||||
|
"constraints": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 输出格式(ContextBinding)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"location_context": "地点:广场,坐标(x=120,y=30,z=0)...",
|
||||||
|
"pattern_context": "示例:先到达命名地点,再监控,再条件触发拍照...",
|
||||||
|
"rules_context": "",
|
||||||
|
"citations": {
|
||||||
|
"location": ["..."],
|
||||||
|
"pattern": ["..."],
|
||||||
|
"rules": []
|
||||||
|
},
|
||||||
|
"resolved_refs": {},
|
||||||
|
"precomputed_waypoints": [
|
||||||
|
{"x": 160.0, "y": 30.0, "z": 10.0}
|
||||||
|
],
|
||||||
|
"relative_refs": [],
|
||||||
|
"required_actions": [
|
||||||
|
"Sequence",
|
||||||
|
"fly_to_waypoint",
|
||||||
|
"loiter",
|
||||||
|
"object_detect",
|
||||||
|
"object_detected",
|
||||||
|
"take_photos"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `*_context`: 分知识域拼接后的文本上下文
|
||||||
|
- `citations`: 每个知识域的原始命中文档片段
|
||||||
|
- `precomputed_waypoints`: Stage2 静态可解析时的预计算坐标
|
||||||
|
- `relative_refs`: 相对目标结构化描述
|
||||||
|
- `required_actions`: 后续用于节点裁剪注入的动作白名单
|
||||||
|
|
||||||
|
### 3.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
本阶段**不调用 LLM**,无提示词模板。仅做检索(Location/Pattern/Rules)、规则推导(`required_actions`、`relative_refs`)、预计算航点。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Stage3:宏观规划(Macro Planning,Round 1)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- 组装 prompt(骨架 + 节点裁剪 + 模板 + 规则 + 检索结果)
|
||||||
|
- 调用对应模型生成宏观树(及可选的 parameter_requests)
|
||||||
|
- 解析模型响应(含 reasoning 提取)
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/prompting/composer.py::PromptComposer.compose_macro()`
|
||||||
|
- `backend_service/src/llm/gateway.py::generate_json()`
|
||||||
|
- `backend_service/src/pipeline/stages.py::stage3_macro_planning()`
|
||||||
|
- 数据契约:`backend_service/src/pipeline/contracts.py::BTDraft`
|
||||||
|
|
||||||
|
关键行为:
|
||||||
|
|
||||||
|
- simple 模式与复杂模式使用不同客户端/模型配置;Stage3 强制关闭 thinking,强制 `response_format=json_object`;节点定义采用裁剪注入(非全量注入)。
|
||||||
|
|
||||||
|
### 4.1 输入格式(Stage3)
|
||||||
|
|
||||||
|
该阶段接收:
|
||||||
|
|
||||||
|
- `user_prompt: str`
|
||||||
|
- `TaskUnderstanding`
|
||||||
|
- `ContextBinding`
|
||||||
|
|
||||||
|
核心输入(示例):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scene_mode": "scene4",
|
||||||
|
"intent_type": "search_and_photo",
|
||||||
|
"required_actions": ["Sequence", "fly_to_waypoint", "rotate_search", "object_detected", "take_photos"],
|
||||||
|
"risk_flags": [],
|
||||||
|
"context_blocks": {
|
||||||
|
"location": "地点:广场...",
|
||||||
|
"pattern": "示例:先到达再搜索...",
|
||||||
|
"rules": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 输出格式(BTDraft)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"system_prompt": "...(裁剪后的系统提示词)...",
|
||||||
|
"user_prompt": "原始指令 + 参考知识增强段",
|
||||||
|
"allowed_nodes": {
|
||||||
|
"actions": ["fly_to_waypoint", "rotate_search", "take_photos"],
|
||||||
|
"conditions": ["object_detected"]
|
||||||
|
},
|
||||||
|
"llm_raw_json": {
|
||||||
|
"root": {
|
||||||
|
"type": "Sequence",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{"type": "action", "name": "fly_to_waypoint", "params": {"x": 120, "y": 30, "z": 10, "acceptance_radius": 2}},
|
||||||
|
{"type": "action", "name": "rotate_search", "params": {"target_class": "bus"}},
|
||||||
|
{"type": "condition", "name": "object_detected", "params": {"target_class": "bus"}},
|
||||||
|
{"type": "action", "name": "take_photos", "params": {"target_class": "bus", "track_time": 8}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reasoning_text": null,
|
||||||
|
"final_prompt": "=== System Prompt === ... === User Prompt === ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `system_prompt/user_prompt`: 实际发给模型的提示词
|
||||||
|
- `allowed_nodes`: 节点裁剪结果(用于调试与复盘)
|
||||||
|
- `llm_raw_json`: 模型返回并解析后的原始计划 JSON
|
||||||
|
- `reasoning_text`: 可选推理文本(若模型返回)
|
||||||
|
- `final_prompt`: 完整组合记录(便于离线排查)
|
||||||
|
|
||||||
|
### 4.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
**simple 模式**(单轮,直接出最终树):
|
||||||
|
|
||||||
|
| 角色 | 模板/内容 | 路径 | 说明 |
|
||||||
|
|--------|------------|------|------|
|
||||||
|
| system | 简单模式全文 | `prompts/simple_mode_prompt.txt` | 直接作为 system,无拼接 |
|
||||||
|
| user | 用户原文 + 参考知识 | 动态 | `user_prompt` + `_build_user_augmentation(context_blocks)` |
|
||||||
|
|
||||||
|
**复杂模式**(scene1/scene4,宏观树 Round 1):
|
||||||
|
|
||||||
|
System 按**顺序**拼接以下内容(来自 `prompting/composer.py::compose_macro()`):
|
||||||
|
|
||||||
|
| 顺序 | 模板/内容 | 路径 | 说明 |
|
||||||
|
|------|-----------|------|------|
|
||||||
|
| 1 | 宏观任务头 | `prompts/partials/macro_header.txt` | 任务定义与输出要求 |
|
||||||
|
| 2 | 节点定义(裁剪后) | `prompts/partials/core_nodes.json` | 按 `required_actions` + `risk_flags` 裁剪,最多 30 个 action/condition,格式化为「## 一、核心节点定义」+ JSON 代码块 |
|
||||||
|
| 3 | 任务模板(二选一) | `prompts/partials/template_ground.txt` 或 `prompts/partials/template_air.txt` | 由 `drone_state`(on_ground / in_air)决定 |
|
||||||
|
| 4 | 通用规则 | `prompts/partials/common_rules.txt` | 若文件存在则追加 |
|
||||||
|
| 5 | 额外示例(可选) | `prompts/partials/system_extra_examples.txt` | 仅当 `intent_type == "generic_mission"` 时追加 |
|
||||||
|
| 6 | 意图标签 | 代码生成 | 固定段落:`## 任务意图标签\n- intent_type: \`{intent_type}\`` |
|
||||||
|
|
||||||
|
User 消息:
|
||||||
|
|
||||||
|
- 内容 = `user_prompt` + 参考知识增强段。
|
||||||
|
- 参考知识增强段由 `_build_user_augmentation(context_blocks)` 生成:若 `context_blocks` 中 `location` / `pattern` / `rules` 非空,则按顺序拼接为「【地点知识】…」「【任务模式】…」「【规则知识】…」,整体包在 `---\n参考知识:\n…\n---` 中。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Stage4:中间层解析(Middleware Resolution)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- 读取 Stage3 输出的 `parameter_requests`
|
||||||
|
- 对含 `landmark` 等实体的请求做位置检索与航点预计算,写入 `resolved_data`
|
||||||
|
- 其他实体透传为 `{node}_entities`,供 Stage5 使用
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/pipeline/stages.py::stage4_middleware_resolution()`
|
||||||
|
- 复用 `retriever.retrieve(scopes=["location"])` 与 `tool_runtime.build_precomputed_waypoint()`
|
||||||
|
|
||||||
|
### 5.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
本阶段**不调用 LLM**,无提示词模板。仅做依赖解析与数据绑定。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Stage5:微观参数填空(Micro Parameter Filling,Round 2)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- 从 `prompts/atomic/nodes_schema.json` 按宏观树中用到的节点名裁剪出 `atomic_schema`
|
||||||
|
- 调用 `PromptComposer.compose_micro()` 组装 Round 2 的 system 提示词
|
||||||
|
- 再次调用模型,输出带完整 `params` 的 JSON 树
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/prompting/composer.py::compose_micro()`
|
||||||
|
- `backend_service/src/pipeline/stages.py::stage5_micro_filling()`
|
||||||
|
|
||||||
|
### 6.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
| 角色 | 模板/内容 | 路径 | 说明 |
|
||||||
|
|--------|------------|------|------|
|
||||||
|
| system | 微观任务头 | `prompts/partials/micro_header.txt` | 第一段 |
|
||||||
|
| system | 宏观骨架树 | 运行时 | `## 1. 原宏观骨架树 (macro_tree)` + `draft.macro_tree` 的 JSON |
|
||||||
|
| system | 确切数据字典 | 运行时 | `## 2. 确切数据字典 (resolved_data)` + Stage4 输出的 `resolved_data` JSON |
|
||||||
|
| system | 原子节点规范 | `prompts/atomic/nodes_schema.json`(按需裁剪) | `## 3. 原子节点规范 (atomic_schema)`;仅保留宏观树中出现的 action/condition 的 schema |
|
||||||
|
| user | 固定指令 | 代码写死 | `"请直接输出完整的带有 params 参数的 JSON 树结构。"` |
|
||||||
|
|
||||||
|
组合方式:system = 上述四段用 `\n\n` 拼接;user = 固定字符串。Round 2 不再注入 RAG 检索块。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Stage6:校验与后处理(ValidateAndPostprocess)
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
|
- JSON Schema 校验(simple / complex)
|
||||||
|
- 注入 `plan_id`、`visualization_url`、`final_prompt`
|
||||||
|
- 保存推理链与历史记录
|
||||||
|
- 在复杂场景下注入 `context.relative_refs`(及可选 `context.resolved_refs`)
|
||||||
|
|
||||||
|
核心代码:
|
||||||
|
|
||||||
|
- `backend_service/src/validation/validator.py`
|
||||||
|
- `backend_service/src/validation/schema_provider.py`
|
||||||
|
- `backend_service/src/pipeline/stages.py::stage6_validate_and_postprocess()`
|
||||||
|
- `backend_service/src/py_tree_generator.py::render_visualization()`
|
||||||
|
- `backend_service/src/py_tree_generator.py::_save_history()`
|
||||||
|
|
||||||
|
### 7.1 输入格式
|
||||||
|
|
||||||
|
该阶段接收:
|
||||||
|
|
||||||
|
- `user_prompt: str`
|
||||||
|
- `TaskUnderstanding`
|
||||||
|
- `ContextBinding`
|
||||||
|
- `BTDraft`
|
||||||
|
- 复杂模式下还有 Stage5 的 `final_tree`;simple 模式下为 `draft.llm_raw_json`
|
||||||
|
|
||||||
|
### 7.2 输出格式(最终 API 返回)
|
||||||
|
|
||||||
|
复杂模式示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "Sequence",
|
||||||
|
"name": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{"type": "action", "name": "fly_to_waypoint", "params": {"x": 120, "y": 30, "z": 10, "acceptance_radius": 2}},
|
||||||
|
{"type": "action", "name": "rotate_search", "params": {"target_class": "bus"}},
|
||||||
|
{"type": "condition", "name": "object_detected", "params": {"target_class": "bus"}},
|
||||||
|
{"type": "action", "name": "take_photos", "params": {"target_class": "bus", "track_time": 8}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"relative_refs": [
|
||||||
|
{"anchor": "front_building", "relation": "left", "distance_m": 20.0}
|
||||||
|
],
|
||||||
|
"resolved_refs": {
|
||||||
|
"strategy": "backend_static_resolution",
|
||||||
|
"waypoints": [{"x": 120.0, "y": 30.0, "z": 10.0}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plan_id": "6a924d0d-f1a7-4ef1-a9fa-31f73f3115ce",
|
||||||
|
"visualization_url": "/static/py_tree.png",
|
||||||
|
"final_prompt": "=== System Prompt === ... === User Prompt === ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
simple 模式示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "action",
|
||||||
|
"name": "move_direction",
|
||||||
|
"params": {"direction": "north", "distance": 50}
|
||||||
|
},
|
||||||
|
"plan_id": "0f0e5e5f-b9a2-4e6f-95f5-c95e9e6280a5",
|
||||||
|
"visualization_url": "/static/py_tree.png",
|
||||||
|
"final_prompt": "=== System Prompt === ... === User Prompt === ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
- `root`: 通过 schema 校验后的行为树根节点
|
||||||
|
- `context`: 仅在复杂模式且命中相对目标时追加
|
||||||
|
- `plan_id`: 每次生成唯一 ID
|
||||||
|
- `visualization_url`: 最新可视化图访问路径
|
||||||
|
- `final_prompt`: 生成时使用的完整提示词记录
|
||||||
|
|
||||||
|
### 7.3 本阶段组合的提示词模板
|
||||||
|
|
||||||
|
本阶段**不调用 LLM**,无提示词模板。仅做校验、注入元数据与写盘。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 数据入库(RAG Ingestion)逻辑
|
||||||
|
|
||||||
|
入库脚本:
|
||||||
|
|
||||||
|
- `tools/rag/ingest.py`
|
||||||
|
|
||||||
|
行为:
|
||||||
|
|
||||||
|
- 扫描 `tools/rag/knowledge_base/`
|
||||||
|
- 根据子目录推断 `kb_type`(location/pattern/rules)
|
||||||
|
- 同时写入:
|
||||||
|
- `drone_docs`(兼容)
|
||||||
|
- `location_kb` / `pattern_kb` / `rules_kb`(新检索路径)
|
||||||
|
|
||||||
|
## 9. 可自定义修改点(推荐按优先级)
|
||||||
|
|
||||||
|
### 9.1 场景与意图逻辑
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/pipeline/stages.py`
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- `_infer_intent_type()`:扩展意图类别
|
||||||
|
- `_extract_risk_flags()`:新增风险规则
|
||||||
|
- `_derive_required_actions()`:调整规则推导的动作集合
|
||||||
|
|
||||||
|
### 9.2 Prompt 策略
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/prompting/composer.py`
|
||||||
|
- `backend_service/src/prompts/prompt_manifest.yaml`
|
||||||
|
- `backend_service/src/prompts/partials/*`
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- 骨架片段选择
|
||||||
|
- 节点裁剪规则(当前上限 30)
|
||||||
|
- 示例注入策略(何时注入 extra examples)
|
||||||
|
- 用户侧检索增强格式
|
||||||
|
|
||||||
|
### 9.3 模型路由与推理参数
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/llm/gateway.py`
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- 分类模型与生成模型分流策略
|
||||||
|
- `temperature`、`max_tokens`、重试次数
|
||||||
|
- thinking 开关策略(Stage1/Stage3)
|
||||||
|
|
||||||
|
### 9.4 检索策略
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/retrieval/retriever.py`
|
||||||
|
- `backend_service/src/retrieval/adapters/chroma_adapter.py`
|
||||||
|
- `tools/rag/ingest.py`
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- 检索并发策略与 `top_k`
|
||||||
|
- 回退策略(主集合与兼容集合)
|
||||||
|
- kb_type 划分方式
|
||||||
|
- 文档切分与 metadata 设计
|
||||||
|
|
||||||
|
### 9.5 相对目标解析
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/pipeline/stages.py`
|
||||||
|
- `backend_service/src/llm/tool_runtime.py`
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- `relative_refs` 抽取规则
|
||||||
|
- 静态解析能力(何时生成 `resolved_refs`)
|
||||||
|
- 与 UAV 端协议字段兼容策略
|
||||||
|
|
||||||
|
### 9.6 校验与输出协议
|
||||||
|
|
||||||
|
可改文件:
|
||||||
|
|
||||||
|
- `backend_service/src/validation/validator.py`
|
||||||
|
- `backend_service/src/validation/schema_provider.py`
|
||||||
|
- `backend_service/src/py_tree_generator.py`(schema 来源)
|
||||||
|
|
||||||
|
可改内容:
|
||||||
|
|
||||||
|
- simple/complex schema 约束强度
|
||||||
|
- 顶层 `context` 字段的可选校验
|
||||||
|
- 失败错误信息与恢复策略
|
||||||
|
|
||||||
|
## 10. 关键环境变量
|
||||||
|
|
||||||
|
- `ORIN_IP`
|
||||||
|
- `OPENAI_API_KEY`
|
||||||
|
- `CLASSIFIER_MODEL` / `SIMPLE_MODEL` / `COMPLEX_MODEL`
|
||||||
|
- `CLASSIFIER_BASE_URL` / `SIMPLE_BASE_URL` / `COMPLEX_BASE_URL`
|
||||||
|
- `STAGE1_ENABLE_THINKING`
|
||||||
|
- `ENABLE_REASONING_CAPTURE`
|
||||||
|
- `REASONING_PREVIEW_LINES`
|
||||||
|
|
||||||
|
## 11. 自定义改造建议(实践顺序)
|
||||||
|
|
||||||
|
1. 先改 Stage1 规则推导(低风险,收益快)
|
||||||
|
2. 再改 PromptComposer 的裁剪与注入(控制长度与稳定性)
|
||||||
|
3. 再改检索策略(top_k、回退、metadata)
|
||||||
|
4. 最后改 schema 与输出协议(需联动执行端)
|
||||||
|
|
||||||
|
## 12. 变更后最小回归清单
|
||||||
|
|
||||||
|
每次改造后至少验证:
|
||||||
|
|
||||||
|
1. simple 指令:返回 `root.action`,且无 children
|
||||||
|
2. scene4 指令:有复合树结构,JSON 可解析
|
||||||
|
3. relative 指令:复杂模式下出现 `context.relative_refs`
|
||||||
|
4. `/generate_plan` 不变更接口字段(兼容外部调用)
|
||||||
|
5. `python tools/rag/ingest.py` 可完成入库(或输出可定位错误)
|
||||||
|
|
||||||
343
README.md
343
README.md
@@ -1,284 +1,153 @@
|
|||||||
# 无人机自然语言控制项目
|
# 无人机自然语言控制项目 (DronePlanning)
|
||||||
|
|
||||||
本项目构建了一个完整的无人机自然语言控制系统,集成了检索增强生成(RAG)知识库、大型语言模型(LLM)、FastAPI后端服务和ROS2通信,最终实现通过自然语言指令控制无人机执行复杂任务。
|
本项目提供一个基于 `FastAPI + LLM + RAG` 的无人机任务规划后端。输入自然语言指令,输出严格 JSON 的行为树计划(`py_tree`)。
|
||||||
|
|
||||||
## 项目结构
|
> 当前版本已与 ROS2 解耦。
|
||||||
|
> 推荐直接使用 `start_all.sh` / `start_all_vllm.sh` 启动。
|
||||||
|
|
||||||
项目被清晰地划分为几个核心模块:
|
## 1. 当前代码结构
|
||||||
|
|
||||||
```
|
```text
|
||||||
.
|
backend_service/src/
|
||||||
├── backend_service/
|
├── main.py
|
||||||
│ ├── src/ # FastAPI应用核心代码
|
├── py_tree_generator.py
|
||||||
│ │ ├── __init__.py
|
├── models.py
|
||||||
│ │ ├── main.py # 应用主入口,提供Web API
|
├── websocket_manager.py
|
||||||
│ │ ├── py_tree_generator.py # RAG与LLM集成,生成py_tree
|
├── pipeline/ # 编排层(stage1~stage4)
|
||||||
│ │ ├── ...
|
├── llm/ # 模型网关/响应解析/工具运行时
|
||||||
│ ├── generated_visualizations/ # 存放最新生成的py_tree可视化图像
|
├── retrieval/ # 多知识库检索(Location/Pattern/Rules)
|
||||||
│ └── requirements.txt # 后端服务的Python依赖
|
├── prompting/ # prompt manifest + 动态注入
|
||||||
│
|
├── validation/ # schema provider + validator
|
||||||
├── tools/
|
├── prompts/
|
||||||
│ ├── map/ # 【数据源】存放原始地图文件(如.world, .json)
|
└── tools/
|
||||||
│ ├── knowledge_base/ # 【处理后】存放build_knowledge_base.py生成的.ndjson文件
|
|
||||||
│ ├── vector_store/ # 【数据库】存放最终的ChromaDB向量数据库
|
|
||||||
│ ├── build_knowledge_base.py # 【步骤1】用于将原始数据转换为自然语言知识
|
|
||||||
│ └── ingest.py # 【步骤2】用于将自然语言知识摄入向量数据库
|
|
||||||
│
|
|
||||||
├── / # ROS2接口定义 (保持不变)
|
|
||||||
└── docs/
|
|
||||||
└── README.md # 本说明文件
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 核心配置:Orin IP 地址
|
## 2. 环境准备(完整命令)
|
||||||
|
|
||||||
**重要提示:** 本项目的后端服务和知识库工具需要与在NVIDIA Jetson Orin设备上运行的服务进行通信(嵌入模型和LLM推理服务),**默认的IP地址为localhost**,所以使用电脑本地部署的模型服务同样可以,但是需要注意指定模型的端口。
|
在项目根目录执行:
|
||||||
|
|
||||||
在使用前,您必须配置正确的Orin设备IP地址。您可以通过以下两种方式之一进行设置:
|
|
||||||
|
|
||||||
1. **设置环境变量 (推荐)**:
|
|
||||||
在您的终端中设置一个名为 `ORIN_IP` 的环境变量。
|
|
||||||
```bash
|
|
||||||
export ORIN_IP="192.168.1.100" # 请替换为您的Orin设备的实际IP地址
|
|
||||||
```
|
|
||||||
脚本会优先使用这个环境变量。
|
|
||||||
|
|
||||||
2. **直接修改脚本**:
|
|
||||||
如果您不想设置环境变量,可以打开 `tools/ingest.py` 和 `backend_service/src/py_tree_generator.py` 文件,找到 `orin_ip = os.getenv("ORIN_IP", "...")` 这样的行,并将默认的IP地址修改为您的Orin设备的实际IP地址。
|
|
||||||
|
|
||||||
**在继续后续步骤之前,请务必完成此项配置。**
|
|
||||||
|
|
||||||
## 模型端口启动
|
|
||||||
|
|
||||||
本项目启动依赖于后端的模型推理服务,即`ORIN_IP`所指向的设备的模型服务端口,目前项目使用instruct模型与embedding模型实现流程,分别部署在8081端口与8090端口。
|
|
||||||
|
|
||||||
1. **推理模型部署**:
|
|
||||||
|
|
||||||
在`/llama.cpp/build/bin`路径下执行以下命令启动模型
|
|
||||||
```bash
|
|
||||||
./llama-server -m ~/models/gguf/Qwen/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf --port 8081 --gpu-layers 36 --host 0.0.0.0 -c 8192
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Embedding模型部署**
|
|
||||||
|
|
||||||
在`/llama.cpp/build/bin`路径下执行以下命令启动模型
|
|
||||||
```bash
|
|
||||||
./llama-server -m ~/models/gguf/Qwen/Qwen3-embedding-4B/Qwen3-Embedding-4B-Q4_K_M.gguf --gpu-layers 36 --port 8090 --embeddings --pooling last --host 0.0.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 工作流程
|
|
||||||
|
|
||||||
整个系统的工作流程分为两个主要阶段:
|
|
||||||
|
|
||||||
1. **知识库构建(一次性设置)**: 将环境信息、无人机能力等知识加工并存入向量数据库。
|
|
||||||
2. **后端服务运行与交互**: 启动主服务,通过API接收指令、生成并执行任务。
|
|
||||||
|
|
||||||
### 阶段一:环境设置与编译
|
|
||||||
|
|
||||||
此阶段为项目准备好运行环境,仅需在初次配置或依赖变更时执行。一个稳定、隔离且兼容的环境是所有后续步骤成功的基础。
|
|
||||||
|
|
||||||
#### 1. 创建Conda环境 (关键步骤)
|
|
||||||
|
|
||||||
为了从根源上避免本地Python环境与系统ROS 2环境的库版本冲突(特别是Python版本和C++标准库),我们**必须**使用Conda创建一个干净、隔离且版本精确的虚拟环境。
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 创建一个使用Python 3.10的新环境。
|
cd /home/huangfukk/DronePlanning
|
||||||
# --name backend: 指定环境名称。
|
|
||||||
# python=3.10: 指定Python版本,必须与ROS 2 Humble要求的版本一致。
|
|
||||||
# --channel conda-forge: 使用conda-forge社区源,其包通常有更好的兼容性。
|
|
||||||
# --no-default-packages: 关键!不安装Conda默认的包(如libgcc),避免与系统ROS 2的C++库冲突。
|
|
||||||
conda create --name backend --channel conda-forge --no-default-packages python=3.10
|
|
||||||
|
|
||||||
# 2. 激活新创建的环境
|
# 1) 创建并激活 venv
|
||||||
conda activate backend
|
python3 -m venv backend_service/venv
|
||||||
```
|
source backend_service/venv/bin/activate
|
||||||
|
|
||||||
#### 2. 安装所有Python依赖
|
# 2) 安装依赖
|
||||||
|
|
||||||
在激活`backend`环境后,使用`pip`一次性安装所有依赖。`requirements.txt`已包含**运行时**(如fastapi, rclpy)和**编译时**(如empy, catkin-pkg, lark)所需的所有库。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 确保在项目根目录 (drone/) 下执行
|
|
||||||
pip install -r backend_service/requirements.txt
|
pip install -r backend_service/requirements.txt
|
||||||
|
|
||||||
|
# 3) 可选:设置设备地址(本地默认 localhost)
|
||||||
|
export ORIN_IP="localhost"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. 编译ROS 2接口
|
## 3. 启动服务(推荐)
|
||||||
|
|
||||||
为了让后端服务能够像导入普通Python包一样导入我们自定义的Action接口 (`drone_interfaces`),你需要先使用`colcon`对其进行编译。
|
### 3.1 llama.cpp 路线
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 确保在项目根目录 (drone/) 下执行
|
cd /home/huangfukk/DronePlanning
|
||||||
colcon build
|
./start_all.sh start
|
||||||
```
|
```
|
||||||
成功后,您会看到`build/`, `install/`, `log/`三个新目录。这一步会将`.action`文件转换为Python和C++代码。
|
|
||||||
|
|
||||||
---
|
常用命令:
|
||||||
|
|
||||||
### 阶段二:数据处理流水线
|
|
||||||
|
|
||||||
此阶段为RAG系统准备数据,让LLM能够理解任务环境。
|
|
||||||
|
|
||||||
#### 1. 准备原始数据
|
|
||||||
|
|
||||||
将你的原始数据文件(例如,`.world`, `.json` 文件等)放入 `tools/map/` 目录中。
|
|
||||||
|
|
||||||
#### 2. 数据预处理
|
|
||||||
|
|
||||||
运行脚本将原始数据“翻译”成自然语言知识。
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 确保在项目根目录 (drone/) 下,并已激活backend环境
|
./start_all.sh stop
|
||||||
cd tools
|
./start_all.sh restart
|
||||||
python build_knowledge_base.py
|
./start_all.sh vl
|
||||||
|
./start_all.sh restart-vl
|
||||||
|
./start_all.sh status
|
||||||
```
|
```
|
||||||
该脚本会扫描 `tools/map/` 目录,并在 `tools/knowledge_base/` 目录下生成对应的 `_knowledge.ndjson` 文件。
|
|
||||||
|
|
||||||
#### 3. 数据入库(Ingestion)
|
### 3.2 vLLM 路线
|
||||||
|
|
||||||
运行脚本将处理好的知识加载到向量数据库中。
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 仍在tools/目录下执行
|
cd /home/huangfukk/DronePlanning
|
||||||
python ingest.py
|
./start_all_vllm.sh start
|
||||||
```
|
```
|
||||||
该脚本会自动扫描 `tools/knowledge_base/` 目录,并将数据存入 `tools/vector_store/` 目录中。
|
|
||||||
|
|
||||||
---
|
常用命令:
|
||||||
|
|
||||||
### 阶段三:服务启动与测试
|
|
||||||
|
|
||||||
完成前两个阶段后,即可启动并测试后端服务。
|
|
||||||
|
|
||||||
#### 1. 启动后端服务
|
|
||||||
|
|
||||||
启动服务的关键在于**按顺序激活环境**:先激活ROS 2工作空间,再激活Conda环境。
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 切换到项目根目录
|
./start_all_vllm.sh stop
|
||||||
cd /path/to/your/drone
|
./start_all_vllm.sh restart
|
||||||
|
./start_all_vllm.sh status
|
||||||
# 2. 激活ROS 2编译环境
|
|
||||||
# 作用:将我们编译好的`drone_interfaces`包的路径告知系统,否则Python会报`ModuleNotFoundError`。
|
|
||||||
# 注意:此命令必须在每次打开新终端时执行一次。
|
|
||||||
source install/setup.bash
|
|
||||||
|
|
||||||
# 3. 激活Conda Python环境
|
|
||||||
conda activate backend
|
|
||||||
|
|
||||||
# 4. 启动FastAPI服务
|
|
||||||
cd backend_service/
|
|
||||||
uvicorn src.main:app --host 0.0.0.0 --port 8000
|
|
||||||
```
|
```
|
||||||
当您看到日志中出现 `Uvicorn running on http://0.0.0.0:8000` 时,表示服务已成功启动。
|
|
||||||
|
|
||||||
#### 2. 运行API接口测试
|
## 4. 构建与入库(RAG)
|
||||||
|
|
||||||
我们提供了一个脚本来验证核心的“任务生成”功能。
|
|
||||||
|
|
||||||
**打开一个新的终端**,并执行以下命令:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 切换到项目根目录
|
cd /home/huangfukk/DronePlanning
|
||||||
cd /path/to/your/drone
|
source backend_service/venv/bin/activate
|
||||||
|
|
||||||
# 2. 激活Conda环境
|
# 1) 从 map 生成知识文本(可选,已有知识可跳过)
|
||||||
conda activate backend
|
python tools/rag/build_knowledge_base.py
|
||||||
|
|
||||||
# 3. 运行测试脚本
|
# 2) 入库到 ChromaDB(Location/Pattern/Rules + 兼容集合)
|
||||||
cd tools/
|
python tools/rag/ingest.py
|
||||||
python test_api.py
|
|
||||||
```
|
|
||||||
如果一切正常,您将在终端看到一系列 `PASS` 信息,以及从服务器返回的Pytree JSON。
|
|
||||||
|
|
||||||
#### 3. API接口使用说明
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 故障排除 / 常见问题 (FAQ)
|
|
||||||
|
|
||||||
以下是在配置和运行此项目时可能遇到的一些常见问题及其解决方案。
|
|
||||||
|
|
||||||
#### **Q1: 启动服务时报错 `ModuleNotFoundError: No module named 'drone_interfaces'`**
|
|
||||||
|
|
||||||
- **原因**: 您当前的终端环境没有加载ROS 2工作空间的路径。仅仅激活Conda环境是不够的。
|
|
||||||
- **解决方案**: 严格遵循“启动后端服务”章节的说明,在激活Conda环境**之前**,必须先运行 `source install/setup.bash` 命令。
|
|
||||||
|
|
||||||
#### **Q2: `colcon build` 编译失败,提示 `ModuleNotFoundError: No module named 'em'`, `'catkin_pkg'`, 或 `'lark'`**
|
|
||||||
|
|
||||||
- **原因**: 您的Python环境中缺少ROS 2编译代码时所必需的依赖包。
|
|
||||||
- **解决方案**: 我们已将所有已知的编译时依赖(`empy`, `catkin-pkg`, `lark`等)添加到了`requirements.txt`中。请确保您已激活正确的Conda环境,然后运行 `pip install -r backend_service/requirements.txt` 来安装它们。
|
|
||||||
|
|
||||||
#### **Q3: 启动服务时报错 `ImportError: ... GLIBCXX_... not found` 或 `ModuleNotFoundError: No module named 'rclpy._rclpy_pybind11'`**
|
|
||||||
|
|
||||||
- **原因**: 您的Conda环境与系统ROS 2环境存在核心库冲突。最常见的原因是Python版本不匹配(例如,Conda是Python 3.11而ROS 2 Humble需要3.10),或者Conda自带的C++库与系统库冲突。
|
|
||||||
- **解决方案**: 这是最棘手的环境问题。最可靠的解决方法是彻底删除当前的Conda环境 (`conda env remove --name backend`),然后严格按照本文档「环境设置」章节的说明,用正确的命令 (`conda create --name backend --channel conda-forge --no-default-packages python=3.10`) 重建一个干净、兼容的环境。
|
|
||||||
|
|
||||||
#### **Q4: 服务启动时,日志显示正在从网络上下载模型(例如 `all-MiniLM-L6-v2`)**
|
|
||||||
|
|
||||||
- **原因**: 后端服务在连接向量数据库时,没有正确指定使用远程嵌入模型,导致ChromaDB退回到默认的、需要下载模型的本地嵌入函数。
|
|
||||||
- **解决方案**: 此问题在当前代码中**已被修复**。`backend_service/src/py_tree_generator.py`现在会正确地将远程嵌入函数实例传递给ChromaDB。如果您在自己的代码中遇到此问题,请检查您的`get_collection`调用。
|
|
||||||
|
|
||||||
#### **Q5: 服务启动时,日志停在 `waiting for action server...`,无法访问API**
|
|
||||||
|
|
||||||
- **原因**: 代码中存在阻塞式的`wait_for_server()`调用,它会一直等待直到无人机端的Action服务器上线,从而卡住了Web服务的启动流程。
|
|
||||||
- **解决方案**: 此问题在当前代码中**已被修复**。`backend_service/src/ros2_client.py`现在使用非阻塞的方式初始化,并在发送任务时检查服务器是否可用。
|
|
||||||
|
|
||||||
##### **A. 生成任务计划**
|
|
||||||
|
|
||||||
接收自然语言指令,返回生成的行为树(py_tree)JSON。
|
|
||||||
|
|
||||||
- **Endpoint**: `POST /generate_plan`
|
|
||||||
- **Request Body**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user_prompt": "无人机起飞到10米,然后前往机库,最后降落。"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **Success Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"root": { ... },
|
|
||||||
"plan_id": "some-unique-id",
|
|
||||||
"visualization_url": "/static/py_tree.png"
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### **B. 查看任务可视化**
|
> 运行 `ingest.py` 前,请确保 embedding 服务(8090)已启动。
|
||||||
|
|
||||||
获取最新生成的行为树的可视化图像。
|
## 5. 快速接口验证
|
||||||
|
|
||||||
- **Endpoint**: `GET /static/py_tree.png`
|
```bash
|
||||||
- **Usage**: 在浏览器中直接打开 `http://<服务器IP>:8000/static/py_tree.png` 即可查看。每次成功调用 `/generate_plan` 后,该图像都会被更新。
|
cd /home/huangfukk/DronePlanning
|
||||||
|
|
||||||
##### **C. 执行任务**
|
# 健康检查
|
||||||
|
curl -s http://localhost:8000/docs >/dev/null && echo "fastapi ok"
|
||||||
|
curl -s http://localhost:8081/v1/models && echo "llm ok"
|
||||||
|
|
||||||
接收一个py_tree JSON,下发给无人机执行(当前为模拟执行)。
|
# 生成计划
|
||||||
|
curl -s http://localhost:8000/generate_plan \
|
||||||
- **Endpoint**: `POST /execute_mission`
|
-H "Content-Type: application/json" \
|
||||||
- **Request Body**: (使用 `/generate_plan` 返回的 `root` 对象)
|
-d '{"user_prompt":"无人机当前在地面,到广场查找绿色公交车,找到就拍照"}'
|
||||||
```json
|
|
||||||
{
|
|
||||||
"py_tree": {
|
|
||||||
"root": { ... }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **Response**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "execution_started"
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### **D. 接收实时状态**
|
## 6. 测试命令
|
||||||
|
|
||||||
通过WebSocket连接,实时接收无人机在执行任务时的状态反馈。
|
```bash
|
||||||
|
cd /home/huangfukk/DronePlanning
|
||||||
|
source backend_service/venv/bin/activate
|
||||||
|
|
||||||
- **Endpoint**: `WS /ws/status`
|
# API 回归
|
||||||
- **Usage**: 使用任意WebSocket客户端连接到 `ws://<服务器IP>:8000/ws/status`。当任务执行时,服务器会主动推送JSON消息,例如:
|
python tools/test_api.py
|
||||||
```json
|
|
||||||
{"node_id": "takeoff_node_1", "status": 0} // 0: RUNNING
|
# 直接测试 8081 的 OpenAI 兼容推理接口
|
||||||
{"node_id": "takeoff_node_1", "status": 1} // 1: SUCCESS
|
python tools/test_llama_server.py \
|
||||||
|
--system-file backend_service/src/prompts/system_prompt_vllm.txt \
|
||||||
|
--user "起飞到10米然后降落" \
|
||||||
|
--base-url "http://127.0.0.1:8081/v1" \
|
||||||
|
--verbose
|
||||||
|
|
||||||
|
# 交互式/批量验证
|
||||||
|
python tools/test_validate/run_tests.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 7. 关键 API
|
||||||
|
|
||||||
|
- `POST /generate_plan`
|
||||||
|
- `POST /execute_mission`(当前返回 execution_disabled)
|
||||||
|
- `WS /ws/status`
|
||||||
|
- `GET /static/py_tree.png`
|
||||||
|
|
||||||
|
## 8. 日志与排障
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/huangfukk/DronePlanning
|
||||||
|
tail -f logs/*.log
|
||||||
|
```
|
||||||
|
|
||||||
|
常见日志:
|
||||||
|
|
||||||
|
- `logs/inference_model.log`(llama.cpp 推理)
|
||||||
|
- `logs/vllm_inference_model.log`(vLLM 推理)
|
||||||
|
- `logs/embedding_model.log`
|
||||||
|
- `logs/fastapi.log`
|
||||||
|
|
||||||
|
如果 `tools/rag/ingest.py` 在 Chroma 初始化时报错,优先确认:
|
||||||
|
|
||||||
|
1. 是否使用项目 venv 运行
|
||||||
|
2. `tools/rag/vector_store/` 是否损坏(可备份后重建)
|
||||||
|
3. embedding 服务是否可访问(`http://localhost:8090/v1/embeddings`)
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
好的,我现在需要处理用户的任务指令,生成一个符合要求的行为树JSON。首先,我得仔细阅读用户的指令和参考知识,确保理解所有要求。
|
||||||
|
|
||||||
|
用户的目标是搜索并锁定危险性最高的气球(红色>蓝色>绿色),然后跟踪30秒后进行打击。参考知识中提到了三个地点,但用户可能希望搜索的区域是这些地点附近,或者需要根据这些坐标来规划路径。
|
||||||
|
|
||||||
|
首先,我需要确定无人机的起降点。参考知识中的地点坐标可能作为搜索区域的中心。比如,用户提到的“学生宿舍”坐标是(5,3,2),但可能需要选择一个合适的中心点进行搜索。不过用户没有明确指定搜索区域,可能需要使用search_pattern来覆盖这些区域,或者直接在某个中心点周围搜索。
|
||||||
|
|
||||||
|
接下来,任务流程应该是:起飞→飞往搜索区域→搜索目标→检测到目标后跟踪→打击。同时必须包含安全监控。
|
||||||
|
|
||||||
|
根据用户提供的参考知识,可能需要将搜索区域设置为某个中心点。比如,用户提到的“跷跷板”在(x:15, y:-8.5, z:1.2),但z坐标需要调整到至少1米,所以可能设置为z=2。或者选择其他地点作为搜索中心。但用户没有明确说明,可能需要假设搜索区域是这些地点的附近,或者使用其中一个作为中心。
|
||||||
|
|
||||||
|
另外,用户提到要搜索并锁定危险性最高的气球,所以需要使用search_pattern或者object_detect。但因为是未知区域,可能更适合使用search_pattern,或者先飞到某个区域再进行检测。
|
||||||
|
|
||||||
|
根据任务范式,可能需要先飞到某个坐标点,然后进行搜索。例如,使用fly_to_waypoint飞到某个中心点,然后执行search_pattern。或者直接使用search_pattern覆盖多个区域。
|
||||||
|
|
||||||
|
不过参考知识中的三个地点可能作为搜索区域的中心,可能需要将搜索区域设置为这些点的附近。但用户没有明确说明,可能需要选择一个中心点,比如“学生宿舍”的坐标,或者综合考虑。
|
||||||
|
|
||||||
|
另外,用户提到要锁定危险性最高的气球,所以需要检测到红色气球。因此,在search_pattern中,target_class应为balloon,并且description为“红色气球危险性高于蓝色气球高于绿色气球”,或者在object_detect中设置description参数为“红>蓝>绿”。
|
||||||
|
|
||||||
|
但根据搜索模式,search_pattern的参数需要target_class和description。所以可能需要设置target_class为balloon,description为“红>蓝>绿”。
|
||||||
|
|
||||||
|
接下来,流程应该是:
|
||||||
|
|
||||||
|
1. 起飞到一定高度。
|
||||||
|
2. 飞往搜索区域的中心点(比如学生宿舍的坐标,调整z到2)。
|
||||||
|
3. 执行search_pattern,搜索红色气球。
|
||||||
|
4. 一旦检测到目标,进行跟踪。
|
||||||
|
5. 跟踪30秒后打击。
|
||||||
|
|
||||||
|
同时,安全监控必须包含电池和GPS检查,以及紧急处理。
|
||||||
|
|
||||||
|
现在需要构建行为树结构。根节点是Parallel,包含主任务和安全监控。主任务是Sequence,包含起飞、飞往搜索点、搜索、跟踪、打击、降落。
|
||||||
|
|
||||||
|
在搜索部分,可能需要使用search_pattern,或者先飞到某个点再进行object_detect。但根据用户指令,可能更适合使用search_pattern,因为需要在未知区域搜索。
|
||||||
|
|
||||||
|
所以,主任务步骤:
|
||||||
|
|
||||||
|
- preflight_checks
|
||||||
|
- takeoff
|
||||||
|
- fly_to_waypoint到某个中心点(比如学生宿舍的坐标,调整z为2)
|
||||||
|
- search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type可能是spiral或grid,假设用spiral,中心点为学生宿舍的坐标,半径适当,比如50米。
|
||||||
|
- 然后,当检测到目标后,进行track_object,跟踪30秒。
|
||||||
|
- 然后strike_target,打击。
|
||||||
|
- 最后降落。
|
||||||
|
|
||||||
|
但需要确保在search_pattern之后,检测到目标,然后进入跟踪。或者可能需要使用Selector来选择直接检测或搜索。
|
||||||
|
|
||||||
|
但根据用户指令,可能需要先飞到某个点,再执行search_pattern。或者直接使用search_pattern覆盖多个区域。
|
||||||
|
|
||||||
|
参考知识中的三个地点可能作为搜索区域的中心,但用户没有明确说明,可能需要选择其中一个作为搜索中心。比如学生宿舍的坐标(x:5, y:3, z:2),调整z为2。
|
||||||
|
|
||||||
|
所以,fly_to_waypoint的参数是x=5, y=3, z=2, acceptance_radius=2.0。
|
||||||
|
|
||||||
|
然后执行search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,中心点为学生宿舍的坐标,半径比如50米。
|
||||||
|
|
||||||
|
之后,当检测到目标后,进行track_object,参数为balloon,description为“红>蓝>绿”,track_time=30.0,min_confidence=0.7,safe_distance=15.0。
|
||||||
|
|
||||||
|
然后strike_target,参数为balloon,description为“红>蓝>绿”,count=1。
|
||||||
|
|
||||||
|
最后降落。
|
||||||
|
|
||||||
|
同时,安全监控需要包含电池和GPS检查,以及紧急处理。
|
||||||
|
|
||||||
|
现在需要构建JSON结构,确保所有参数符合约束,并且流程正确。
|
||||||
|
|
||||||
|
可能的搜索中心是学生宿舍的坐标,调整z为2。然后search_pattern的参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,radius为50米,center_x=5, center_y=3, center_z=2。
|
||||||
|
|
||||||
|
然后,当检测到目标后,进行track_object,跟踪30秒,然后打击。
|
||||||
|
|
||||||
|
在行为树中,主任务Sequence的步骤包括:
|
||||||
|
|
||||||
|
- preflight_checks
|
||||||
|
- takeoff (altitude=2.0)
|
||||||
|
- fly_to_waypoint到学生宿舍的坐标,调整z为2
|
||||||
|
- search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,radius=50,center_x=5, center_y=3, center_z=2
|
||||||
|
- 然后,当检测到目标后,进行track_object
|
||||||
|
- strike_target
|
||||||
|
- land
|
||||||
|
|
||||||
|
同时,安全监控的Selector包含电池和GPS条件,以及紧急处理。
|
||||||
|
|
||||||
|
现在需要检查所有参数是否符合约束,例如z=2,符合≥1。
|
||||||
|
|
||||||
|
其他参数如radius=50,符合[5,1000]。
|
||||||
|
|
||||||
|
所以,生成的JSON结构应该符合这些要求,并且流程正确。
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 200 KiB |
75
backend_service/history/20260120_102200_plan.json
Normal file
75
backend_service/history/20260120_102200_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260120_102205_plan.json
Normal file
51
backend_service/history/20260120_102205_plan.json
Normal file
File diff suppressed because one or more lines are too long
63
backend_service/history/20260120_102211_plan.json
Normal file
63
backend_service/history/20260120_102211_plan.json
Normal file
File diff suppressed because one or more lines are too long
44
backend_service/history/20260120_102215_plan.json
Normal file
44
backend_service/history/20260120_102215_plan.json
Normal file
File diff suppressed because one or more lines are too long
75
backend_service/history/20260120_102222_plan.json
Normal file
75
backend_service/history/20260120_102222_plan.json
Normal file
File diff suppressed because one or more lines are too long
47
backend_service/history/20260120_102226_plan.json
Normal file
47
backend_service/history/20260120_102226_plan.json
Normal file
File diff suppressed because one or more lines are too long
64
backend_service/history/20260120_102232_plan.json
Normal file
64
backend_service/history/20260120_102232_plan.json
Normal file
File diff suppressed because one or more lines are too long
40
backend_service/history/20260120_102236_plan.json
Normal file
40
backend_service/history/20260120_102236_plan.json
Normal file
File diff suppressed because one or more lines are too long
75
backend_service/history/20260120_102742_plan.json
Normal file
75
backend_service/history/20260120_102742_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260120_102747_plan.json
Normal file
51
backend_service/history/20260120_102747_plan.json
Normal file
File diff suppressed because one or more lines are too long
63
backend_service/history/20260120_102755_plan.json
Normal file
63
backend_service/history/20260120_102755_plan.json
Normal file
File diff suppressed because one or more lines are too long
44
backend_service/history/20260120_102759_plan.json
Normal file
44
backend_service/history/20260120_102759_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_111034_plan.json
Normal file
56
backend_service/history/20260120_111034_plan.json
Normal file
File diff suppressed because one or more lines are too long
82
backend_service/history/20260120_111135_plan.json
Normal file
82
backend_service/history/20260120_111135_plan.json
Normal file
File diff suppressed because one or more lines are too long
82
backend_service/history/20260120_111754_plan.json
Normal file
82
backend_service/history/20260120_111754_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260120_111830_plan.json
Normal file
78
backend_service/history/20260120_111830_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260120_111903_plan.json
Normal file
78
backend_service/history/20260120_111903_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260120_115045_plan.json
Normal file
78
backend_service/history/20260120_115045_plan.json
Normal file
File diff suppressed because one or more lines are too long
82
backend_service/history/20260120_115356_plan.json
Normal file
82
backend_service/history/20260120_115356_plan.json
Normal file
File diff suppressed because one or more lines are too long
82
backend_service/history/20260120_115415_plan.json
Normal file
82
backend_service/history/20260120_115415_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260120_115816_plan.json
Normal file
78
backend_service/history/20260120_115816_plan.json
Normal file
File diff suppressed because one or more lines are too long
77
backend_service/history/20260120_115851_plan.json
Normal file
77
backend_service/history/20260120_115851_plan.json
Normal file
File diff suppressed because one or more lines are too long
50
backend_service/history/20260120_115900_plan.json
Normal file
50
backend_service/history/20260120_115900_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_115910_plan.json
Normal file
56
backend_service/history/20260120_115910_plan.json
Normal file
File diff suppressed because one or more lines are too long
64
backend_service/history/20260120_115921_plan.json
Normal file
64
backend_service/history/20260120_115921_plan.json
Normal file
File diff suppressed because one or more lines are too long
45
backend_service/history/20260120_115929_plan.json
Normal file
45
backend_service/history/20260120_115929_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260120_115942_plan.json
Normal file
78
backend_service/history/20260120_115942_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260120_115954_plan.json
Normal file
72
backend_service/history/20260120_115954_plan.json
Normal file
File diff suppressed because one or more lines are too long
45
backend_service/history/20260120_120002_plan.json
Normal file
45
backend_service/history/20260120_120002_plan.json
Normal file
File diff suppressed because one or more lines are too long
62
backend_service/history/20260120_120014_plan.json
Normal file
62
backend_service/history/20260120_120014_plan.json
Normal file
File diff suppressed because one or more lines are too long
40
backend_service/history/20260120_120022_plan.json
Normal file
40
backend_service/history/20260120_120022_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_120356_plan.json
Normal file
56
backend_service/history/20260120_120356_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_120657_plan.json
Normal file
56
backend_service/history/20260120_120657_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_122451_plan.json
Normal file
56
backend_service/history/20260120_122451_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_122519_plan.json
Normal file
56
backend_service/history/20260120_122519_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_122538_plan.json
Normal file
56
backend_service/history/20260120_122538_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123118_plan.json
Normal file
56
backend_service/history/20260120_123118_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123142_plan.json
Normal file
56
backend_service/history/20260120_123142_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123322_plan.json
Normal file
56
backend_service/history/20260120_123322_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123342_plan.json
Normal file
56
backend_service/history/20260120_123342_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123516_plan.json
Normal file
56
backend_service/history/20260120_123516_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123521_plan.json
Normal file
56
backend_service/history/20260120_123521_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123526_plan.json
Normal file
56
backend_service/history/20260120_123526_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123531_plan.json
Normal file
56
backend_service/history/20260120_123531_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123536_plan.json
Normal file
56
backend_service/history/20260120_123536_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123541_plan.json
Normal file
56
backend_service/history/20260120_123541_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123546_plan.json
Normal file
56
backend_service/history/20260120_123546_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123551_plan.json
Normal file
56
backend_service/history/20260120_123551_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123556_plan.json
Normal file
56
backend_service/history/20260120_123556_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123601_plan.json
Normal file
56
backend_service/history/20260120_123601_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_123801_plan.json
Normal file
56
backend_service/history/20260120_123801_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_125258_plan.json
Normal file
56
backend_service/history/20260120_125258_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260120_125315_plan.json
Normal file
56
backend_service/history/20260120_125315_plan.json
Normal file
File diff suppressed because one or more lines are too long
63
backend_service/history/20260121_202255_plan.json
Normal file
63
backend_service/history/20260121_202255_plan.json
Normal file
File diff suppressed because one or more lines are too long
79
backend_service/history/20260203_222653_plan.json
Normal file
79
backend_service/history/20260203_222653_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_222711_plan.json
Normal file
72
backend_service/history/20260203_222711_plan.json
Normal file
File diff suppressed because one or more lines are too long
53
backend_service/history/20260203_222720_plan.json
Normal file
53
backend_service/history/20260203_222720_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_222813_plan.json
Normal file
72
backend_service/history/20260203_222813_plan.json
Normal file
File diff suppressed because one or more lines are too long
49
backend_service/history/20260203_222823_plan.json
Normal file
49
backend_service/history/20260203_222823_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_222838_plan.json
Normal file
56
backend_service/history/20260203_222838_plan.json
Normal file
File diff suppressed because one or more lines are too long
18
backend_service/history/20260203_222840_plan.json
Normal file
18
backend_service/history/20260203_222840_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260203_222851_plan.json
Normal file
51
backend_service/history/20260203_222851_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260203_222907_plan.json
Normal file
78
backend_service/history/20260203_222907_plan.json
Normal file
File diff suppressed because one or more lines are too long
19
backend_service/history/20260203_222910_plan.json
Normal file
19
backend_service/history/20260203_222910_plan.json
Normal file
File diff suppressed because one or more lines are too long
62
backend_service/history/20260203_222943_plan.json
Normal file
62
backend_service/history/20260203_222943_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_222954_plan.json
Normal file
56
backend_service/history/20260203_222954_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_223851_plan.json
Normal file
72
backend_service/history/20260203_223851_plan.json
Normal file
File diff suppressed because one or more lines are too long
49
backend_service/history/20260203_223901_plan.json
Normal file
49
backend_service/history/20260203_223901_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_223912_plan.json
Normal file
56
backend_service/history/20260203_223912_plan.json
Normal file
File diff suppressed because one or more lines are too long
20
backend_service/history/20260203_223915_plan.json
Normal file
20
backend_service/history/20260203_223915_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260203_223925_plan.json
Normal file
51
backend_service/history/20260203_223925_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260203_223938_plan.json
Normal file
78
backend_service/history/20260203_223938_plan.json
Normal file
File diff suppressed because one or more lines are too long
19
backend_service/history/20260203_223941_plan.json
Normal file
19
backend_service/history/20260203_223941_plan.json
Normal file
File diff suppressed because one or more lines are too long
67
backend_service/history/20260203_224016_plan.json
Normal file
67
backend_service/history/20260203_224016_plan.json
Normal file
File diff suppressed because one or more lines are too long
40
backend_service/history/20260203_224025_plan.json
Normal file
40
backend_service/history/20260203_224025_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_224508_plan.json
Normal file
72
backend_service/history/20260203_224508_plan.json
Normal file
File diff suppressed because one or more lines are too long
49
backend_service/history/20260203_224518_plan.json
Normal file
49
backend_service/history/20260203_224518_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_224529_plan.json
Normal file
56
backend_service/history/20260203_224529_plan.json
Normal file
File diff suppressed because one or more lines are too long
20
backend_service/history/20260203_224532_plan.json
Normal file
20
backend_service/history/20260203_224532_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260203_224542_plan.json
Normal file
51
backend_service/history/20260203_224542_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260203_224557_plan.json
Normal file
78
backend_service/history/20260203_224557_plan.json
Normal file
File diff suppressed because one or more lines are too long
19
backend_service/history/20260203_224559_plan.json
Normal file
19
backend_service/history/20260203_224559_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_224642_plan.json
Normal file
72
backend_service/history/20260203_224642_plan.json
Normal file
File diff suppressed because one or more lines are too long
49
backend_service/history/20260203_224653_plan.json
Normal file
49
backend_service/history/20260203_224653_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_224705_plan.json
Normal file
56
backend_service/history/20260203_224705_plan.json
Normal file
File diff suppressed because one or more lines are too long
18
backend_service/history/20260203_224707_plan.json
Normal file
18
backend_service/history/20260203_224707_plan.json
Normal file
File diff suppressed because one or more lines are too long
51
backend_service/history/20260203_224718_plan.json
Normal file
51
backend_service/history/20260203_224718_plan.json
Normal file
File diff suppressed because one or more lines are too long
78
backend_service/history/20260203_224732_plan.json
Normal file
78
backend_service/history/20260203_224732_plan.json
Normal file
File diff suppressed because one or more lines are too long
19
backend_service/history/20260203_224736_plan.json
Normal file
19
backend_service/history/20260203_224736_plan.json
Normal file
File diff suppressed because one or more lines are too long
67
backend_service/history/20260203_224810_plan.json
Normal file
67
backend_service/history/20260203_224810_plan.json
Normal file
File diff suppressed because one or more lines are too long
40
backend_service/history/20260203_224818_plan.json
Normal file
40
backend_service/history/20260203_224818_plan.json
Normal file
File diff suppressed because one or more lines are too long
72
backend_service/history/20260203_225639_plan.json
Normal file
72
backend_service/history/20260203_225639_plan.json
Normal file
File diff suppressed because one or more lines are too long
49
backend_service/history/20260203_225648_plan.json
Normal file
49
backend_service/history/20260203_225648_plan.json
Normal file
File diff suppressed because one or more lines are too long
56
backend_service/history/20260203_225701_plan.json
Normal file
56
backend_service/history/20260203_225701_plan.json
Normal file
File diff suppressed because one or more lines are too long
63
backend_service/history/20260203_225712_plan.json
Normal file
63
backend_service/history/20260203_225712_plan.json
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user