Files
zclaw_openfang/docs/knowledge-base/troubleshooting.md
iven 6f72442531 docs(guide): rewrite CLAUDE.md with ZCLAW-first perspective
Major changes:
- Shift from "OpenFang desktop client" to "independent AI Agent desktop app"
- Add decision principle: "Is this useful for ZCLAW? Does it affect ZCLAW?"
- Simplify project structure and tech stack sections
- Replace OpenClaw vs OpenFang comparison with unified backend approach
- Consolidate troubleshooting from scattered sections into organized FAQ
- Update Hands system documentation with 8 capabilities and status
- Stream
2026-03-20 19:30:09 +08:00

824 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 故障排查指南
> 记录开发过程中遇到的问题、根因分析和解决方案。
---
## 1. 连接问题
### 1.1 WebSocket 连接失败
**症状**: `WebSocket connection failed``Unexpected server response: 400`
**排查步骤**:
```bash
# 1. 检查 OpenFang 是否运行
curl http://127.0.0.1:50051/api/health
# 2. 检查端口是否正确
netstat -ano | findstr "50051"
# 3. 验证 Agent ID
curl http://127.0.0.1:50051/api/agents
```
**常见原因**:
| 原因 | 解决方案 |
|------|----------|
| OpenFang 未启动 | `./openfang.exe start` |
| 端口错误 | OpenFang 使用 50051不是 4200 |
| Agent ID 无效 | 使用 `/api/agents` 获取真实 UUID |
### 1.2 端口被占用
**症状**: `Port 1420 is already in use`
**解决方案**:
```bash
# Windows - 查找并终止进程
netstat -ano | findstr "1420"
taskkill /PID <PID> /F
# 或使用 PowerShell
Stop-Process -Id <PID> -Force
```
### 1.3 Vite 代理不工作
**症状**: 前端请求返回 404 或 CORS 错误
**检查清单**:
- [ ] `vite.config.ts` 中配置了 `/api` 代理
- [ ] `ws: true` 已启用WebSocket 需要)
- [ ] `changeOrigin: true` 已设置
- [ ] 重启 Vite 开发服务器
---
## 2. 聊天问题
### 2.1 LLM API Key 未配置
**症状**:
```
Missing API key: No LLM provider configured.
Set an API key (e.g. GROQ_API_KEY) and restart
```
**根本原因**: Agent 使用的 LLM 提供商没有配置 API Key
**解决方案**:
1. 检查 Agent 使用的提供商:
```bash
curl -s http://127.0.0.1:50051/api/status | jq '.agents[] | {name, model_provider}'
```
2. 配置对应的 API Key
```bash
# 编辑 ~/.openfang/.env
echo "ZHIPU_API_KEY=your_key" >> ~/.openfang/.env
echo "BAILIAN_API_KEY=your_key" >> ~/.openfang/.env
echo "GEMINI_API_KEY=your_key" >> ~/.openfang/.env
```
3. 重启 OpenFang
```bash
./openfang.exe restart
```
**快速解决**: 使用已配置的 Agent
| Agent | 提供商 | 状态 |
|-------|--------|------|
| General Assistant | zhipu | 通常已配置 |
### 2.1.1 配置热重载限制(重要)
**症状**: 修改 `config.toml` 后,`/api/config``/api/status` 仍然返回旧配置
**根本原因**: OpenFang 将配置持久化在 SQLite 数据库中,`config.toml` 只在启动时读取
**验证问题**:
```bash
# 检查 config.toml 内容
cat ~/.openfang/config.toml
# 输出: provider = "zhipu"
# 检查 API 返回的配置
curl -s http://127.0.0.1:50051/api/config
# 输出: {"default_model":{"provider":"bailian",...}} # 不一致!
```
**解决方案**:
1. **必须完全重启 OpenFang**(热重载 `/api/config/reload` 不会更新持久化配置)
```bash
# 方法 1: 通过 API 关闭(然后手动重启)
curl -X POST http://127.0.0.1:50051/api/shutdown
# 方法 2: 使用 CLI
./openfang.exe stop
./openfang.exe start
```
2. **验证配置已生效**:
```bash
curl -s http://127.0.0.1:50051/api/status | grep -E "default_provider|default_model"
# 应输出: "default_provider":"zhipu"
```
**配置文件位置**:
| 文件 | 用途 |
|------|------|
| `~/.openfang/config.toml` | 主配置(启动时读取) |
| `~/.openfang/.env` | API Key 环境变量 |
| `~/.openfang/secrets.env` | 敏感信息 |
| `~/.openfang/data/openfang.db` | SQLite 数据库(持久化配置) |
**支持的 Provider**:
| Provider | 环境变量 | 模型示例 |
|----------|----------|----------|
| zhipu | `ZHIPU_API_KEY` | glm-4-flash, GLM-4-Plus |
| bailian | `BAILIAN_API_KEY` | qwen3.5-plus, qwen3-coder-next |
| gemini | `GEMINI_API_KEY` | gemini-2.5-flash |
| deepseek | `DEEPSEEK_API_KEY` | deepseek-chat |
| openai | `OPENAI_API_KEY` | gpt-4, gpt-3.5-turbo |
### 2.2 Agent ID 获取失败导致无法对话
**症状**: Gateway 显示已连接,但发送消息无响应或报错 "No agent available"
**根本原因**: `fetchDefaultAgentId()` 使用错误的 API 端点
**错误代码**:
```typescript
// ❌ 错误 - /api/status 不返回 agents 字段
const status = await this.restGet('/api/status');
if (status?.agents && status.agents.length > 0) { ... }
```
**修复代码**:
```typescript
// ✅ 正确 - 使用 /api/agents 端点
const agents = await this.restGet<Array<{ id: string; name?: string; state?: string }>>('/api/agents');
if (agents && agents.length > 0) {
const runningAgent = agents.find(a => a.state === 'Running');
this.defaultAgentId = (runningAgent || agents[0]).id;
}
```
**历史背景**: 这个问题与 OpenClaw 的握手认证问题类似,但根因不同:
- OpenClaw: 需要 `cli/cli/operator` 身份 + Ed25519 配对
- OpenFang: 不需要认证握手,但需要正确的 Agent UUID
**验证修复**:
```bash
# 确认 /api/agents 返回数据
curl http://127.0.0.1:50051/api/agents
# 应返回: [{ "id": "uuid", "name": "...", "state": "Running" }]
```
### 2.3 流式响应不显示
**症状**: 消息发送后无响应或响应不完整
**排查步骤**:
1. 确认 WebSocket 连接状态:
```typescript
console.log(client.getState()); // 应为 'connected'
```
2. 检查事件处理:
```typescript
// 确保处理了 text_delta 事件
ws.on('message', (data) => {
const event = JSON.parse(data.toString());
if (event.type === 'text_delta') {
console.log('Delta:', event.content);
}
});
```
3. 验证消息格式:
```javascript
// ✅ 正确
{ type: 'message', content: 'Hello', session_id: 'xxx' }
// ❌ 错误
{ type: 'chat', message: { role: 'user', content: 'Hello' } }
```
### 2.3 消息格式错误
**症状**: WebSocket 连接成功,但发送消息后收到错误
**根本原因**: 使用了文档中的格式,而非实际格式
**正确的消息格式**:
```json
{
"type": "message",
"content": "你的消息内容",
"session_id": "唯一会话ID"
}
```
---
## 3. 前端问题
### 3.1 Zustand 状态不更新
**症状**: UI 不反映状态变化
**检查**:
1. 确保使用 selector
```typescript
// ✅ 正确 - 使用 selector
const messages = useChatStore((state) => state.messages);
// ❌ 错误 - 可能导致不必要的重渲染
const store = useChatStore();
const messages = store.messages;
```
2. 检查 immer/persist 配置
### 3.2 切换 Agent 后对话消失
**症状**: 点击其他 Agent 后,之前的对话内容丢失
**根本原因**: `setCurrentAgent` 切换 Agent 时清空了 `messages`,但没有恢复该 Agent 之前的对话
**解决方案**:
修改 `chatStore.ts` 中的 `setCurrentAgent` 函数:
```typescript
setCurrentAgent: (agent) =>
set((state) => {
if (state.currentAgent?.id === agent.id) {
return { currentAgent: agent };
}
// Save current conversation before switching
const conversations = upsertActiveConversation([...state.conversations], state);
// Try to find existing conversation for this agent
const agentConversation = conversations.find(c => c.agentId === agent.id);
if (agentConversation) {
// Restore the agent's previous conversation
return {
conversations,
currentAgent: agent,
messages: [...agentConversation.messages],
sessionKey: agentConversation.sessionKey,
currentConversationId: agentConversation.id,
};
}
// No existing conversation, start fresh
return {
conversations,
currentAgent: agent,
messages: [],
sessionKey: null,
currentConversationId: null,
};
}),
```
修改 `partialize` 配置以保存 `currentAgentId`
```typescript
partialize: (state) => ({
conversations: state.conversations,
currentModel: state.currentModel,
currentAgentId: state.currentAgent?.id, // 添加此行
currentConversationId: state.currentConversationId,
}),
```
添加 `onRehydrateStorage` 钩子恢复消息:
```typescript
onRehydrateStorage: () => (state) => {
// Rehydrate Date objects from JSON strings
if (state?.conversations) {
for (const conv of state.conversations) {
conv.createdAt = new Date(conv.createdAt);
conv.updatedAt = new Date(conv.updatedAt);
for (const msg of conv.messages) {
msg.timestamp = new Date(msg.timestamp);
msg.streaming = false;
}
}
}
// Restore messages from current conversation if exists
if (state?.currentConversationId && state.conversations) {
const currentConv = state.conversations.find(c => c.id === state.currentConversationId);
if (currentConv) {
state.messages = [...currentConv.messages];
state.sessionKey = currentConv.sessionKey;
}
}
},
```
**验证修复**:
1. 与 Agent A 对话
2. 切换到 Agent B
3. 切换回 Agent A → 对话应恢复
**文件**: `desktop/src/store/chatStore.ts`
### 3.3 流式消息累积错误
**症状**: 流式内容显示不正确或重复
**解决方案**:
```typescript
onDelta: (delta: string) => {
set((state) => ({
messages: state.messages.map((m) =>
m.id === assistantId
? { ...m, content: m.content + delta } // 累积内容
: m
),
}));
}
```
---
## 4. Tauri 桌面端问题
### 4.1 Tauri 编译失败
**常见错误**:
- Rust 版本不兼容
- 依赖缺失
- Cargo.toml 配置错误
**解决方案**:
```bash
# 更新 Rust
rustup update
# 清理并重新构建
cd desktop/src-tauri
cargo clean
cargo build
```
### 4.2 Tauri 窗口白屏
**原因**: Vite 开发服务器未启动或连接失败
**解决方案**:
1. 确保 `pnpm dev` 在运行
2. 检查 `tauri.conf.json` 中的 `beforeDevCommand`
3. 检查浏览器控制台错误
### 4.3 Tauri 热重载不工作
**检查**:
- `beforeDevCommand` 配置正确
- 文件监听未超出限制Linux: `fs.inotify.max_user_watches`
---
## 5. 调试技巧
### 5.1 启用详细日志
```typescript
// gateway-client.ts
private log(level: string, message: string, data?: unknown) {
if (this.debug) {
console.log(`[GatewayClient:${level}]`, message, data || '');
}
}
```
### 5.2 WebSocket 抓包
```bash
# 使用 wscat 测试
npm install -g wscat
wscat -c ws://127.0.0.1:50051/api/agents/{agentId}/ws
```
### 5.3 检查 OpenFang 状态
```bash
# 完整状态
curl -s http://127.0.0.1:50051/api/status | jq
# Agent 状态
curl -s http://127.0.0.1:50051/api/agents | jq '.[] | {id, name, state}'
# Hands 状态
curl -s http://127.0.0.1:50051/api/hands | jq '.[] | {id, name, requirements_met}'
```
---
## 6. 错误代码参考
| 错误信息 | 原因 | 解决方案 |
|---------|------|----------|
| `Port 1420 is already in use` | Vite 服务器已运行 | 终止现有进程 |
| `Unexpected server response: 400` | Agent ID 无效 | 使用真实 UUID |
| `Missing API key` | LLM 提供商未配置 | 配置 API Key |
| `Connection refused` | OpenFang 未运行 | 启动服务 |
| `CORS error` | 代理未配置 | 检查 vite.config.ts |
---
## 7. 新用户引导 (Onboarding)
### 7.1 首次使用引导流程
**需求**: 当用户第一次使用系统时引导用户设置默认助手的人格信息about me、you in my eye 等)。
**实现方案**:
1. **检测首次使用** - 使用 `useOnboarding` hook 检查 localStorage:
```typescript
// desktop/src/lib/use-onboarding.ts
const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed';
const USER_PROFILE_KEY = 'zclaw-user-profile';
export function useOnboarding(): OnboardingState {
// 检查 localStorage 是否有完成记录
// 返回 { isNeeded, isLoading, markCompleted, resetOnboarding }
}
```
2. **引导向导** - 使用 `AgentOnboardingWizard` 组件:
```typescript
// App.tsx 中的集成
if (showOnboarding) {
return (
<AgentOnboardingWizard
isOpen={true}
onClose={() => {
markCompleted({ userName: 'User', userRole: 'user' });
setShowOnboarding(false);
}}
onSuccess={(clone) => {
markCompleted({
userName: clone.userName || 'User',
userRole: clone.userRole,
});
setCurrentAgent({
id: clone.id,
name: clone.name,
icon: clone.emoji || '🦞',
// ...
});
setShowOnboarding(false);
}}
/>
);
}
```
3. **数据持久化** - 用户信息存储在 localStorage:
```typescript
interface UserProfile {
userName: string;
userRole?: string;
completedAt: string;
}
```
**文件位置**:
| 文件 | 用途 |
|------|------|
| `desktop/src/lib/use-onboarding.ts` | 引导状态管理 hook |
| `desktop/src/components/AgentOnboardingWizard.tsx` | 5 步引导向导组件 |
| `desktop/src/App.tsx` | 引导流程集成 |
**引导步骤**:
1. 认识用户 - 收集用户名称和角色
2. Agent 身份 - 设置助手名称、昵称、emoji
3. 人格风格 - 选择沟通风格
4. 使用场景 - 选择应用场景
5. 工作环境 - 配置工作目录
### 7.2 Onboarding 创建 Agent 失败
**症状**: 首次使用引导完成后,点击"完成"按钮报错Agent 创建失败
**根本原因**: Onboarding 应该**更新现有的默认 Agent**,而不是创建新的 Agent
**错误代码**:
```typescript
// ❌ 错误 - 总是尝试创建新 Agent
const clone = await createClone(createOptions);
```
**修复代码**:
```typescript
// ✅ 正确 - 如果存在现有 Agent 则更新
let clone: Clone | undefined;
if (clones && clones.length > 0) {
// 更新现有的默认 Agent
clone = await updateClone(clones[0].id, personalityUpdates);
} else {
// 没有现有 Agent 才创建新的
clone = await createClone(createOptions);
}
```
**文件**: `desktop/src/components/AgentOnboardingWizard.tsx`
**验证修复**:
1. 清除 localStorage 中的 onboarding 标记
2. 重新启动应用
3. 完成引导流程 → 应该成功更新默认 Agent
### 3.4 刷新页面后对话内容丢失
**症状**: 切换 Tab 时对话正常保留,但按 F5 刷新页面后对话内容消失
**根本原因**: `onComplete` 回调中没有将当前对话保存到 `conversations` 数组,导致 `persist` 中间件无法持久化
**问题分析**:
Zustand 的 `persist` 中间件只保存 `partialize` 中指定的字段:
```typescript
partialize: (state) => ({
conversations: state.conversations, // ← 从这里恢复
currentModel: state.currentModel,
currentAgentId: state.currentAgent?.id,
currentConversationId: state.currentConversationId,
}),
```
但 `messages` 数组只在内存中,刷新后丢失。恢复逻辑依赖 `conversations` 数组:
```typescript
onRehydrateStorage: () => (state) => {
if (state?.currentConversationId && state.conversations) {
const currentConv = state.conversations.find(c => c.id === state.currentConversationId);
if (currentConv) {
state.messages = [...currentConv.messages]; // ← 从 conversations 恢复
}
}
},
```
**问题**: `onComplete` 回调中只更新了 `messages`,没有调用 `upsertActiveConversation` 保存到 `conversations`
**修复代码**:
```typescript
onComplete: () => {
const state = get();
// Save conversation to persist across refresh
const conversations = upsertActiveConversation([...state.conversations], state);
const currentConvId = state.currentConversationId || conversations[0]?.id;
set({
isStreaming: false,
conversations, // ← 保存到 conversations 数组
currentConversationId: currentConvId, // ← 确保 ID 被设置
messages: state.messages.map((m) =>
m.id === assistantId ? { ...m, streaming: false, runId } : m
),
});
// ... rest of the callback
},
```
**文件**: `desktop/src/store/chatStore.ts`
**验证修复**:
1. 发送消息并获得 AI 回复
2. 按 F5 刷新页面
3. 对话内容应该保留
### 3.5 ChatArea 输入框布局错乱
**症状**: 对话过程中输入框被移动到页面顶部,而不是固定在底部
**根本原因**: `ChatArea` 组件返回的是 React Fragment (`<>...</>`),没有包裹在 flex 容器中
**问题代码**:
```typescript
// ❌ 错误 - Fragment 没有 flex 布局
return (
<>
<div className="h-14 ...">Header</div>
<div className="flex-1 ...">Messages</div>
<div className="border-t ...">Input</div>
</>
);
```
**修复代码**:
```typescript
// ✅ 正确 - 使用 flex 容器
return (
<div className="flex flex-col h-full">
<div className="h-14 flex-shrink-0 ...">Header</div>
<div className="flex-1 overflow-y-auto ...">Messages</div>
<div className="flex-shrink-0 ...">Input</div>
</div>
);
```
**文件**: `desktop/src/components/ChatArea.tsx`
**布局原理**:
```
┌─────────────────────────────────────┐
│ Header (h-14, flex-shrink-0) │ ← 固定高度
├─────────────────────────────────────┤
│ │
│ Messages (flex-1, overflow-y-auto) │ ← 占据剩余空间,可滚动
│ │
├─────────────────────────────────────┤
│ Input (flex-shrink-0) │ ← 固定在底部
└─────────────────────────────────────┘
```
---
## 7. 记忆系统问题
### 7.1 Memory Tab 为空,多轮对话后无记忆
**症状**: 经过多次对话后,右侧面板的"记忆"Tab 内容为空
**根本原因**: 多个配置问题导致记忆未被提取
**问题分析**:
1. **LLM 提取默认禁用**: `useLLM: false` 导致只使用规则提取
2. **提取阈值过高**: `minMessagesForExtraction: 4` 短对话不会触发
3. **agentId 不一致**: `MemoryPanel` 用 `'zclaw-main'``MemoryGraph` 用 `'default'`
4. **Gateway 端点不存在**: `GatewayLLMAdapter` 调用 `/api/llm/complete`OpenFang 无此端点
**修复方案**:
```typescript
// 1. 启用 LLM 提取 (memory-extractor.ts)
export const DEFAULT_EXTRACTION_CONFIG: ExtractionConfig = {
useLLM: true, // 从 false 改为 true
minMessagesForExtraction: 2, // 从 4 降低到 2
// ...
};
// 2. 统一 agentId fallback (MemoryGraph.tsx)
const agentId = currentAgent?.id || 'zclaw-main'; // 从 'default' 改为 'zclaw-main'
// 3. 修复 Gateway 适配器端点 (llm-service.ts)
// 使用 OpenFang 的 /api/agents/{id}/message 端点
const response = await fetch(`/api/agents/${agentId}/message`, {
method: 'POST',
body: JSON.stringify({ message: fullPrompt, ... }),
});
```
**文件**:
- `desktop/src/lib/memory-extractor.ts`
- `desktop/src/lib/llm-service.ts`
- `desktop/src/components/MemoryGraph.tsx`
**验证修复**:
1. 打开浏览器控制台
2. 进行至少 2 轮对话
3. 查看日志: `[MemoryExtractor] Using LLM-powered semantic extraction`
4. 检查 Memory Tab 是否显示提取的记忆
### 7.2 Memory Graph UI 与系统风格不一致
**症状**: 记忆图谱使用深色主题,与系统浅色主题不协调
**根本原因**: `MemoryGraph.tsx` 硬编码深色背景 `#1a1a2e`
**修复方案**:
```typescript
// Canvas 背景 - 支持亮/暗双主题
ctx.fillStyle = '#f9fafb'; // gray-50 (浅色)
// 工具栏 - 添加 dark: 变体
<div className="bg-gray-100 dark:bg-gray-800/50">
// 图谱画布 - 添加 dark: 变体
<div className="bg-gray-50 dark:bg-gray-900">
```
**文件**: `desktop/src/components/MemoryGraph.tsx`
**设计规范**:
- 使用 Tailwind 的 `dark:` 变体支持双主题
- 强调色使用 `orange-500` 而非 `blue-600`
- 文字颜色使用 `gray-700 dark:text-gray-300`
---
## 8. 端口配置问题
### 8.1 OpenFang 端口不匹配导致 Network Error
**症状**: 创建 Agent 或其他 API 操作时报错 `Failed to create agent: Network Error`,控制台显示 `POST http://localhost:1420/api/agents net::ERR_CONNECTION_REFUSED`
**根本原因**: `runtime-manifest.json` 声明端口 4200但实际 OpenFang 运行在 **50051** 端口
**正确配置**:
| 配置位置 | 正确端口 |
|---------|----------|
| `runtime-manifest.json` | 4200 (声明,但实际不使用) |
| **实际运行端口** | **50051** |
| `vite.config.ts` 代理 | **50051** |
| `gateway-client.ts` | **50051** |
**解决方案**:
1. 更新 `vite.config.ts`:
```typescript
proxy: {
'/api': {
target: 'http://127.0.0.1:50051', // 使用实际运行端口
// ...
},
}
```
2. 更新 `gateway-client.ts`:
```typescript
export const DEFAULT_GATEWAY_URL = `${DEFAULT_WS_PROTOCOL}127.0.0.1:50051/ws`;
export const FALLBACK_GATEWAY_URLS = [
DEFAULT_GATEWAY_URL,
`${DEFAULT_WS_PROTOCOL}127.0.0.1:4200/ws`, // 保留作为备选
];
```
**验证端口**:
```bash
# 检查实际运行的端口
netstat -ano | findstr "50051"
netstat -ano | findstr "4200"
```
**注意**: `runtime-manifest.json` 中的端口声明与实际运行端口不一致,以实际监听端口为准。
**涉及文件**:
- `desktop/vite.config.ts` - Vite 代理配置
- `desktop/src/lib/gateway-client.ts` - WebSocket 客户端默认 URL
- `desktop/src/components/Settings/General.tsx` - 设置页面显示地址
- `desktop/src/components/Settings/ModelsAPI.tsx` - 模型 API 重连逻辑
**排查流程**:
1. 先用 `netstat` 确认实际监听端口
2. 对比 `runtime-manifest.json` 声明端口与实际端口
3. 确保所有前端配置使用**实际监听端口**
4. 重启 Vite 开发服务器
**验证修复**:
```bash
# 检查端口监听
netstat -ano | findstr "50051"
# 应显示 LISTENING
# 重启 Vite 后测试
curl http://localhost:1420/api/agents
# 应返回 JSON 数组而非 404/502
```
**文件**: 多个配置文件
---
## 9. 相关文档
- [OpenFang 配置指南](./openfang-configuration.md) - 配置文件位置、格式和最佳实践
- [Agent 和 LLM 提供商配置](./agent-provider-config.md) - Agent 管理和 Provider 配置
- [OpenFang WebSocket 协议](./openfang-websocket-protocol.md) - WebSocket 通信协议
---
## 更新历史
| 日期 | 变更 |
|------|------|
| 2026-03-20 | 添加端口配置问题runtime-manifest.json 声明 4200 但实际运行 50051 |
| 2026-03-18 | 添加记忆提取和图谱 UI 问题 |
| 2026-03-18 | 添加刷新后对话丢失问题和 ChatArea 布局问题 |
| 2026-03-17 | 添加首次使用引导流程 |
| 2026-03-17 | 添加配置热重载限制问题 |
| 2026-03-14 | 初始版本 |