diff --git a/cmd/picoclaw/internal/agent/helpers.go b/cmd/picoclaw/internal/agent/helpers.go index 23227d56a..0afa50293 100644 --- a/cmd/picoclaw/internal/agent/helpers.go +++ b/cmd/picoclaw/internal/agent/helpers.go @@ -82,9 +82,14 @@ func agentCmd(message, sessionKey, model string, debug bool) error { func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) { prompt := fmt.Sprintf("%s You: ", internal.Logo) + historyFile := filepath.Join(internal.GetPicoclawHome(), ".picoclaw_history") + if err := os.MkdirAll(internal.GetPicoclawHome(), 0700); err != nil { + fmt.Printf("Error creating picoclaw home directory: %v\n", err) + } + rl, err := readline.NewEx(&readline.Config{ Prompt: prompt, - HistoryFile: filepath.Join(os.TempDir(), ".picoclaw_history"), + HistoryFile: historyFile, HistoryLimit: 100, InterruptPrompt: "^C", EOFPrompt: "exit", diff --git a/docs/zh/config-versioning.md b/docs/zh/config-versioning.md new file mode 100644 index 000000000..77a6ccec0 --- /dev/null +++ b/docs/zh/config-versioning.md @@ -0,0 +1,228 @@ +# 配置文件版本控制指南 + +## 概述 + +PicoClaw 使用模式版本控制系统来管理 `config.json`,确保在配置格式演进时能够顺利升级。 + +## 版本历史 + +### 版本 1 +- **引入时间**:初始版本,支持 version 字段 +- **变更**:向 Config 结构体添加了 `version` 字段 +- **迁移**:现有配置无需结构性变更 + +### 版本 2 +- **引入时间**:模型启用/禁用支持和渠道配置统一 +- **变更**: + - 向 `ModelConfig` 添加了 `enabled` 字段 — 允许禁用单个模型条目而无需删除 + - 在 V1→V2 迁移期间,`enabled` 会自动推断:带有 API 密钥或保留名称 `local-model` 的模型会被启用;其他模型默认禁用 + - 迁移了旧版渠道字段:Discord `mention_only` → `group_trigger.mention_only`,OneBot `group_trigger_prefix` → `group_trigger.prefixes` + - V0 配置现在直接迁移到 CurrentVersion(V2),而不是经过 V1 + - `makeBackup()` 现在使用仅日期后缀(如 `config.json.20260330.bak`),同时也会备份 `.security.yml` + +## 工作原理 + +### 自动迁移 +加载配置文件时: +1. 系统首先从 JSON 中读取 `version` 字段 +2. 根据检测到的版本,加载相应的配置结构体(`configV0`、`configV1` 等) +3. 如果加载的版本低于最新版本,则增量应用迁移 +4. 保存前,系统会自动创建 `config.json` 和 `.security.yml` 的带日期戳备份 +5. 版本号会自动更新 +6. 迁移后的配置会自动保存到磁盘 + +### 版本字段 +`config.json` 中的 `version` 字段表示模式版本: +- `0` 或缺失:旧版配置(无 version 字段) +- `1`:上一版本(加载时会自动迁移到 V2) +- `2`:当前版本 + +```json +{ + "version": 2, + "agents": {...}, + ... +} +``` + +## 添加新的迁移 + +对配置模式进行破坏性变更时: + +### 步骤 1:定义新版本结构体 + +如果结构发生重大变更,创建新版本的结构体: + +```go +// ConfigV2 代表版本 2 的配置结构 +type ConfigV2 struct { + Version int `json:"version"` + Agents AgentsConfig `json:"agents"` + // ... 其他字段,新结构 +} +``` + +### 步骤 2:更新当前配置版本 + +```go +const CurrentVersion = 2 // 递增此值 +``` + +### 步骤 3:添加加载器函数 + +```go +// loadConfigV3 加载版本 3 的配置 +func loadConfigV3(data []byte) (*Config, error) { + cfg := DefaultConfig() + + // 解析为 ConfigV3 结构体 + var v3 ConfigV3 + if err := json.Unmarshal(data, &v3); err != nil { + return nil, err + } + + // 转换为当前 Config + cfg.Version = v3.Version + cfg.Agents = v3.Agents + // ... 映射其他字段 + + return cfg, nil +} +``` + +### 步骤 4:添加迁移逻辑 + +```go +func (c *configV2) Migrate() (*Config, error) { + // 在此处应用 V2→V3 的结构性变更 + migrated := &c.Config + migrated.Version = 3 + // 应用结构性变更 + return migrated, nil +} +``` + +### 步骤 5:更新 LoadConfig 开关 + +```go +func LoadConfig(path string) (*Config, error) { + // ... 读取文件 ... + + switch versionInfo.Version { + case 0: + cfg, err = loadConfigV0(data) + case 1: + cfg, err = loadConfigV1(data) + case 2: + cfg, err = loadConfig(data) + case 3: + cfg, err = loadConfigV3(data) + default: + return nil, fmt.Errorf("unsupported config version: %d", versionInfo.Version) + } + + // ... 迁移和验证 ... +} +``` + +### 步骤 6:测试迁移 + +在 `config_migration_test.go` 中创建测试: + +```go +func TestMigrateV2ToV3(t *testing.T) { + // 创建版本 2 的配置 + v2Config := Config{ + Version: 2, + // ... 设置测试数据 + } + + // 应用迁移 + migrated, err := v2Config.Migrate() + if err != nil { + t.Fatalf("Migration failed: %v", err) + } + + // 验证版本已更新 + if migrated.Version != 3 { + t.Errorf("Expected version 3, got %d", migrated.Version) + } + + // 验证数据正确保留/转换 + // ... +} +``` + +## 迁移最佳实践 + +1. **版本特定结构体**:为每个有结构性变更的版本定义单独的结构体 +2. **向后兼容**:确保旧配置仍能用其特定结构体加载 +3. **无数据丢失**:迁移应保留所有用户设置 +4. **幂等性**:多次运行相同迁移应该是安全的 +5. **自动保存**:迁移后的配置会自动保存以更新用户的文件 +6. **自动备份**:保存前,系统会创建 `config.json` 和 `.security.yml` 的带日期戳备份 +7. **全面测试**:使用真实用户配置文件进行测试 +8. **更新默认值**:使 `defaults.go` 与最新模式保持同步 + +## 迁移示例 + +### 场景:添加带默认值的新字段 + +旧配置(版本 2): +```json +{ + "version": 2, + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4" + } + ] +} +``` + +迁移到版本 3: +```go +func (c *configV2) Migrate() (*Config, error) { + migrated := &c.Config + migrated.Version = 3 + + // 如果未设置,添加带默认值的新字段 + // ... + + return migrated, nil +} +``` + +新配置(版本 3): +```json +{ + "version": 3, + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4", + "new_option": true + } + ] +} +``` + +## 故障排除 + +### 配置未升级 +- 检查 `CurrentVersion` 是否已递增 +- 验证迁移逻辑是否处理目标版本 +- 确保 `Migrate()` 在 `LoadConfig()` 中被调用 + +### 迁移错误 +- 检查错误消息以获取特定迁移失败信息 +- 审查迁移逻辑的边缘情况 +- 确保所有必需字段正确初始化 +- 验证源版本的加载器函数 + +### 迁移后数据丢失 +- 确保迁移期间所有字段都被复制 +- 检查迁移不会不必要地用默认值覆盖值 +- 审查加载器函数中的转换逻辑 +- 检查自动备份文件(如 `config.json.20260330.bak`)以恢复原始数据 diff --git a/docs/zh/cron.md b/docs/zh/cron.md new file mode 100644 index 000000000..da4cb6ad3 --- /dev/null +++ b/docs/zh/cron.md @@ -0,0 +1,125 @@ +# 定时任务与 Cron 作业 + +> 返回 [README](../README.md) + +PicoClaw 将定时作业存储在当前工作区中,可以将它们作为提醒、完全自主的 Agent 回合或 shell 命令来运行。 + +## 调度类型 + +PicoClaw 目前在 cron 工具中使用三种调度形式: + +- `at_seconds`:一次性任务,相对于当前时间。运行后,作业会从存储中删除。 +- `every_seconds`:重复间隔,以秒为单位。 +- `cron_expr`:重复的 cron 表达式,如 `0 9 * * *`。 + +CLI 命令 `picoclaw cron add` 目前仅支持重复作业: + +- `--every ` +- `--cron ''` + +目前没有用于一次性 `at` 作业的 CLI 参数。 + +示例: + +```bash +picoclaw cron add --name "Daily summary" --message "Summarize today's logs" --cron "0 18 * * *" +picoclaw cron add --name "Ping" --message "heartbeat" --every 300 --deliver +``` + +## 执行模式 + +作业存储时带有消息负载,可以以三种稳定的面向用户的模式执行: + +### `deliver: false` + +这是 cron 工具的默认值。 + +当作业触发时,PicoClaw 会将保存的消息作为新的 Agent 回合发送回 Agent 循环。对于可能需要推理、工具或生成回复的定时工作使用此模式。 + +### `deliver: true` + +当作业触发时,PicoClaw 会将保存的消息直接发布到目标渠道和接收者,无需 Agent 处理。 + +CLI `picoclaw cron add --deliver` 参数使用此模式。 + +### `command` + +当 cron-tool 作业包含 `command` 时,PicoClaw 会通过 `exec` 工具运行该 shell 命令,并将命令输出发布回渠道。 + +对于命令作业,创建时会将 `deliver` 强制设为 `false`。保存的 `message` 仅作为描述性文本;定时动作是 shell 命令。 + +当前的 CLI `picoclaw cron add` 命令不暴露 `command` 参数。 + +## 配置与安全门控 + +### `tools.cron` + +`tools.cron.enabled` 控制面向 Agent 的 `cron` 工具是否注册。默认值:`true`。 + +如果禁用 `tools.cron`,用户将无法再通过 Agent 工具创建或管理作业。网关仍会启动 `CronService`,但不会安装作业执行回调。因此,定时作业不会实际运行;一次性作业可能会被删除,重复作业可能会被重新调度而不会执行其负载。CLI 仍使用相同的作业存储。 + +`tools.cron.exec_timeout_minutes` 设置定时命令执行的超时时间。默认值:`5`。设为 `0` 表示无超时。 + +### `tools.exec` + +定时命令作业依赖于 `tools.exec.enabled`。默认值:`true`。 + +如果 `tools.exec.enabled` 为 `false`: + +- 新的命令作业会被 cron 工具拒绝 +- 现有命令作业在触发时会发布 `command execution is disabled` 错误 + +`tools.exec.allow_remote` 仍由 exec 工具强制执行,但 cron 命令调度在创建作业时已经需要内部渠道。实际上,提醒作业可以从远程渠道调度,而定时命令作业仅限于内部渠道。 + +### `allow_command` + +`tools.cron.allow_command` 默认为 `true`。 + +这不是硬禁用开关。如果将 `allow_command` 设为 `false`,PicoClaw 仍会在调用者明确传递 `command_confirm: true` 时允许命令作业。 + +命令作业还需要内部渠道。非命令提醒没有此限制。 + +示例: + +```json +{ + "tools": { + "cron": { + "enabled": true, + "exec_timeout_minutes": 5, + "allow_command": true + }, + "exec": { + "enabled": true + } + } +} +``` + +## 持久化与位置 + +Cron 作业存储在: + +```text +/cron/jobs.json +``` + +默认工作区是: + +```text +~/.picoclaw/workspace +``` + +如果设置了 `PICOCLAW_HOME`,默认工作区变为: + +```text +$PICOCLAW_HOME/workspace +``` + +网关和 `picoclaw cron` CLI 子命令使用相同的 `cron/jobs.json` 文件。 + +注意: + +- 一次性 `at_seconds` 作业运行后会删除 +- 重复作业保留在存储中直到被删除 +- 禁用的作业保留在存储中,仍会显示在 `picoclaw cron list` 中 diff --git a/docs/zh/security_configuration.md b/docs/zh/security_configuration.md new file mode 100644 index 000000000..fb7307d36 --- /dev/null +++ b/docs/zh/security_configuration.md @@ -0,0 +1,578 @@ +# 安全配置 + +## 概述 + +PicoClaw 支持通过将敏感数据(API 密钥、令牌、密钥、密码)与主配置分离存储在 `.security.yml` 文件中来提高安全性: + +1. **关注点分离**:配置设置和密钥在不同的文件中 +2. **更易于共享**:主配置可以共享而不会暴露敏感数据 +3. **更好的版本控制**:`.security.yml` 应添加到 `.gitignore` +4. **灵活的部署**:不同环境可以使用不同的安全文件 + +## 文件结构 + +``` +~/.picoclaw/ +├── config.json # 主配置(可安全共享) +└── .security.yml # 安全数据(永不共享) +``` + +## 工作原理 + +安全配置通过**直接字段映射**工作,而不是通过 `ref:` 字符串引用。系统自动从 `.security.yml` 加载值并将其应用到 `config.json` 中的相应字段。 + +### 关键点: + +- `.security.yml` 中的值会自动映射到配置中的相应字段 +- 映射基于字段名称和结构,而不是引用字符串 +- 如果 `.security.yml` 中存在某个值,它**会覆盖** `config.json` 中的值 +- 您可以完全从 `config.json` 中省略敏感字段(推荐) + +## 安全配置结构 + +### 完整示例:.security.yml + +```yaml +# 模型 API 密钥 +# 所有模型必须使用 `api_keys`(复数)数组格式 +# 即使是单个密钥也必须作为包含一个元素的数组提供 +model_list: + gpt-5.4: + api_keys: + - "sk-proj-your-actual-openai-key-1" + - "sk-proj-your-actual-openai-key-2" # 可选:用于故障转移的多个密钥 + claude-sonnet-4.6: + api_keys: + - "sk-ant-your-actual-anthropic-key" # 数组格式中的单个密钥 + +# 渠道令牌 +channels: + telegram: + token: "your-telegram-bot-token" + feishu: + app_secret: "your-feishu-app-secret" + encrypt_key: "your-feishu-encrypt-key" + verification_token: "your-feishu-verification-token" + discord: + token: "your-discord-bot-token" + weixin: + token: "your-weixin-token" + qq: + app_secret: "your-qq-app-secret" + dingtalk: + client_secret: "your-dingtalk-client-secret" + slack: + bot_token: "your-slack-bot-token" + app_token: "your-slack-app-token" + matrix: + access_token: "your-matrix-access-token" + line: + channel_secret: "your-line-channel-secret" + channel_access_token: "your-line-channel-access-token" + onebot: + access_token: "your-onebot-access-token" + wecom: + token: "your-wecom-token" + encoding_aes_key: "your-wecom-encoding-aes-key" + wecom_app: + corp_secret: "your-wecom-app-corp-secret" + token: "your-wecom-app-token" + encoding_aes_key: "your-wecom-app-encoding-aes-key" + wecom_aibot: + secret: "your-wecom-aibot-secret" + token: "your-wecom-aibot-token" + encoding_aes_key: "your-wecom-aibot-encoding-aes-key" + pico: + token: "your-pico-token" + irc: + password: "your-irc-password" + nickserv_password: "your-irc-nickserv-password" + sasl_password: "your-irc-sasl-password" + +# Web 工具 API 密钥 +web: + brave: + api_keys: + - "BSAyour-brave-api-key-1" + - "BSAyour-brave-api-key-2" # 可选:用于故障转移的多个密钥 + tavily: + api_keys: + - "tvly-your-tavily-api-key" # 数组格式中的单个密钥 + perplexity: + api_keys: + - "pplx-your-perplexity-api-key" # 数组格式中的单个密钥 + glm_search: + api_key: "your-glm-search-api-key" # GLMSearch 使用单个密钥格式(不是数组) + baidu_search: + api_key: "your-baidu-search-api-key" + +# 技能注册表令牌 +skills: + github: + token: "your-github-token" + clawhub: + auth_token: "your-clawhub-auth-token" +``` + +## 使用方法 + +### 步骤 1:创建 .security.yml + +创建或复制安全文件: +```bash +cp security.example.yml ~/.picoclaw/.security.yml +``` + +### 步骤 2:填写实际值 + +编辑 `~/.picoclaw/.security.yml`,用您实际的 API 密钥和令牌替换占位符值。 + +### 步骤 3:设置正确的权限 + +```bash +chmod 600 ~/.picoclaw/.security.yml +``` + +### 步骤 4:简化 config.json(推荐) + +现在您可以从 `config.json` 中删除敏感字段,因为它们是从 `.security.yml` 加载的: + +**之前:** +```json +{ + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4", + "api_base": "https://api.openai.com/v1", + "api_key": "sk-your-actual-api-key-here" + } + ], + "channels": { + "telegram": { + "enabled": true, + "token": "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" + } + } +} +``` + +**之后:** +```json +{ + "model_list": [ + { + "model_name": "gpt-5.4", + "model": "openai/gpt-5.4", + "api_base": "https://api.openai.com/v1" + // api_key 现在从 .security.yml 加载 + } + ], + "channels": { + "telegram": { + "enabled": true + // token 现在从 .security.yml 加载 + } + } +} +``` + +### 步骤 5:验证 + +重启 PicoClaw 并验证它正确加载: +```bash +picoclaw --version +``` + +## 字段映射规则 + +### 模型 + +**在 .security.yml 中:** +```yaml +model_list: + : + api_keys: + - "key-1" + - "key-2" +``` + +**映射:** +- 字段 `api_keys`(数组)映射到模型的 API 密钥 +- `` 必须与 `config.json` 中的 `model_name` 字段匹配 +- 支持索引名称(如 "gpt-5.4:0")— 系统也会尝试基础名称("gpt-5.4") + +### 渠道 + +每个渠道直接映射其字段: + +**在 .security.yml 中:** +```yaml +channels: + telegram: + token: "value" + feishu: + app_secret: "value" + encrypt_key: "value" + verification_token: "value" + discord: + token: "value" +``` + +**映射:** +- `channels.telegram.token` → `config.channels.telegram.token` +- `channels.feishu.app_secret` → `config.channels.feishu.app_secret` +- 等等。 + +### Web 工具 + +**Brave、Tavily、Perplexity:** +```yaml +web: + brave: + api_keys: + - "key-1" + - "key-2" +``` +- 使用 `api_keys`(复数)数组格式 + +**GLMSearch:** +```yaml +web: + glm_search: + api_key: "single-key-here" +``` +- 使用 `api_key`(单数)单个字符串格式 + +**BaiduSearch:** +```yaml +web: + baidu_search: + api_key: "your-key" +``` +- 使用 `api_key`(单数)单个字符串格式 + +### 技能 + +**在 .security.yml 中:** +```yaml +skills: + github: + token: "value" + clawhub: + auth_token: "value" +``` + +## API 密钥格式 + +### 模型 - 单个密钥 + +使用包含一个元素的数组格式: +```yaml +model_list: + gpt-5.4: + api_keys: + - "sk-your-key" +``` + +### 模型 - 多个密钥(负载均衡与故障转移) + +使用包含多个元素的数组格式: +```yaml +model_list: + gpt-5.4: + api_keys: + - "sk-your-key-1" + - "sk-your-key-2" + - "sk-your-key-3" +``` + +**好处:** +- **负载均衡**:请求分布在多个密钥之间 +- **故障转移**:如果一个密钥失败,自动切换到另一个密钥 +- **速率限制管理**:在多个密钥之间分配使用 +- **高可用性**:减少 API 提供商问题期间的停机时间 + +### Web 工具(Brave/Tavily/Perplexity)- 单个密钥 + +```yaml +web: + brave: + api_keys: + - "BSA-your-key" +``` + +### Web 工具(Brave/Tavily/Perplexity)- 多个密钥 + +```yaml +web: + brave: + api_keys: + - "BSA-key-1" + - "BSA-key-2" +``` + +### Web 工具(GLMSearch/BaiduSearch)- 仅限单个密钥 + +```yaml +web: + glm_search: + api_key: "your-glm-key" # 单个字符串(不是数组) + baidu_search: + api_key: "your-baidu-key" # 单个字符串(不是数组) +``` + +## 模型名称匹配 + +系统支持 `.security.yml` 中智能模型名称匹配: + +### 示例 1:精确匹配 + +**config.json:** +```json +{ + "model_name": "gpt-5.4:0" +} +``` + +**.security.yml(带索引的精确匹配):** +```yaml +model_list: + gpt-5.4:0: + api_keys: ["key-1"] +``` + +### 示例 2:基础名称匹配 + +**config.json:** +```json +{ + "model_name": "gpt-5.4:0" +} +``` + +**.security.yml(不带索引的基础名称):** +```yaml +model_list: + gpt-5.4: + api_keys: ["key-1", "key-2"] +``` + +两种方法都有效。基础名称匹配允许您在配置使用索引模型名称进行负载均衡时在 `.security.yml` 中使用更简单的密钥。 + +## 向后兼容 + +系统保持完全向后兼容: + +1. **直接值**:您仍可以在 `config.json` 中使用直接值(不推荐用于生产环境) +2. **混合使用**:您可以在 `.security.yml` 和 `config.json` 中都有某些字段 +3. **可选安全文件**:如果 `.security.yml` 不存在,系统将仅使用 `config.json` 中的值 +4. **覆盖行为**:如果两个文件都存在某字段,`.security.yml` 的值优先 + +## 环境变量 + +您可以使用环境变量覆盖任何安全值: + +**对于模型:** +```bash +export PICOCLAW_CHANNELS_TELEGRAM_TOKEN="token-from-env" +``` + +**对于渠道:** +```bash +export PICOCLAW_CHANNELS_TELEGRAM_TOKEN="token-from-env" +export PICOCLAW_CHANNELS_FEISHU_APP_SECRET="secret-from-env" +``` + +**对于 Web 工具:** +```bash +export PICOCLAW_TOOLS_WEB_BRAVE_API_KEY="key-from-env" +export PICOCLAW_TOOLS_WEB_BAIDU_API_KEY="baidu-key-from-env" +``` + +环境变量具有最高优先级,会覆盖 `config.json` 和 `.security.yml` 的值。 + +格式为:`PICOCLAW_
__`,用下划线分隔路径段并转换为大写。 + +## 安全最佳实践 + +1. **永不提交 `.security.yml`** 到版本控制 +2. **添加到 .gitignore**:确保 `.security.yml` 在您的 `.gitignore` 文件中 +3. **设置文件权限**:`chmod 600 ~/.picoclaw/.security.yml` +4. **不同环境使用不同密钥**(开发、暂存、生产) +5. **定期轮换密钥**并更新 `.security.yml` +6. **安全备份**:加密包含 `.security.yml` 的备份。请注意,配置迁移会自动创建带日期戳的备份(如 `config.json.20260330.bak` 和 `.security.yml.20260330.bak`) +7. **审查访问权限**:确保只有授权用户能读取该文件 + +## API + +### loadSecurityConfig + +```go +func loadSecurityConfig(securityPath string) (*SecurityConfig, error) +``` + +从 `.security.yml` 加载安全配置。如果文件不存在,返回空的 `SecurityConfig`。 + +### saveSecurityConfig + +```go +func saveSecurityConfig(securityPath string, sec *SecurityConfig) error +``` + +以 `0o600` 权限将安全配置保存到 `.security.yml`。 + +### applySecurityConfig + +```go +func applySecurityConfig(cfg *Config, sec *SecurityConfig) error +``` + +通过将值从 `.security.yml` 复制到配置的相应字段来应用安全配置。 + +### securityPath + +```go +func securityPath(configPath string) string +``` + +返回配置文件中 `.security.yml` 的相对路径。 + +## 测试 + +运行安全配置测试: + +```bash +go test ./pkg/config -run TestSecurityConfig +``` + +## 故障排除 + +### 错误:"failed to load security config" + +- 验证 `.security.yml` 存在于 `config.json` 的同一目录中 +- 检查 YAML 语法是否有效(使用 YAML 验证器) +- 确保文件权限允许读取 + +### 错误:"model security entry not found" + +- 确保 `config.json` 中的模型名称与 `.security.yml` 中的完全匹配 +- 检查 `.security.yml` 中存在 `model_list` 部分 +- 对于带索引名称的模型(如 "gpt-5.4:0"),确保使用确切的名称或不带索引的基础名称 +- 验证 YAML 结构正确(正确的缩进) + +### 多个 API 密钥不工作 + +- 确保对模型和 Web 工具使用 `api_keys`(复数)(GLMSearch/BaiduSearch 除外) +- 检查数组格式在 YAML 中正确(破折号的正确缩进) +- 记住:模型、Brave、Tavily、Perplexity 必须使用 `api_keys`(数组格式) +- GLMSearch 和 BaiduSearch 必须使用 `api_key`(单个字符串格式) + +### 负载均衡/故障转移问题 + +- 验证 `api_keys` 数组中的所有 API 密钥都有效 +- 检查所有密钥具有相同的速率限制和权限 +- 监控日志以查看正在使用哪些密钥以及哪些失败 +- 确保 `api_keys` 数组在 YAML 中格式正确 + +### 密钥未被应用 + +- 检查 `.security.yml` 与 `config.json` 在同一目录中 +- 验证文件权限允许读取(`chmod 600 ~/.picoclaw/.security.yml`) +- 确保 YAML 结构与预期格式匹配 +- 检查字段名称中的拼写错误(区分大小写) +- 验证模型/渠道名称完全匹配(区分大小写) + +## 迁移指南 + +### 步骤 1:备份您的配置 + +系统在保存迁移后的配置前会自动创建带日期戳的备份(如 `config.json.20260330.bak` 和 `.security.yml.20260330.bak`)。如果您更喜欢手动备份: + +```bash +cp ~/.picoclaw/config.json ~/.picoclaw/config.json.backup +``` + +### 步骤 2:创建 .security.yml + +```bash +cp security.example.yml ~/.picoclaw/.security.yml +``` + +### 步骤 3:填写您的 API 密钥 + +编辑 `~/.picoclaw/.security.yml`,用您实际的密钥替换占位符值。 + +### 步骤 4:从 config.json 中删除敏感字段 + +从 `config.json` 中删除或注释敏感字段: +- `model_list` 条目中的 `api_key` 字段 +- `channels` 中的 `token` 字段 +- `tools.web` 中的 `api_key` 字段 +- `tools.skills` 中的 `token`/`auth_token` 字段 + +### 步骤 5:设置正确的权限 + +```bash +chmod 600 ~/.picoclaw/.security.yml +``` + +### 步骤 6:测试 + +```bash +picoclaw --version +``` + +### 步骤 7:验证功能 + +测试您的模型和渠道以确保一切正常工作。 + +### 步骤 8:清理(可选) + +如果一切正常,您可以删除备份: +```bash +rm ~/.picoclaw/config.json.backup +# 也可以删除自动生成的带日期戳的备份: +rm ~/.picoclaw/config.json.20*.bak ~/.picoclaw/.security.yml.20*.bak +``` + +## 高级:加密 API 密钥 + +PicoClaw 支持加密安全文件中的 API 密钥以提供额外保护。 + +### 设置 + +1. 通过环境变量设置密码短语: +```bash +export PICOCLAW_CREDENTIAL_PASSPHRASE="your-secure-passphrase" +``` + +2. 保存配置时,API 密钥将自动加密: +```go +SaveConfig(path, config) +``` + +### 加密格式 + +加密密钥存储为: +```yaml +model_list: + gpt-5.4: + api_keys: + - "enc://encrypted-base64-string" +``` + +系统在加载配置时会在运行时自动解密密钥。 + +### 好处 + +- 额外的安全层 +- 密钥静态加密 +- 密码短语可以与配置文件分开管理 + +### 重要说明 + +- 始终安全地备份您的密码短语 +- 如果丢失密码短语,您将失去对加密密钥的访问权限 +- 使用强且唯一的密码短语 +- 永不将密码短语提交到版本控制