docs: translate AI chat API design document to English

Translate the GNS3 Copilot Agent Chat API design document from Chinese to English to improve accessibility for international contributors and align with project documentation standards. The translation covers all sections including overview, core features, architecture design, API endpoints, and response formats.
This commit is contained in:
YueGuobin committed 2026-03-06 01:14:52 +08:00
1 parent 8fa6d6810a
commit 216b3d7219
6 files changed
+1226 -1226

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -1,42 +1,42 @@
# TODO: 修复孤儿 Tool Calls 导致的 Checkpoint 状态不一致
# TODO: Fix Orphan Tool Calls Causing Checkpoint State Inconsistency
## 问题描述
## Problem Description
当 LangGraph agent 在执行过程中异常终止(如服务被强制关闭、进程崩溃等),可能导致 checkpoint 中保存了包含 `tool_calls` 的 `AIMessage`,但没有对应的 `ToolMessage`。这种状态不一致会导致后续对话恢复时出现错误。
When the LangGraph agent terminates abnormally during execution (such as forced service shutdown, process crash, etc.), it may result in a checkpoint containing an `AIMessage` with `tool_calls` but no corresponding `ToolMessage`. This state inconsistency can cause errors during subsequent conversation recovery.
### 术语说明
### Terminology
- **孤儿 tool_calls**:`AIMessage` 中包含 `tool_calls` 字段,但消息列表中没有对应的 `ToolMessage`
- **Checkpoint**:LangGraph 用于持久化对话状态的机制
- **状态不一致**:checkpoint 中的消息状态不符合预期的消息对(AIMessage + ToolMessage)
- **Orphan tool_calls**: `AIMessage` contains `tool_calls` field, but there's no corresponding `ToolMessage` in the message list
- **Checkpoint**: LangGraph's mechanism for persisting conversation state
- **State inconsistency**: Message state in checkpoint doesn't match expected message pairs (AIMessage + ToolMessage)
---
## 触发场景
## Trigger Scenarios
### 场景 1:进程异常终止(主要问题)
### Scenario 1: Process Abnormal Termination (Primary Issue)
```
执行流程:
用户消息 → llm_call → AIMessage(tool_calls) → [Checkpoint 保存]
Execution flow:
User message → llm_call → AIMessage(tool_calls) → [Checkpoint saved]
↓
[进程崩溃/服务关闭]
[Process crash/service shutdown]
↓
tool_node 未执行
tool_node not executed
↓
Checkpoint 中:
- AIMessage (有 tool_calls) ✅
- ToolMessage ❌ 缺失
Checkpoint contains:
- AIMessage (has tool_calls) ✅
- ToolMessage ❌ missing
```
**触发条件:**
- LLM 返回包含 tool_calls 的响应
- Checkpoint 已保存 AIMessage
- 在 tool_node 执行前服务被关闭(kill -9、Ctrl+C、崩溃等)
**Trigger conditions:**
- LLM returns a response containing tool_calls
- Checkpoint has saved AIMessage
- Service is shut down before tool_node execution (kill -9, Ctrl+C, crash, etc.)
### 场景 2:达到最大调用次数(已处理)
### Scenario 2: Maximum Call Count Reached (Already Handled)
当前代码通过 `recursion_limit_continue` 函数在 tool_node **执行后**检查剩余步数:
Current code checks remaining steps after tool_node execution via the `recursion_limit_continue` function:
```python
def recursion_limit_continue(state: MessagesState) -> Literal["llm_call", END]:
@@ -48,59 +48,59 @@ def recursion_limit_continue(state: MessagesState) -> Literal["llm_call", END]:
return END
```
**执行流程:**
**Execution flow:**
```
remaining_steps = 5
llm_call → AIMessage(tool_calls) → remaining_steps = 4
↓
should_continue → tool_node(因为有 tool_calls)
should_continue → tool_node (because there are tool_calls)
↓
tool_node → ToolMessage → remaining_steps = 3
↓
recursion_limit_continue → remaining_steps < 4 → END ✅
```
**结论:** 场景 2 不会产生孤儿 tool_calls,因为 tool_node 总是会执行并生成 ToolMessage。
**Conclusion:** Scenario 2 won't produce orphan tool_calls because tool_node always executes and generates a ToolMessage.
---
## 修复方案
## Fix Solution
### 核心思路
### Core Idea
在 `stream_chat` 开始时,对于已存在的会话,检测并修复孤儿 tool_calls。
At the start of `stream_chat`, for existing sessions, detect and fix orphan tool_calls.
### 修复策略
### Fix Strategy
**策略 A:清除 tool_calls(推荐)**
**Strategy A: Clear tool_calls (Recommended)**
创建一个新的 `AIMessage`,内容与原消息相同,但不包含 `tool_calls` 字段。
Create a new `AIMessage` with the same content as the original message but without the `tool_calls` field.
**优点:**
- 简单、干净
- 不会影响后续对话
- 用户可以重新提问
**Advantages:**
- Simple and clean
- Won't affect subsequent conversation
- User can ask the question again
**缺点:**
- 丢失了 LLM 原本的意图(但已经崩溃了,无法恢复)
**Disadvantages:**
- Loses LLM's original intent (but it already crashed, can't be recovered)
---
## 实现代码
## Implementation Code
### 1. 添加修复方法(`agent_service.py`)
### 1. Add Fix Method (`agent_service.py`)
```python
async def _fix_orphan_tool_calls(self, graph, config: dict, session_id: str):
"""
检测并修复孤儿 tool_calls(AIMessage有tool_calls但没有对应ToolMessage)。
Detect and fix orphan tool_calls (AIMessage has tool_calls but no corresponding ToolMessage).
当进程在 tool_node 执行前崩溃时会产生孤儿 tool_calls。
Orphan tool_calls occur when the process crashes before tool_node execution.
使用 LangGraph 的 aupdate_state API,安全地创建新的 checkpoint 版本。
Uses LangGraph's aupdate_state API to safely create a new checkpoint version.
"""
try:
# 1. 读取当前状态
# 1. Read current state
state = await graph.aget_state(config)
if not state or not state.values.get("messages"):
return
@@ -108,37 +108,37 @@ async def _fix_orphan_tool_calls(self, graph, config: dict, session_id: str):
messages = state.values["messages"]
last_message = messages[-1]
# 2. 检测孤儿 tool_calls
# 2. Detect orphan tool_calls
if not (hasattr(last_message, "tool_calls") and last_message.tool_calls):
return
# 检查是否有对应的 ToolMessage
# Check if there's a corresponding ToolMessage
has_tool_message = any(isinstance(m, ToolMessage) for m in messages)
if has_tool_message:
return
log.warning("检测到孤儿 tool_calls: session=%s, 将清除", session_id)
log.warning("Detected orphan tool_calls: session=%s, will clear", session_id)
# 3. 创建修复后的消息(不含 tool_calls)
# 3. Create fixed message (without tool_calls)
from langchain.messages import AIMessage
fixed_message = AIMessage(
content=last_message.content,
id=getattr(last_message, "id", None)
)
# 4. 使用 LangGraph API 更新状态(创建新 checkpoint)
# 4. Use LangGraph API to update state (create new checkpoint)
await graph.aupdate_state(config, {"messages": [fixed_message]})
log.info("孤儿 tool_calls 已修复: session=%s", session_id)
log.info("Orphan tool_calls fixed: session=%s", session_id)
except Exception as e:
log.error("修复孤儿 tool_calls 失败: %s", e, exc_info=True)
log.error("Failed to fix orphan tool_calls: %s", e, exc_info=True)
```
### 2. 在 `stream_chat` 中调用(`agent_service.py`)
### 2. Call in `stream_chat` (`agent_service.py`)
在获取 graph 之后、开始 stream 之前添加修复逻辑:
Add fix logic after getting the graph and before starting the stream:
```python
async def stream_chat(
@@ -151,7 +151,7 @@ async def stream_chat(
mode: str = "text",
llm_config: Optional[Dict[str, Any]] = None,
) -> AsyncGenerator[Dict[str, Any], None]:
# ... 现有代码 ...
# ... existing code ...
# Get or create chat session
repo = ChatSessionsRepository(self._checkpointer_conn)
@@ -163,7 +163,7 @@ async def stream_chat(
session = await repo.create_session(...)
log.debug("Created new chat session: thread_id=%s", session_id)
# ... 设置 context variables ...
# ... set context variables ...
# Build config
config = {
@@ -187,100 +187,100 @@ async def stream_chat(
# Get the compiled graph
graph = await self._get_graph()
# 🔧 修复状态:对于已存在的会话,检查并修复孤儿 tool_calls
# 🔧 Fix state: for existing sessions, check and fix orphan tool_calls
if not is_new_session:
await self._fix_orphan_tool_calls(graph, config, session_id)
log.debug("LangGraph graph obtained, starting stream")
# ... 继续现有代码 ...
# ... continue existing code ...
```
### 3. 需要添加的 import
### 3. Required Imports
确保 `agent_service.py` 中有以下 import:
Ensure `agent_service.py` has the following import:
```python
from langchain.messages import ToolMessage # 用于检测 ToolMessage 类型
from langchain.messages import ToolMessage # For detecting ToolMessage type
```
---
## 对 Checkpoint 数据库的影响
## Impact on Checkpoint Database
### LangGraph Checkpoint 机制
### LangGraph Checkpoint Mechanism
LangGraph 的 checkpoint 是**版本化**的,每次状态更新会创建新记录:
LangGraph checkpoints are **versioned** - each state update creates a new record:
```
checkpoints 表结构:
checkpoints table structure:
- thread_id
- checkpoint_id (递增的版本号)
- checkpoint (序列化的状态数据)
- checkpoint_id (incrementing version number)
- checkpoint (serialized state data)
- metadata
- ...
```
### 安全性分析
### Security Analysis
| 方面 | 影响 | 说明 |
|------|------|------|
| **原始数据** | 保留不变 | `aupdate_state` 创建新版本,不覆盖历史 |
| **数据库结构** | 完全兼容 | 使用 LangGraph 原生 API,不会破坏结构 |
| **并发安全** | 内置保护 | LangGraph 有锁机制处理并发访问 |
| **存储开销** | 很小 | 只增加一条 checkpoint 记录(约几 KB) |
| **可回滚性** | 支持 | 可回滚到修复前的任何版本 |
| Aspect | Impact | Description |
|--------|--------|-------------|
| **Original data** | Preserved unchanged | `aupdate_state` creates new version, doesn't overwrite history |
| **Database structure** | Fully compatible | Uses LangGraph native API, won't break structure |
| **Concurrency safety** | Built-in protection | LangGraph has locking mechanism for concurrent access |
| **Storage overhead** | Minimal | Only adds one checkpoint record (about a few KB) |
| **Revertibility** | Supported | Can roll back to any version before fix |
### 为什么不直接操作数据库
### Not Direct Database Manipulation
**❌ 危险方式:**
**❌ Dangerous approach:**
```python
# 直接修改数据库 - 破坏性强
# Direct database modification - destructive
await conn.execute(
"UPDATE checkpoints SET checkpoint = ? WHERE ...",
[modified_json]
)
```
**问题:**
- 可能破坏序列化格式
- 不创建新版本,覆盖历史
- 可能导致数据库锁定或损坏
- 违反 LangGraph 的设计原则
**Problems:**
- May break serialization format
- Doesn't create new version, overwrites history
- May cause database locking or corruption
- Violates LangGraph design principles
**✅ 安全方式:**
**✅ Safe approach:**
```python
# 使用 LangGraph 的 aupdate_state
# Use LangGraph's aupdate_state
await graph.aupdate_state(config, {"messages": [fixed_message]})
```
---
## 测试方法
## Testing Methods
### 方法 1:模拟崩溃测试(推荐)
### Method 1: Simulated Crash Test (Recommended)
利用强制关闭服务来模拟崩溃场景:
Simulate crash scenarios by forcibly shutting down the service:
```
步骤:
1. 启动 GNS3 服务
2. 发送一个会触发 tool_calls 的消息(例如查询拓扑)
3. 观察日志,等待看到 AIMessage 返回(有 tool_calls)
4. 在 tool_node 执行完成前,强制关闭服务:
- 方式 1: kill -9 <pid>
- 方式 2: Ctrl+C (如果支持)
5. 重启 GNS3 服务
6. 使用同一个 session_id 继续对话
7. 观察日志,应该看到:
- "检测到孤儿 tool_calls: session=xxx, 将清除"
- "孤儿 tool_calls 已修复: session=xxx"
8. 验证对话可以正常进行
Steps:
1. Start GNS3 service
2. Send a message that triggers tool_calls (e.g., query topology)
3. Observe logs, wait for AIMessage return (with tool_calls)
4. Force shutdown service before tool_node completes:
- Method 1: kill -9 <pid>
- Method 2: Ctrl+C (if supported)
5. Restart GNS3 service
6. Continue conversation using same session_id
7. Observe logs, should see:
- "Detected orphan tool_calls: session=xxx, will clear"
- "Orphan tool_calls fixed: session=xxx"
8. Verify conversation can proceed normally
```
### 方法 2:单元测试
### Method 2: Unit Tests
直接构造孤儿 tool_calls 状态来测试修复逻辑:
Directly construct orphan tool_calls state to test fix logic:
```python
# tests/test_agent_service.py
@@ -290,24 +290,24 @@ from langchain.messages import AIMessage, HumanMessage, ToolMessage
@pytest.mark.asyncio
async def test_fix_orphan_tool_calls():
"""测试孤儿 tool_calls 修复逻辑"""
"""Test orphan tool_calls fix logic"""
from gns3server.agent.gns3_copilot.agent_service import AgentService
# 创建测试用的 agent service
# Create test agent service
service = AgentService("/tmp/test_project")
await service._get_checkpointer()
graph = await service._get_graph()
config = {"configurable": {"thread_id": "test_session"}}
# 构造孤儿状态:先添加正常消息
# Construct orphan state: add normal messages first
await graph.aupdate_state(
config,
{
"messages": [
HumanMessage(content="测试消息", id="msg_1"),
HumanMessage(content="Test message", id="msg_1"),
AIMessage(
content="让我帮你查一下",
content="Let me check for you",
id="msg_2",
tool_calls=[{
"id": "call_123",
@@ -315,30 +315,30 @@ async def test_fix_orphan_tool_calls():
"args": {"project_id": "test"}
}]
)
# 注意:没有对应的 ToolMessage
# Note: No corresponding ToolMessage
],
"llm_calls": 1,
"remaining_steps": 20
}
)
# 调用修复逻辑
# Call fix logic
await service._fix_orphan_tool_calls(graph, config, "test_session")
# 验证修复结果
# Verify fix result
state = await graph.aget_state(config)
last_message = state.values["messages"][-1]
# 应该不再有 tool_calls
# Should no longer have tool_calls
assert not hasattr(last_message, "tool_calls") or not last_message.tool_calls
assert last_message.content == "让我帮你查一下"
assert last_message.content == "Let me check for you"
# 清理
# Cleanup
await service.close()
@pytest.mark.asyncio
async def test_no_fix_when_normal():
"""测试正常状态不会被误修复"""
"""Test that normal state isn't incorrectly fixed"""
from gns3server.agent.gns3_copilot.agent_service import AgentService
service = AgentService("/tmp/test_project")
@@ -347,14 +347,14 @@ async def test_no_fix_when_normal():
graph = await service._get_graph()
config = {"configurable": {"thread_id": "test_session_2"}}
# 构造正常状态:有完整的 AIMessage + ToolMessage 对
# Construct normal state: complete AIMessage + ToolMessage pair
await graph.aupdate_state(
config,
{
"messages": [
HumanMessage(content="测试消息", id="msg_1"),
HumanMessage(content="Test message", id="msg_1"),
AIMessage(
content="让我帮你查一下",
content="Let me check for you",
id="msg_2",
tool_calls=[{
"id": "call_123",
@@ -363,7 +363,7 @@ async def test_no_fix_when_normal():
}]
),
ToolMessage(
content="拓扑信息:...",
content="Topology info: ...",
tool_call_id="call_123",
name="get_topology",
id="msg_3"
@@ -374,78 +374,78 @@ async def test_no_fix_when_normal():
}
)
# 记录原始消息数量
# Record original message count
state_before = await graph.aget_state(config)
msg_count_before = len(state_before.values["messages"])
# 调用修复逻辑
# Call fix logic
await service._fix_orphan_tool_calls(graph, config, "test_session_2")
# 验证状态未改变
# Verify state unchanged
state_after = await graph.aget_state(config)
msg_count_after = len(state_after.values["messages"])
assert msg_count_before == msg_count_after # 不应该添加新消息
assert msg_count_before == msg_count_after # Should not add new messages
last_message = state_after.values["messages"][-1]
assert isinstance(last_message, ToolMessage) # 最后一条还是 ToolMessage
assert isinstance(last_message, ToolMessage) # Last is still ToolMessage
# 清理
# Cleanup
await service.close()
```
### 方法 3:增强日志和监控
### Method 3: Enhanced Logging and Monitoring
即使无法主动触发,也可以在生产环境验证修复逻辑是否生效:
Even without active triggering, you can verify fix logic works in production:
```python
# 在 _fix_orphan_tool_calls 中添加详细日志
log.warning("检测到孤儿 tool_calls: session=%s", session_id)
log.info("原始消息: tool_calls=%d, content=%s",
# Add detailed logging in _fix_orphan_tool_calls
log.warning("Detected orphan tool_calls: session=%s", session_id)
log.info("Original message: tool_calls=%d, content=%s",
len(last_message.tool_calls),
last_message.content[:100])
log.info("修复后: tool_calls=%d",
log.info("After fix: tool_calls=%d",
len(fixed_message.tool_calls) if hasattr(fixed_message, "tool_calls") else 0)
```
---
## 文件修改清单
## File Modification Checklist
### 需要修改的文件
### Files to Modify
1. **`gns3server/agent/gns3_copilot/agent_service.py`**
- 添加 `_fix_orphan_tool_calls` 方法
- 在 `stream_chat` 方法中调用修复逻辑
- Add `_fix_orphan_tool_calls` method
- Call fix logic in `stream_chat` method
### 需要添加的测试文件(可选)
### Test Files to Add (Optional)
2. **`tests/test_agent_service.py`**(新建或添加到现有测试文件)
- `test_fix_orphan_tool_calls()` - 测试孤儿 tool_calls 修复
- `test_no_fix_when_normal()` - 测试正常状态不被误修复
2. **`tests/test_agent_service.py`** (create new or add to existing test file)
- `test_fix_orphan_tool_calls()` - Test orphan tool_calls fix
- `test_no_fix_when_normal()` - Test normal state isn't incorrectly fixed
---
## 实施步骤
## Implementation Steps
1. ✅ 创建待办文档(当前文档)
2. ⬜ 在 `agent_service.py` 中添加 `_fix_orphan_tool_calls` 方法
3. ⬜ 在 `stream_chat` 中调用修复逻辑
4. ⬜ 使用模拟崩溃方法测试修复效果
5. ⬜ 添加单元测试(可选)
6. ⬜ 更新相关文档(如有必要)
1. ✅ Create TODO document (current document)
2. ⬜ Add `_fix_orphan_tool_calls` method in `agent_service.py`
3. ⬜ Call fix logic in `stream_chat`
4. ⬜ Test fix effect using simulated crash method
5. ⬜ Add unit tests (optional)
6. ⬜ Update related documentation (if necessary)
---
## 相关代码文件
## Related Code Files
- **主要修改文件**: `gns3server/agent/gns3_copilot/agent_service.py`
- **相关文件**: `gns3server/agent/gns3_copilot/agent/gns3_copilot.py`
- **测试文件**: `tests/test_agent_service.py` (待创建)
- **Main modification file**: `gns3server/agent/gns3_copilot/agent_service.py`
- **Related file**: `gns3server/agent/gns3_copilot/agent/gns3_copilot.py`
- **Test file**: `tests/test_agent_service.py` (to be created)
---
## 参考文档
## Reference Documentation
- [LangGraph Checkpointer 文档](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer)
- [LangGraph Checkpointer Documentation](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer)
- [LangGraph State Management](https://langchain-ai.github.io/langgraph/concepts/low_level/#state)
- [GNS3-Copilot AI Chat API 设计](../ai-chat-api-design.md)
- [GNS3-Copilot AI Chat API Design](../ai-chat-api-design.md)
@@ -1,69 +1,69 @@
# GNS3 Copilot Tool Response Format Standard
## 概述
## Overview
本文档定义了 GNS3 Copilot 工具的标准响应格式,确保所有工具返回统一的数据结构,便于前端处理和美化显示。
This document defines the standard response format for GNS3 Copilot tools, ensuring all tools return a unified data structure for easy frontend processing and display.
## 标准响应格式
## Standard Response Format
### 顶层结构
### Top-level Structure
所有工具应返回以下标准格式:
All tools should return the following standard format:
```python
{
"success": bool, # 整体操作是否成功
"total": int, # 总操作数量
"successful": int, # 成功数量
"failed": int, # 失败数量
"data": list[dict], # 详细结果列表
"error": str, # 全局错误信息(可选,操作完全失败时)
"metadata": dict # 元数据(可选)
"success": bool, # Whether the overall operation succeeded
"total": int, # Total number of operations
"successful": int, # Number of successful operations
"failed": int, # Number of failed operations
"data": list[dict], # Detailed result list
"error": str, # Global error message (optional, when operation completely fails)
"metadata": dict # Metadata (optional)
}
```
**字段说明**:
**Field Descriptions**:
| 字段 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `success` | `bool` | 是 | 整体操作是否成功(`failed == 0` 时为 `True`) |
| `total` | `int` | 是 | 处理的项目总数 |
| `successful` | `int` | 是 | 成功的项目数量 |
| `failed` | `int` | 是 | 失败的项目数量 |
| `data` | `list[dict]` | 是 | 每个项目的详细结果 |
| `error` | `str` | 否 | 全局错误消息(当整个操作失败时) |
| `metadata` | `dict` | 否 | 元数据(时间戳、执行时间等) |
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | `bool` | Yes | Whether the overall operation succeeded (True when `failed == 0`) |
| `total` | `int` | Yes | Total number of items processed |
| `successful` | `int` | Yes | Number of successful items |
| `failed` | `int` | Yes | Number of failed items |
| `data` | `list[dict]` | Yes | Detailed results for each item |
| `error` | `str` | No | Global error message (when entire operation fails) |
| `metadata` | `dict` | No | Metadata (timestamp, execution time, etc.) |
### 单个项目格式
### Single Item Format
`data` 数组中的每个项目应遵循以下格式:
Each item in the `data` array should follow this format:
```python
{
"id": str, # 设备/节点/链接 ID
"name": str, # 人类可读的名称
"status": "success" | "failed", # 项目状态
"result": str, # 成功时的结果或输出
"error": str # 失败时的错误信息
"id": str, # Device/node/link ID
"name": str, # Human-readable name
"status": "success" | "failed", # Item status
"result": str, # Result or output on success
"error": str # Error message on failure
}
```
**字段说明**:
**Field Descriptions**:
| 字段 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `id` | `str` | 是 | 设备/节点/链接的唯一标识符 |
| `name` | `str` | 是 | 人类可读的名称 |
| `status` | `str` | 是 | `"success"` 或 `"failed"` |
| `result` | `str` | 条件 | 状态为 `success` 时的输出 |
| `error` | `str` | 条件 | 状态为 `failed` 时的错误信息 |
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | `str` | Yes | Unique identifier for device/node/link |
| `name` | `str` | Yes | Human-readable name |
| `status` | `str` | Yes | `"success"` or `"failed"` |
| `result` | `str` | Conditional | Output when status is `success` |
| `error` | `str` | Conditional | Error message when status is `failed` |
## 示例
## Examples
### 成功响应示例
### Success Response Example
```python
# 执行多个设备的显示命令
# Execute display commands on multiple devices
{
"success": True,
"total": 3,
@@ -96,10 +96,10 @@
}
```
### 完全失败示例
### Complete Failure Example
```python
# 整个操作失败(如参数错误)
# Entire operation failed (e.g., parameter error)
{
"success": False,
"total": 0,
@@ -113,10 +113,10 @@
}
```
### 单个设备操作示例
### Single Device Operation Example
```python
# 操作单个设备
# Operate on a single device
{
"success": True,
"total": 1,
@@ -134,56 +134,56 @@
}
```
## 使用标准化函数
## Using the Standardization Function
在 `gns3server.agent.gns3_copilot.utils` 模块中提供了 `normalize_tool_response` 函数,用于将各种格式转换为标准格式:
The `normalize_tool_response` function is provided in the `gns3server.agent.gns3_copilot.utils` module to convert various formats to the standard format:
```python
from gns3server.agent.gns3_copilot.utils import normalize_tool_response
# 标准化工具响应
# Normalize tool response
normalized = normalize_tool_response(raw_response, tool_name="my_tool")
```
该函数支持:
- 列表格式(`[{...}, {...}]`)
- 字典格式(`{"nodes": [...]}`)
- 字符串格式(自动解析 JSON/Python literal)
- 混合格式(兼容旧工具)
This function supports:
- List format (`[{...}, {...}]`)
- Dict format (`{"nodes": [...]}`)
- String format (automatically parses JSON/Python literal)
- Mixed format (compatible with legacy tools)
## 兼容性
## Compatibility
### 向后兼容
### Backward Compatibility
`normalize_tool_response` 函数设计为向后兼容,可以处理现有工具的各种格式:
The `normalize_tool_response` function is designed to be backward compatible and can handle various formats from existing tools:
- `status` / `error` 字段
- `output` / `result` 字段
- `device_name` / `name` 字段
- `total_nodes` / `total` 字段
- `status` / `error` fields
- `output` / `result` fields
- `device_name` / `name` fields
- `total_nodes` / `total` fields
### 推荐的迁移策略
### Recommended Migration Strategy
1. **新工具**:直接返回标准格式
2. **现有工具**:保持不变,使用 `normalize_tool_response` 标准化
3. **前端**:依赖标准格式处理显示
1. **New Tools**: Return standard format directly
2. **Existing Tools**: Keep unchanged, use `normalize_tool_response` to standardize
3. **Frontend**: Rely on standard format for display processing
## 前端集成建议
## Frontend Integration Recommendations
### 渲染逻辑
### Rendering Logic
```javascript
function renderToolResponse(response) {
if (!response.success) {
// 显示全局错误
// Show global error
showError(response.error);
return;
}
// 显示统计摘要
// Show statistics summary
showSummary(response.total, response.successful, response.failed);
// 渲染每个项目
// Render each item
response.data.forEach(item => {
if (item.status === 'success') {
showSuccess(item.name, item.result);
@@ -194,22 +194,22 @@ function renderToolResponse(response) {
}
```
### 状态图标
### Status Icons
| 状态 | 图标建议 | 颜色 |
|------|----------|------|
| `success` | ✓ 绿色 | 绿色 |
| `failed` | ✗ 红色 | 红色 |
| `unknown` | ? 灰色 | 灰色 |
| Status | Icon Suggestion | Color |
|--------|----------------|-------|
| `success` | ✓ Green | Green |
| `failed` | ✗ Red | Red |
| `unknown` | ? Gray | Gray |
## 版本控制
## Version Control
当前标准版本:`v1.0`
Current standard version: `v1.0`
格式变更时,应更新 `metadata.version` 字段,前端据此适配。
When the format changes, update the `metadata.version` field, and the frontend adapts accordingly.
## 参考
## References
- 实现:`gns3server/agent/gns3_copilot/utils/parse_tool_content.py`
- 消息转换:`gns3server/agent/gns3_copilot/utils/message_converters.py`
- 工具示例:`gns3server/agent/gns3_copilot/tools_v2/`
- Implementation: `gns3server/agent/gns3_copilot/utils/parse_tool_content.py`
- Message conversion: `gns3server/agent/gns3_copilot/utils/message_converters.py`
- Tool examples: `gns3server/agent/gns3_copilot/tools_v2/`