Files
zclaw_openfang/docs/knowledge-base/troubleshooting.md
2026-03-17 23:26:16 +08:00

566 lines
14 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
---
## 8. 相关文档
- [OpenFang 配置指南](./openfang-configuration.md) - 配置文件位置、格式和最佳实践
- [Agent 和 LLM 提供商配置](./agent-provider-config.md) - Agent 管理和 Provider 配置
- [OpenFang WebSocket 协议](./openfang-websocket-protocol.md) - WebSocket 通信协议
---
## 更新历史
| 日期 | 变更 |
|------|------|
| 2026-03-17 | 添加首次使用引导流程 |
| 2026-03-17 | 添加配置热重载限制问题 |
| 2026-03-14 | 初始版本 |