---
slug: "知识自动沉淀引擎"
source_type: "clawhub"
source_url: "https://clawhub.ai/skills/knowledge-precipitation"
repo: ""
source_file: "description"
---
---
name: knowledge-precipitation
version: 0.1.18
description: |
  每日知识沉淀引擎（Knowledge Auto-Precipitation Engine，KAPE）v0.1.18。自动完成：下载昨日Get笔记内容 → 结合对话记录 → 深度分析用户学习、感悟、工作状态 → 生成含主题关联图的日志简报 → 同步归档到 Get笔记（带标签）+ 飞书知识库 + 飞书文档。触发场景：「整理昨天的日志」「生成日报简报」「知识沉淀」「整理学习记录」「存档昨天的内容」。

## v0.1.18 更新说明（2026-06-27 凭证修复）
- 同步版本号至 0.1.18，修正 skill_workshop 注册信息。

## v0.1.15 更新说明（2026-06-27 Bug 修复）
- **根因**：cron isolated session 不会自动注入 `GETNOTE_CLIENT_ID` 和 `GETNOTE_API_KEY` 环境变量，导致 `getnote auth login` 失败，提示"API未授权"。
- **修复**：新增「运行时凭证读取」章节，所有调用 Get笔记 CLI 的步骤均需先从配置文件（openclaw.json + secrets.json）读取凭证后再执行，彻底摆脱对环境变量的依赖。

## v0.1.14 更新说明（2026-06-06 Bug 修复）
- **根因**：cron 执行时使用了 `grep "^2026-06-05"`（行首匹配），但日期在最后一列（行首是笔记 ID），导致 7 条笔记全部漏读。v0.1.12/v0.1.13 均已警告此 Bug，但执行端仍违反。
- **SKILL.md 强化约束**：在命令示例中直接写出正确的 `grep` 命令，**禁止**使用行首锚 `^`。

## v0.1.13 更新说明
- **Get笔记日期过滤逻辑再次确认**：CLI 输出格式为 `ID | Title | Type | Created`，日期在**最后一列**，行首是笔记 ID（不是日期）。过滤必须用 `grep "${TARGET_DATE} "`（尾部空格避免部分匹配），严禁用 `grep "^${TARGET_DATE}"`（行首不是日期，永远匹配不到）
- **分页读取逻辑补全**：当笔记数量超过 limit（200条）时，用**最后一条笔记 ID** 作为下次 `since-id` 参数继续读取，循环直至 `has_more=false`，确保不漏读

## v0.1.12 更新说明
- **修复 Get笔记 日期过滤 Bug**：`grep "^2026-05-28"` 永远匹配不到（行首是笔记 ID，不是日期），应改为 `grep "2026-05-28 "`（日期在最后一列，尾部加空格避免部分匹配）
- 同时排查其他 skill：getnote/references/list.md 已补充 CLI 输出格式说明；tech-news-daily 和 sci-china-reviewer 均无需修改

## v0.1.11 更新说明
- **修复重复创建 Bug**：SKILL.md 描述的工作流与实际 CLI 命令不匹配，导致 Agent 摸索尝试产生冗余文档/笔记
- **根因**：
  - Get笔记 SKILL 写的是直接调 HTTPS API（会返回 10004 未授权），实际应使用 `getnote save` CLI
  - 飞书文档 SKILL 写了"先创建空文档 → 再写入内容"的两步流程，但写入失败重试时 Agent 又创建了新文档
- **修复**：统一使用 CLI 的原子操作（一次命令完成创建+写入），禁止分步创建空文档再写入

## v0.1.10 更新说明
- **修复飞书知识库同步失败 Bug**：cron 报告成功但实际未同步到知识库「日志简报」节点
- **根因**：Step 5 飞书知识库操作没有明确指定 parent_node_token，导致节点创建到了错误位置（根目录或其他节点下）
- **修复**：在 Step 5② 飞书知识库部分明确写入正确的节点层级结构；必须先创建 wiki 节点（获得 doc_token），再写入文档内容

