Install
openclaw skills install @binhuatochina/knowledge-precipitationopenclaw skills install @binhuatochina/knowledge-precipitation本 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_IDGETNOTE_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 无环境变量注入,绝不能省略此步骤。
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(可删除文档) |
每天自动生成日志简报,三端同步归档。每个平台每次只执行一次原子操作,不允许先创建空文档再单独写入。
⚠️ 重要:必须先读取凭证,再检查/执行授权。isolated session 无环境变量注入,不读取凭证会导致"API 未授权"。
第一步:读取凭证 + 检查认证状态
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:
${GETNOTE_CLI} auth login --api-key "${GETNOTE_API_KEY}" --client-id "${GETNOTE_CLIENT_ID}"
Logged in successfully. 确认后再继续。Authenticated:直接继续,不做任何操作。date 命令获取当前日期:date +%Y-%m-%ddate 命令计算昨天的日期:date -v-1d +%Y-%m-%ddate 命令验证星期:date -jf "%Y-%m-%d" "<目标日期>" +%A目标日期:YYYY-MM-DD(周X),并用 date 命令确认YYYY-MM-DD(用于字符串前缀匹配)⚠️ 重要:Get笔记 API 必须通过
getnoteCLI 调用,禁止直接调用https://openapi.biji.com/...(会返回10004 未授权)。
读取步骤:
设置 CLI 路径 + 读取凭证(必须先读取凭证,因为 isolated session 无环境变量):
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')
检查认证状态(如未认证则登录):
${GETNOTE_CLI} auth status
Not authenticated:
${GETNOTE_CLI} auth login --api-key "${GETNOTE_API_KEY}" --client-id "${GETNOTE_CLIENT_ID}"
Authenticated:直接继续读取并过滤目标日期笔记(必须完成全部分页读取):
# ⚠️ 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} "
分页读取(防止遗漏):当笔记数超过 200 条时,用最后一条笔记 ID 作为下次 since-id 继续读取,循环直至 has_more=false:
# 第一页
${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
获取单条笔记详情:
${GETNOTE_CLI} note <note_id> --field content # 获取正文
${GETNOTE_CLI} note <note_id> --field title # 获取标题
sessions_list 获取所有 session(设置足够的 activeMinutes 覆盖目标日期)updatedAt >= 目标日期开始时间 AND updatedAt < 今日开始时间sessions_history 读取符合条件的 session 内容(includeTools=false)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 图形。
必须先确保目录存在:
mkdir -p /Users/openclawer/.openclaw/workspace/日志管理
文件路径:/Users/openclawer/.openclaw/workspace/日志管理/{target_date}-日志简报.md
⚠️ 核心原则:每个平台每次只执行一次原子操作。不允许先创建空文档/空笔记,再单独写入内容的分步操作。如果写入失败,先删除已创建的半成品,再重新执行完整的原子操作。
CLI 路径常量(所有 lark-cli 命令必须使用完整路径):
LARK_CLI="/Users/openclawer/.npm-global/bin/lark-cli"
getnote save 原子操作)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 saveCLI。
⚠️ 节点层级结构:
- 个人知识库(space_id:
7621391289904516315)日志简报节点(node_token:SERDwHBAniUqqBkx5vNctvgKn6f)- 每日日志简报文档(创建在此节点下)
步骤 1:创建 wiki 节点(原子操作)
cd /Users/openclawer/.openclaw/workspace/日志管理
${LARK_CLI} wiki +node-create \
--space-id 7621391289904516315 \
--parent-node-token SERDwHBAniUqqBkx5vNctvgKn6f \
--title "日志简报 {target_date} | 张公子"
obj_token 提取新文档的 doc tokennode_token 提取新 wiki 节点 token步骤 2:写入简报内容(原子操作)
${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
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:创建文档并写入内容(原子操作)
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 权限
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
向用户发送完成通知,包含: