Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor: 统一Hands系统常量到单个源文件 refactor: 更新Hands中文名称和描述 fix: 修复技能市场在连接状态变化时重新加载 fix: 修复身份变更提案的错误处理逻辑 docs: 更新多个功能文档的验证状态和实现位置 docs: 更新Hands系统文档 test: 添加测试文件验证工作区路径
150 lines
3.6 KiB
Markdown
150 lines
3.6 KiB
Markdown
# ZCLAW Skill系统问题修复 - 完成报告
|
||
|
||
## 问题诊断
|
||
|
||
### 用户报告的问题
|
||
对话过程中agent无法正确调用skill去完成任务。
|
||
|
||
### 根因分析
|
||
|
||
经过代码分析,发现**两个核心问题**:
|
||
|
||
#### 问题1: Agent循环不完整
|
||
**位置**: `crates/zclaw-runtime/src/loop_runner.rs`
|
||
|
||
**原始实现**:
|
||
```
|
||
用户消息 → LLM → 工具调用 → 结果直接返回
|
||
```
|
||
|
||
**正确实现应该是**:
|
||
```
|
||
用户消息 → LLM → 工具调用 → 工具结果 → LLM(处理结果) → 最终响应
|
||
```
|
||
|
||
**关键代码问题**:
|
||
```rust
|
||
// 原始代码在工具执行后直接返回结果
|
||
full_response.push_str(&format!("\n[工具 {} 结果]: {}", name, ...));
|
||
```
|
||
|
||
#### 问题2: 流式输出暴露内部细节
|
||
`StreamChunk::ToolUseDelta`事件被当作`[工具参数]`文本输出给用户,这是调试信息不应该暴露。
|
||
|
||
---
|
||
|
||
## 修复方案
|
||
|
||
### 1. 实现完整Agent循环
|
||
|
||
**文件**: `crates/zclaw-runtime/src/loop_runner.rs`
|
||
|
||
**改动**:
|
||
- 添加循环结构, 最多10次迭代
|
||
- 检查LLM响应中的工具调用
|
||
- 如果没有工具调用, 返回最终响应
|
||
- 如果有工具调用, 执行后添加结果到消息历史, **继续循环让LLM处理**
|
||
|
||
```rust
|
||
loop {
|
||
iterations += 1;
|
||
if iterations > max_iterations { return error; }
|
||
|
||
// 调用LLM
|
||
let response = self.driver.complete(request).await?;
|
||
|
||
// 提取工具调用
|
||
let tool_calls = response.content.iter().filter_map(|b| match b {
|
||
ContentBlock::ToolUse { id, name, input } => Some((id, name, input)),
|
||
_ => None
|
||
}).collect();
|
||
|
||
// 没有工具调用 - 返回最终响应
|
||
if tool_calls.is_empty() {
|
||
return Ok(AgentLoopResult { response: text, ... });
|
||
}
|
||
|
||
// 添加工具调用到消息历史
|
||
for (id, name, input) in &tool_calls {
|
||
messages.push(Message::tool_use(id, ToolId::new(name), input));
|
||
}
|
||
|
||
// 执行工具
|
||
for (id, name, input) in tool_calls {
|
||
let result = self.execute_tool(&name, input, &context).await;
|
||
messages.push(Message::tool_result(id, ToolId::new(&name), result));
|
||
}
|
||
|
||
// 继续循环 - LLM将处理工具结果
|
||
}
|
||
```
|
||
|
||
### 2. 简化LoopEvent
|
||
|
||
**改动**:
|
||
```rust
|
||
pub enum LoopEvent {
|
||
Delta(String),
|
||
ToolStart { name: String },
|
||
ToolEnd { name: String, output: serde_json::Value },
|
||
IterationStart { iteration: usize, max_iterations: usize },
|
||
Complete(AgentLoopResult),
|
||
Error(String),
|
||
}
|
||
```
|
||
|
||
**隐藏的信息**:
|
||
- `ToolUseDelta` - 工具参数增量(内部处理)
|
||
- 工具的详细input/output(只记录到消息历史)
|
||
|
||
### 3. 多轮流式支持
|
||
|
||
**run_streaming方法**:
|
||
- 实现与`run`相同的循环逻辑
|
||
- 支持流式输出
|
||
- 发送`IterationStart`事件通知新迭代开始
|
||
|
||
---
|
||
|
||
## 验证
|
||
|
||
### 编译状态
|
||
✅ 编译通过,无错误
|
||
|
||
### 需要测试
|
||
1. **单次工具调用** - "搜索腾讯财报"
|
||
2. **多轮工具调用** - "搜索并分析数据"
|
||
3. **无工具调用** - "你好"
|
||
|
||
---
|
||
|
||
## 后续步骤
|
||
|
||
1. **手动测试** - 启动应用测试skill调用流程
|
||
2. **前端适配** - 磁保前端正确处理新的事件类型
|
||
3. **文档更新** - 更新skill系统文档
|
||
|
||
---
|
||
|
||
## 关键文件
|
||
|
||
| 文件 | 改动说明 |
|
||
|------|----------|
|
||
| `crates/zclaw-runtime/src/loop_runner.rs` | 核心循环逻辑重写 |
|
||
| `crates/zclaw-types/src/message.rs` | 已有`tool_result`方法(无需修改) |
|
||
| `crates/zclaw-kernel/src/kernel.rs` | System prompt构建(已有skill注入) |
|
||
|
||
## 测试命令
|
||
|
||
```bash
|
||
# 编译
|
||
cargo build -p zclaw-runtime
|
||
|
||
# 启动开发环境
|
||
pnpm start:dev
|
||
|
||
# 测试对话
|
||
# 发送: "帮我分析一下腾讯的财报"
|
||
# 预期: LLM调用execute_skill工具,返回分析结果
|
||
```
|