## v0.1.8 更新说明
- **修复星期几判断 Bug**：Agent 禁止凭推理判断日期对应的星期几，必须用 `date -jf "%Y-%m-%d" <日期> +%A` 命令验证。Bug 原因：Agent 用当前日期倒推"昨天是周几"时出错（2026-05-11 本是周一，推断成周日）
- 第一步增加强制要求：先输出 `目标日期：YYYY-MM-DD（周X）` 再开始生成内容

## v0.1.7 更新说明
- 新增强制完整输出规则：无论输入数据多少，哪怕只有1条笔记，也必须生成完整模板的所有区块（主题关联图、数据概览、核心主题、每条笔记的详细分析、张公子关注什么、可以改进什么、明日关注、关键词、今日洞察）。简报不是摘要，是完整的结构化记录。当某日数据较少时，每个主题下的「分析/联想」部分可以简短，但不得省略整个区块。

## v0.1.6 更新说明
- 新增内容过滤规则：排除科技新闻日报生成的内容（本地文件、飞书文档链接、相关新闻笔记均不纳入日志简报）

## v0.1.5 更新说明
- 新增主题关联图功能：帮助快速定位知识节点
- 日志简报结构优化：主题关联图位于数据概览之后、核心主题之前

## v0.1.4 更新说明
- 新增第零步：Get笔记授权检查与自动刷新（电脑重启后 CLI 认证状态丢失时自动修复）
- API Key 从配置文件读取后直接传给 CLI，不记录任何日志，防止隐私泄露

## v0.1.3 更新说明
- 修复 memory 文件路径格式问题（Markdown 链接语法导致 ENOENT）
- 添加容错机制：memory 文件不存在时跳过，不阻断流程
- 确保 Get笔记 API 始终被调用，不依赖 memory 文件状态
---

# KAPE — 知识自动沉淀引擎 v0.1.18

## 安全说明

本 skill 需要以下工具权限：
- `exec`：调用 getnote CLI 获取笔记数据（只读操作）
- `feishu_wiki`、`feishu_doc`：写入飞书文档
- `sessions_list`、`sessions_history`：读取对话记录

**不会执行任何本地文件写入之外的 shell 命令**，所有外部 API 调用均为只读请求。

**重要**：禁止直接调用 `https://openapi.biji.com/...` HTTPS API（会返回 `10004 未授权`），必须使用 `getnote` CLI。

## 凭证配置

Get笔记 API 凭证存储位置：
- `GETNOTE_CLIENT_ID` → `openclaw.json` 的 `skills.entries.getnote.env.GETNOTE_CLIENT_ID`
- `GETNOTE_API_KEY` → `secrets.json` 的 `skills.entries.getnote.apiKey`（SecretRef 解析后的实际值）

飞书 appSecret → `secrets.json` 的 `channels.feishu.accounts.main.appSecret`

> ⚠️ **核心约束**：cron isolated session **不会**自动注入环境变量 `GETNOTE_CLIENT_ID` 和 `GETNOTE_API_KEY`，所有调用 Get笔记 CLI 的 shell 命令前**必须**先从配置文件读取凭证（详见下方「运行时凭证读取」章节）。不读取凭证直接调用 CLI 会导致"API 未授权"错误。

飞书机器人需已加入知识库成员，否则 `feishu_wiki(spaces)` 返回空。

## 运行时凭证读取

> ⚠️ **所有调用 Get笔记 CLI 的步骤**（第零步、第一步、第三步等），**必须先执行以下凭证读取代码**，再执行 CLI 命令。isolated session 无环境变量注入，绝不能省略此步骤。

