首页布局优化前
This commit is contained in:
@@ -97,7 +97,92 @@ echo "GEMINI_API_KEY=your_key" >> ~/.openfang/.env
|
||||
|-------|--------|------|
|
||||
| General Assistant | zhipu | 通常已配置 |
|
||||
|
||||
### 2.2 流式响应不显示
|
||||
### 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 流式响应不显示
|
||||
|
||||
**症状**: 消息发送后无响应或响应不完整
|
||||
|
||||
@@ -165,7 +250,94 @@ const messages = store.messages;
|
||||
|
||||
2. 检查 immer/persist 配置
|
||||
|
||||
### 3.2 流式消息累积错误
|
||||
### 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 流式消息累积错误
|
||||
|
||||
**症状**: 流式内容显示不正确或重复
|
||||
|
||||
@@ -269,8 +441,125 @@ curl -s http://127.0.0.1:50051/api/hands | jq '.[] | {id, name, requirements_met
|
||||
|
||||
---
|
||||
|
||||
## 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 | 初始版本 |
|
||||
|
||||
Reference in New Issue
Block a user