# 隐私配置与 Skill 隐私提取/加载
本文介绍 OpenViking 的隐私配置能力,以及它与 Skill 写入(提取)和读取(加载还原)的协作机制。
## 目标
隐私配置用于把敏感值(如 `api_key`、`token`、`base_url`)从技能正文中分离出来,避免明文长期存储在 `SKILL.md` 中,同时保留版本化管理与回滚能力。
核心目标:
- 写入时自动抽取敏感值并占位符化
- 读取时按当前生效配置自动还原
- 支持版本查询、切换与审计
---
## 核心对象与存储结构
隐私配置按二级键组织:`category + target_key`。
- `category`:配置类别(当前 Skill 使用 `skill`)
- `target_key`:目标标识(通常是 skill 名)
用户空间下的存储路径:
```
viking://user/{user_space}/privacy/{category}/{target_key}/
├── .meta.json # 元信息(active_version/latest_version/labels 等)
├── current.json # 当前生效版本快照
└── history/
├── version_1.json
├── version_2.json
└── ...
```
`current.json` 与 `history/version_x.json` 都是完整快照(`values` 整包)。
---
## 版本语义
### upsert(写入)
- 每次 upsert 都以传入 `values` 作为“新快照候选”
- 如果与当前版本 `values` 完全一致,则不新建版本,直接返回当前版本
- 否则创建新版本并自动设为当前生效版本
- 允许新增 key(不会因“未知 key”报错)
### activate(激活)
- 将历史版本设置为当前生效版本(写回 `current.json`)
- 更新 `.meta.json` 中的 `active_version`
---
## Skill 隐私提取(写入链路)
当通过 `add_skill` 写入 Skill 时,会走隐私提取与占位符化流程。
```
add_skill
-> SkillProcessor._sanitize_skill_privacy
-> extract_skill_privacy_values (LLM 抽取 values)
-> placeholderize_skill_content_with_blocks (明文替换为占位符)
-> privacy.upsert(category="skill", target_key=skill_name, values=...)
-> 写入占位符化后的 SKILL.md
```
### 关键点
1. **抽取结果来源**:模型返回 JSON,读取 `values` 字段作为隐私键值。
2. **内容替换**:原文中命中的敏感片段会替换为占位符:
- {{ov_privacy:skill:{skill_name}:{field_name}}}
3. **保留块映射**:会同时记录:
- `original_content_blocks`
- `replacement_content_blocks`
4. **写盘结果**:`SKILL.md` 持久化的是占位符内容,不是明文值。
---
## Skill 加载还原(读取链路)
读取 `SKILL.md` 时,`FSService.read` 会尝试自动还原占位符。
```
fs.read(uri)
-> get_skill_name_from_uri(uri)
-> privacy.get_current(category="skill", target_key=skill_name)
-> restore_skill_content(content, skill_name, current.values)
```
### URI 匹配
当前支持通过后缀识别 Skill:`/skills/{name}/SKILL.md`,可兼容:
- `viking://agent/skills/{name}/SKILL.md`
- `viking://agent/{agent_id}/skills/{name}/SKILL.md`
### restore 规则
`restore_skill_content` 的行为如下:
1. **content 中有占位符,且 privacy value 存在且非空**
-> 直接替换为对应值。
2. **content 中有占位符,但 privacy value 缺失或为空**
-> 保留占位符不替换,并记入 `unresolved_entries`。
3. **privacy 中存在 key 且非空,但 content 中没有对应占位符**
-> 记入“额外配置”提示(`Configured but not referenced in content`)。
4. 当存在 `unresolved_entries` 或“额外配置”时,会在内容末尾追加:
- `[OpenViking Privacy Notice]`
- `Related configured privacy values: ...`
- `Not replaced (missing config): ...`(如有)
- `Configured but not referenced in content: ...`(如有)
> 注意:当前实现中,仅当该 skill 已有 `current` 配置时才会进入 restore。若没有当前配置,不会追加 notice。
---
## 与 CLI/API 的关系
- 管理面:通过 Privacy API/CLI 管理版本(查询、写入、回滚)
- 内容面:通过 `read` 自动恢复占位符
- 两者解耦:内容文件负责占位符,隐私服务负责敏感值与版本
常用命令:
```bash
openviking privacy categories
openviking privacy list skill
openviking privacy skill
openviking privacy upsert skill --values-json '{"api_key":"..."}'
openviking privacy activate skill
openviking read viking://agent/default/skills//SKILL.md
```
---
## 设计收益
- 降低明文敏感信息在技能正文中的暴露风险
- 通过版本化支持密钥轮换与快速回滚
- 对上层调用透明:`read` 即可拿到还原后的可执行技能文本
- 对不完整配置提供可观测提示,便于排障
---
## 相关文档
- [技能](../api/04-skills.md) - Skill 写入与读取 API
- [隐私配置 API](../api/10-privacy.md) - 隐私配置端点说明
- [上下文提取](./06-extraction.md) - 内容提取主流程
- [架构概述](./01-architecture.md) - 系统分层与模块关系