```bash
GETNOTE_CLI="/Users/openclawer/.npm-global/lib/node_modules/@getnote/cli/bin/getnote"

# 从配置文件读取凭证（isolated session 不会自动注入环境变量）
GETNOTE_CFG=$(python3 -c "
import json
with open('/Users/openclawer/.openclaw/openclaw.json') as f:
    oc = json.load(f)
with open('/Users/openclawer/.openclaw/secrets.json') as f:
    sec = json.load(f)

# GETNOTE_CLIENT_ID：从 openclaw.json 直接读取
client_id = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('env',{}).get('GETNOTE_CLIENT_ID','')

# GETNOTE_API_KEY：从 secrets.json 读取（SecretRef 解析）
apikey_ref = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('apiKey',{})
if isinstance(apikey_ref, dict) and apikey_ref.get('source') == 'file':
    key_path = apikey_ref.get('id','')
    apikey = sec
    for k in key_path.split('/'):
        if k:
            apikey = apikey.get(k, {})
    api_key = apikey if isinstance(apikey, str) else apikey.get('apiKey','')
else:
    api_key = str(apikey_ref)

print(f'{client_id}\n{api_key}')
")

GETNOTE_CLIENT_ID=$(echo "$GETNOTE_CFG" | sed -n '1p')
GETNOTE_API_KEY=$(echo "$GETNOTE_CFG" | sed -n '2p')

# 验证读取结果（不打印完整 key）
echo "CLIENT_ID: ${GETNOTE_CLIENT_ID:0:10}..."
echo "API_KEY: ${GETNOTE_API_KEY:0:10}..."
```

---

## 共享文件夹配置

飞书文档统一存放在共享文件夹「牛管家日志」，确保张公子有删除权限。

| 配置项 | 值 |
|--------|---|
| 文件夹名称 | 牛管家日志 |
| 文件夹 token | `FQfXfYBGGllxxydJ1SgcJZWqnpf` |
| 文件夹 URL | https://qcnu4qzh46f0.feishu.cn/drive/folder/FQfXfYBGGllxxydJ1SgcJZWqnpf |
| 张公子权限 | full_access（可删除文档） |

---

## 核心工作流

每天自动生成日志简报，三端同步归档。**每个平台每次只执行一次原子操作，不允许先创建空文档再单独写入。**

---

### 第零步：Get笔记 凭证读取与授权检查

> ⚠️ **重要**：必须先读取凭证，再检查/执行授权。isolated session 无环境变量注入，不读取凭证会导致"API 未授权"。

**第一步：读取凭证 + 检查认证状态**
```bash
GETNOTE_CLI="/Users/openclawer/.npm-global/lib/node_modules/@getnote/cli/bin/getnote"

# 读取凭证
GETNOTE_CFG=$(python3 -c "
import json
with open('/Users/openclawer/.openclaw/openclaw.json') as f:
    oc = json.load(f)
with open('/Users/openclawer/.openclaw/secrets.json') as f:
    sec = json.load(f)
client_id = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('env',{}).get('GETNOTE_CLIENT_ID','')
apikey_ref = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('apiKey',{})
if isinstance(apikey_ref, dict) and apikey_ref.get('source') == 'file':
    key_path = apikey_ref.get('id','')
    apikey = sec
    for k in key_path.split('/'):
        if k:
            apikey = apikey.get(k, {})
    api_key = apikey if isinstance(apikey, str) else apikey.get('apiKey','')
else:
    api_key = str(apikey_ref)
print(f'{client_id}\n{api_key}')
")
GETNOTE_CLIENT_ID=$(echo "$GETNOTE_CFG" | sed -n '1p')
GETNOTE_API_KEY=$(echo "$GETNOTE_CFG" | sed -n '2p')

# 检查认证状态
${GETNOTE_CLI} auth status
```

**第二步：根据认证状态决定操作**
- 若返回 `Not authenticated`：
  ```bash
  ${GETNOTE_CLI} auth login --api-key "${GETNOTE_API_KEY}" --client-id "${GETNOTE_CLIENT_ID}"
  ```
  等待 `Logged in successfully.` 确认后再继续。
- 若返回 `Authenticated`：直接继续，不做任何操作。

---

### 第一步：确定日期范围

- **目标日期**：昨天
- **获取方式**：
  1. 使用 `date` 命令获取当前日期：`date +%Y-%m-%d`
  2. 用 `date` 命令计算昨天的日期：`date -v-1d +%Y-%m-%d`
  3. 用 `date` 命令验证星期：`date -jf "%Y-%m-%d" "<目标日期>" +%A`
- **输出要求**：在生成简报前，先输出 `目标日期：YYYY-MM-DD（周X）`，并用 `date` 命令确认
- **日期格式**：`YYYY-MM-DD`（用于字符串前缀匹配）

---

### 第二步：获取数据（并行）

#### Get笔记读取

> ⚠️ **重要**：Get笔记 API 必须通过 `getnote` CLI 调用，**禁止**直接调用 `https://openapi.biji.com/...`（会返回 `10004 未授权`）。

**读取步骤：**

1. **设置 CLI 路径 + 读取凭证**（必须先读取凭证，因为 isolated session 无环境变量）：
   ```bash
   GETNOTE_CLI="/Users/openclawer/.npm-global/lib/node_modules/@getnote/cli/bin/getnote"

   # 读取凭证（isolated session 不会自动注入环境变量）
   GETNOTE_CFG=$(python3 -c "
   import json
   with open('/Users/openclawer/.openclaw/openclaw.json') as f:
       oc = json.load(f)
   with open('/Users/openclawer/.openclaw/secrets.json') as f:
       sec = json.load(f)
   client_id = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('env',{}).get('GETNOTE_CLIENT_ID','')
   apikey_ref = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('apiKey',{})
   if isinstance(apikey_ref, dict) and apikey_ref.get('source') == 'file':
       key_path = apikey_ref.get('id','')
       apikey = sec
       for k in key_path.split('/'):
           if k:
               apikey = apikey.get(k, {})
       api_key = apikey if isinstance(apikey, str) else apikey.get('apiKey','')
   else:
       api_key = str(apikey_ref)
   print(f'{client_id}\n{api_key}')
   ")
   GETNOTE_CLIENT_ID=$(echo "$GETNOTE_CFG" | sed -n '1p')
   GETNOTE_API_KEY=$(echo "$GETNOTE_CFG" | sed -n '2p')
   ```

2. **检查认证状态**（如未认证则登录）：
   ```bash
   ${GETNOTE_CLI} auth status
   ```
   - 若返回 `Not authenticated`：
     ```bash
     ${GETNOTE_CLI} auth login --api-key "${GETNOTE_API_KEY}" --client-id "${GETNOTE_CLIENT_ID}"
     ```
   - 若返回 `Authenticated`：直接继续

3. **读取并过滤目标日期笔记（必须完成全部分页读取）**：
   ```bash
   # ⚠️ CLI 输出格式：ID | Title | Type | Created（日期在最后一列，行首是笔记ID）
   # ⚠️ 正确写法：grep "2026-05-28 "（尾部空格），匹配最后一列的日期
   # ❌ 错误写法：grep "^2026-05-28" —— 日期不在行首，永远匹配不到，**严禁使用**
   TARGET_DATE="2026-05-28"  # 昨日日期，格式 YYYY-MM-DD
   ${GETNOTE_CLI} notes --since-id 0 --limit 200 2>/dev/null | grep "${TARGET_DATE} "
   ```


4. **分页读取（防止遗漏）**：当笔记数超过 200 条时，用最后一条笔记 ID 作为下次 `since-id` 继续读取，循环直至 `has_more=false`：
   ```bash
   # 第一页
   ${GETNOTE_CLI} notes --since-id 0 --limit 200 2>/dev/null | grep "${TARGET_DATE} "
   # 下一页：用上一步最后一条笔记 ID 替换 0
   ${GETNOTE_CLI} notes --since-id <last_note_id> --limit 200 2>/dev/null | grep "${TARGET_DATE} "
   # 循环继续直至 has_more=false
   ```

5. **获取单条笔记详情**：
   ```bash
   ${GETNOTE_CLI} note <note_id> --field content  # 获取正文
   ${GETNOTE_CLI} note <note_id> --field title   # 获取标题
   ```

#### 对话记录获取

1. 用 `sessions_list` 获取所有 session（设置足够的 `activeMinutes` 覆盖目标日期）
2. 判断 session 在目标日期有活动的条件：`updatedAt` >= 目标日期开始时间 AND `updatedAt` < 今日开始时间
3. 用 `sessions_history` 读取符合条件的 session 内容（`includeTools=false`）
4. 解析用户消息（`role: user`）作为对话记录

#### 词汇存档（若有）

- **容错读取**：用 `exec` + `cat` 读取 `workspace/vocabulary/{target_date}.md`，若文件不存在或读取失败则跳过，不阻断流程
- 统计当日新增单词数量（如有）

> ⚠️ **路径处理规范**：所有从 `memory_search` 或 `sessions_list` 等工具返回的路径，返回格式可能为 Markdown 链接（如 `[2026-04-05.md](http://...`）或纯路径。传给 `read` 工具前，**必须先去除 Markdown 链接格式**，只提取纯路径部分。

---

### 第三步：深度分析与整理

> ⚠️ **内容过滤规则（v0.1.6 新增，必须执行）**
> 整理前需剔除【科技新闻日报】自动生成的内容：
> - **排除文件**：`memory/YYYY-MM-DD-tech-news.md`
> - **排除文档**：飞书文档标题含「科技新闻日报」或「科技新闻热榜」
> - **排除笔记**：Get笔记标签或来源为「科技新闻日报」相关条目
> - **仅保留**张公子个人学习、播客、录音、网页剪藏、手动笔记等主动获取内容

**生成日志简报结构**（见 `references/briefing-template.md`）

> ⚠️ **强制完整输出规则（v0.1.7 新增）**：无论输入数据多少，哪怕只有1条笔记，也必须生成完整模板的所有区块。简报不是摘要，是完整的结构化记录。

**生成主题关联图（v0.1.5 新增）：**
根据当日笔记和对话记录，自动提取3-5个核心主题，标注主题间的关联关系。

**关联类型标签：**
- `→` 因果关系（A导致B）
- `⟶` 支撑关系（A证实/支持B）
- `⇄` 竞争关系（A与B竞争）
- `↙` 衍生关系（A衍生出B）

**飞书文档中使用列表格式替代 ASCII 图形。**

---

### 第四步：写入本地文件

**必须先确保目录存在**：
```bash
mkdir -p /Users/openclawer/.openclaw/workspace/日志管理
```

**文件路径**：`/Users/openclawer/.openclaw/workspace/日志管理/{target_date}-日志简报.md`

---

### 第五步：三端同步归档（一次性操作到位）

> ⚠️ **核心原则**：每个平台每次只执行一次原子操作。不允许先创建空文档/空笔记，再单独写入内容的分步操作。如果写入失败，**先删除已创建的半成品**，再重新执行完整的原子操作。

**CLI 路径常量**（所有 `lark-cli` 命令必须使用完整路径）：
```bash
LARK_CLI="/Users/openclawer/.npm-global/bin/lark-cli"
```

---

#### ① Get笔记（使用 `getnote save` 原子操作）

```bash
GETNOTE_CLI="/Users/openclawer/.npm-global/lib/node_modules/@getnote/cli/bin/getnote"

# 读取凭证（isolated session 不会自动注入环境变量）
GETNOTE_CFG=$(python3 -c "
import json
with open('/Users/openclawer/.openclaw/openclaw.json') as f:
    oc = json.load(f)
with open('/Users/openclawer/.openclaw/secrets.json') as f:
    sec = json.load(f)
client_id = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('env',{}).get('GETNOTE_CLIENT_ID','')
apikey_ref = oc.get('skills',{}).get('entries',{}).get('getnote',{}).get('apiKey',{})
if isinstance(apikey_ref, dict) and apikey_ref.get('source') == 'file':
    key_path = apikey_ref.get('id','')
    apikey = sec
    for k in key_path.split('/'):
        if k:
            apikey = apikey.get(k, {})
    api_key = apikey if isinstance(apikey, str) else apikey.get('apiKey','')
else:
    api_key = str(apikey_ref)
print(f'{client_id}\n{api_key}')
")
GETNOTE_CLIENT_ID=$(echo "$GETNOTE_CFG" | sed -n '1p')
GETNOTE_API_KEY=$(echo "$GETNOTE_CFG" | sed -n '2p')

# content 必须是完整简报正文，不允许简写
${GETNOTE_CLI} save "<完整简报正文>" \
  --title "日志简报 {target_date} | 张公子" \
  --tag AI整理 \
  --tag 日志简报
```

> ⚠️ **禁止**直接调用 `https://openapi.biji.com/...` HTTPS API，必须使用 `getnote save` CLI。

---

#### ② 飞书知识库（先创建节点 → 再写入内容，两步都是原子的）

> ⚠️ **节点层级结构**：
> - 个人知识库（space_id: `7621391289904516315`）
> - `日志简报` 节点（node_token: `SERDwHBAniUqqBkx5vNctvgKn6f`）
> - **每日日志简报文档**（创建在此节点下）

**步骤 1：创建 wiki 节点（原子操作）**
```bash
cd /Users/openclawer/.openclaw/workspace/日志管理
${LARK_CLI} wiki +node-create \
  --space-id 7621391289904516315 \
  --parent-node-token SERDwHBAniUqqBkx5vNctvgKn6f \
  --title "日志简报 {target_date} | 张公子"
```
- 从返回的 `obj_token` 提取新文档的 doc token
- 从返回的 `node_token` 提取新 wiki 节点 token

**步骤 2：写入简报内容（原子操作）**
```bash
${LARK_CLI} docs +update \
  --api-version v2 \
  --doc "<obj_token>" \
  --command overwrite \
  --doc-format xml \
  --content @./2026-05-25-日志简报.md
```
> ⚠️ `--doc-format xml` 必须明确指定（默认 xml）。
> ⚠️ **重要**：飞书文档 **不支持 Markdown 表格**（写入时表格内容会丢失）。如简报包含表格，**必须使用 `--doc-format xml` 并提供 XML 格式内容**，XML 表格结构为 `<table><thead><tr><th>...</th></tr></thead><tbody><tr><td>...</td></tr></tbody></table>`。

**步骤 3：记录知识库 URL**

---

#### ③ 飞书文档（创建+写入一次完成，再授权）

**步骤 1：获取 tenant_access_token**
```bash
python3 -c "
import json, urllib.request
with open('/Users/openclawer/.openclaw/openclaw.json') as f:
    d = json.load(f)
# appSecret 从 secrets.json 读取
with open('/Users/openclawer/.openclaw/secrets.json') as f:
    sec = json.load(f)
app_secret = sec.get('channels',{}).get('feishu',{}).get('accounts',{}).get('main',{}).get('appSecret','')
req = urllib.request.Request(
    'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
    data=json.dumps({'app_id':'cli_a94b4a1e43781cc7','app_secret':app_secret}).encode(),
    headers={'Content-Type':'application/json'}
)
resp = urllib.request.urlopen(req, timeout=10)
print(resp.read().decode())
"
```

**步骤 2：创建文档并写入内容（原子操作）**
```bash
cd /Users/openclawer/.openclaw/workspace/日志管理
${LARK_CLI} docs +create \
  --api-version v2 \
  --parent-token FQfXfYBGGllxxydJ1SgcJZWqnpf \
  --doc-format xml \
  --content @./2026-05-25-日志简报.md
```
> ⚠️ **禁止先创建空文档再单独写入**。必须在 `--content` 中直接提供完整内容。
> ⚠️ **重要**：飞书文档 **不支持 Markdown 表格**，如简报包含表格，必须改用 `--doc-format xml` 并提供 XML 格式内容。

**步骤 3：提取 document_id（从返回的 `document.document_id`）**

**步骤 4：授予张公子 full_access 权限**
```bash
curl -X POST 'https://open.feishu.cn/open-apis/drive/v1/permissions/<document_id>/members?type=docx' \
  -H 'Authorization: Bearer {tenant_access_token}' \
  -H 'Content-Type: application/json' \
  -d '{"member_type":"openid","member_id":"ou_d8ace8a146610ca26bc07d8e68a5620f","perm":"full_access"}'
```

**步骤 5：记录文档 URL**

---

### 第六步：用户反馈

向用户发送完成通知，包含：
- Get笔记数量（分类统计：录音/播客/纯文本等）
- 参考对话记录数量
- 简报核心发现摘要（1-3句话）
- 各端存储结果链接

---

## 错误处理原则

1. **任何一步失败不影响其他步骤**：三端归档是独立的，写入本地文件是最基本的保障
2. **明确告知用户失败原因**：如果某个平台失败，需要在反馈中说明
3. **不要静默失败**：如果关键步骤（如获取数据）失败，必须通知用户
4. **写入前先查重**：写入前先检查目标日期是否已有同名笔记/文档，若有则先删除再创建