添加技能模块

This commit is contained in:
2026-02-15 01:17:39 +08:00
parent c417a8f7a9
commit 86daba5771
48 changed files with 10121 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
# KB Workflow 环境变量配置
export KB_BASE_PATH="~/work/knowledge-base"
# 可选: B站 Cookie(用于下载会员视频)
# export BILIBILI_COOKIE="SESSDATA=xxx; bili_jct=xxx"
# 可选: AI 分析配置
# export AI_API_KEY="your-api-key"
# export AI_PROVIDER="openai" # 或 zhipu, minimax
# export AI_MODEL="gpt-4"
# 可选: Git 配置
# export KB_GIT_USER="Your Name"
# export KB_GIT_EMAIL="your@email.com"
+14
View File
@@ -0,0 +1,14 @@
# KB Workflow 环境变量配置
export KB_BASE_PATH="~/work/knowledge-base"
# 可选: B站 Cookie(用于下载会员视频)
# export BILIBILI_COOKIE="SESSDATA=xxx; bili_jct=xxx"
# 可选: AI 分析配置
# export AI_API_KEY="your-api-key"
# export AI_PROVIDER="openai" # 或 zhipu, minimax
# export AI_MODEL="gpt-4"
# 可选: Git 配置
# export KB_GIT_USER="Your Name"
# export KB_GIT_EMAIL="your@email.com"
+247
View File
@@ -0,0 +1,247 @@
# KB Workflow 环境安装脚本
一键检查并安装 kb-workflow 知识库工作流所需的全部环境依赖。
## 功能特性
- ✅ 自动检测系统类型 (Ubuntu/Debian/Fedora/Arch)
- ✅ 检查系统依赖 (Python3, pip3, ffmpeg, tesseract, git)
- ✅ 检查 Python 依赖包 (httpx, aiohttp, playwright, easyocr 等)
- ✅ 自动创建知识库目录结构
- ✅ 生成环境变量配置示例
- ✅ 汇总显示所有需要执行的安装命令
## 使用方法
### 运行检查脚本
```bash
cd /home/starmen1265/.openclaw/workspace/skills
./install-kb-workflow.sh
```
脚本会检查所有依赖,并显示需要执行的安装命令。
### 示例输出
```
========================================
第一步: 检查系统依赖
========================================
[SUCCESS] Python3 已安装: Python 3.12.3
[WARN] pip3 未安装
[WARN] ffmpeg 未安装
...
========================================
需要安装的系统依赖
========================================
检测到以下依赖未安装:
- python3-pip
- ffmpeg
- tesseract-ocr
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
请运行以下命令安装:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
sudo apt-get update
sudo apt-get install -y python3-pip ffmpeg tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng
```
## 依赖清单
### 系统依赖
| 依赖 | 用途 | 安装命令 |
|------|------|----------|
| **python3** | Python 运行时 | `sudo apt-get install python3` |
| **python3-pip** | Python 包管理器 | `sudo apt-get install python3-pip` |
| **ffmpeg** | 视频处理(OCR 截帧) | `sudo apt-get install ffmpeg` |
| **tesseract-ocr** | OCR 引擎(备用方案) | `sudo apt-get install tesseract-ocr` |
| **git** | 版本控制(归档提交) | `sudo apt-get install git` |
### Tesseract 语言包
```bash
# Ubuntu/Debian
sudo apt-get install tesseract-ocr-chi-sim tesseract-ocr-chi-tra tesseract-ocr-eng
# Fedora/RHEL
sudo dnf install tesseract-langpack-chi_sim tesseract-langpack-chi_tra tesseract-langpack-eng
```
### Python 依赖
```bash
# 基础 HTTP 库
pip3 install --user httpx aiohttp requests
# 图像和 OCR
pip3 install --user Pillow easyocr pytesseract
# 浏览器自动化
pip3 install --user playwright
python3 -m playwright install chromium
```
## 目录结构
安装完成后会创建以下目录结构:
```
~/work/knowledge-base/ # 知识库根目录(可配置)
├── archive/ # 归档目录
│ ├── pending/ # 待处理
│ └── subscriptions/ # 订阅信息
├── metadata/ # 元数据文件
├── content/ # 内容文件
│ ├── downloads/ # 视频下载
│ └── subtitles/ # 字幕文件
└── analyzed/ # 深度分析报告
```
## 环境变量
### 必需
- `KB_BASE_PATH` - 知识库根目录(默认: `~/work/knowledge-base`)
### 可选
- `BILIBILI_COOKIE` - B站 Cookie,用于下载会员视频
- `AI_API_KEY` - AI API 密钥,用于深度内容分析
- `AI_PROVIDER` - AI 提供商(openai/zhipu/minimax)
- `AI_MODEL` - AI 模型名称(如 gpt-4)
- `AI_BASE_URL` - 自定义 API 端点
- `KB_GIT_USER` - Git 用户名(用于归档提交)
- `KB_GIT_EMAIL` - Git 邮箱(用于归档提交)
- `KB_GIT_REMOTE` - Git 远程仓库地址
### 配置方法
将以下内容添加到 `~/.bashrc` 或 `~/.zshrc`:
```bash
# KB Workflow 配置
export KB_BASE_PATH="~/work/knowledge-base"
# 可选:B站 Cookie
export BILIBILI_COOKIE="SESSDATA=xxx; bili_jct=xxx"
# 可选:AI 配置
export AI_API_KEY="your-api-key"
export AI_PROVIDER="openai"
export AI_MODEL="gpt-4"
```
然后重新加载配置:
```bash
source ~/.bashrc # 或 source ~/.zshrc
```
## 故障排除
### 1. pip3 命令不存在
```bash
# 方法 1: 安装 pip3
sudo apt-get install python3-pip
# 方法 2: 使用 python3 -m pip
python3 -m pip install --user <package>
```
### 2. Playwright 浏览器安装失败
```bash
# 手动安装 Chromium
python3 -m playwright install chromium
# 如果仍然失败,尝试安装依赖
sudo apt-get install libnss3 libatk-bridge2.0-0 libxss1 libgtk-3-0
```
### 3. EasyOCR 安装失败
EasyOCR 依赖 PyTorch,可能需要较长时间安装:
```bash
# 先安装 PyTorch
pip3 install --user torch torchvision --index-url https://download.pytorch.org/whl/cpu
# 再安装 easyocr
pip3 install --user easyocr
```
### 4. 权限问题
如果使用 `--user` 安装的包无法找到,检查 PATH:
```bash
# 查看用户安装路径
python3 -m site --user-base
# 添加到 PATH(添加到 ~/.bashrc)
export PATH="$PATH:$(python3 -m site --user-base)/bin"
```
### 5. OCR 中文识别问题
确保已安装中文语言包:
```bash
# 检查已安装的语言包
tesseract --list-langs
# 应该能看到 chi_sim 和 chi_tra
```
## 更新
如果技能更新了依赖,可以重新运行检查脚本:
```bash
./install-kb-workflow.sh
```
## 子技能清单
| 技能 | 路径 | 功能 |
|------|------|------|
| kb-workflow | `kb-workflow/kb_workflow.py` | 主流程协调 |
| kb-link-parser | `kb-link-parser/link-parser.py` | 链接解析 |
| kb-bilibili-metadata | `kb-bilibili-metadata/bilibili_metadata.py` | B站元数据提取 |
| kb-bilibili-content | `kb-bilibili-content/bilibili_content.py` | B站内容下载 |
| kb-ocr-processor | `kb-ocr-processor/ocr_processor.py` | 视频 OCR 识别 |
| kb-deep-analyzer | `kb-deep-analyzer/deep_analyzer.py` | AI 深度分析 |
## 使用 kb-workflow
安装完成后,可以通过以下方式使用:
### 方式 1:通过主代理
直接向主代理发送消息:
```
添加到知识库 https://www.bilibili.com/video/BVxxxxx
```
### 方式 2:手动运行子技能
```bash
# 1. 链接解析
python3 kb-link-parser/link-parser.py --url "https://www.bilibili.com/video/BVxxxxx"
# 2. 元数据提取
python3 kb-bilibili-metadata/bilibili_metadata.py --id "BVxxxxx" --type video
# 3. 内容下载
python3 kb-bilibili-content/bilibili_content.py --id "BVxxxxx" --type subtitle
# 4. 深度分析
python3 kb-deep-analyzer/deep_analyzer.py --metadata meta.json --content content.json --output analysis.md
```
## 许可证
MIT License
+360
View File
@@ -0,0 +1,360 @@
---
name: kb-workflow
description: 当收到"添加到知识库 + 链接" 或"保存到知识库 + 链接"时使用
---
# KB Workflow
## 什么时候使用
当用户发送包含以下模式的消息时,此技能自动触发:
| 触发词 | 行为 | 示例 |
|--------|------|------|
| `添加到知识库` + 链接 | 添加到知识库并处理 | "添加到知识库 https://www.bilibili.com/video/BVxxx" |
| `保存到知识库` + 链接 | 同上 | "保存到知识库 https://www.bilibili.com/video/BVxxx" |
## 环境变量
### 执行前必须检查
在执行 workflow 之前,必须检查环境变量是否已经设置。
**必须的环境变量**(缺失则报错退出):
配置方式:将变量写入 `~/.kb_env` 文件:
```bash
export AI_API_KEY="your-api-key"
export AI_PROVIDER="zhipu"
export AI_MODEL="glm-5"
export AI_BASE_URL="https://open.bigmodel.cn/api/coding/paas/v4"
```
**可选的环境变量**:
| 变量名 | 用途 | 示例 |
|--------|------|------|
| `BILIBILI_COOKIE` | 下载会员视频 | `SESSDATA=xxx; bili_jct=xxx` |
| `KB_GIT_USER` | Git提交用户名 | `Your Name` |
| `KB_GIT_EMAIL` | Git提交邮箱 | `your@email.com` |
### 检查命令
```bash
# 检查必须的环境变量
echo "AI_API_KEY: ${AI_API_KEY:0:10}..."
echo "AI_PROVIDER: $AI_PROVIDER"
echo "AI_MODEL: $AI_MODEL"
echo "AI_BASE_URL: $AI_BASE_URL"
# 验证方式1: 检查变量是否存在
if [ -z "$AI_API_KEY" ]; then
echo "❌ AI_API_KEY 未设置"
exit 1
fi
# 验证方式2: 执行测试调用
python3 -c "
import os
if not os.environ.get('AI_API_KEY'):
raise RuntimeError('AI_API_KEY 未设置')
print('✅ 环境变量检查通过')
"
```
## Workflow 执行方式
主代理直接执行以下步骤,无需启动 kb-workflow 子代理:
1. 主代理启动子代理调用 kb-link-parser
2. 主代理启动子代理调用 kb-平台-metadata
3. 主代理启动子代理调用 kb-平台-content
4. 主代理启动子代理调用 kb-ocr-processor(如需要)
5. 主代理启动子代理调用 kb-deep-analyzer
6. 主代理执行 `kb_workflow.py --archive-file` 归档
## Workflow 执行步骤
当触发后,按照以下步骤执行:
### 步骤 1: 环境变量检查
**❌ 禁止跳过此步骤!必须先检查环境变量。**
```bash
# 0. 从 ~/.kb_env 加载环境变量(专门的知识库配置文件,不受 bashrc 交互式检查影响)
if [ -f ~/.kb_env ]; then
source ~/.kb_env
fi
# 1. 检查必须的环境变量是否存在
if [ -z "$AI_API_KEY" ] || [ -z "$AI_PROVIDER" ] || [ -z "$AI_MODEL" ] || [ -z "$AI_BASE_URL" ]; then
echo "❌ 错误:必须的环境变量未设置"
echo "请配置以下环境变量:"
echo " - AI_API_KEY"
echo " - AI_PROVIDER (zhipu/openai/minimax)"
echo " - AI_MODEL"
echo " - AI_BASE_URL"
exit 1
fi
echo "✅ 环境变量检查通过"
echo " AI_PROVIDER: $AI_PROVIDER"
echo " AI_MODEL: $AI_MODEL"
```
**如果检查不通过,必须**:
1. 报错提示用户
2. 退出,不执行后续步骤
3. 等待用户配置好环境变量后重新执行
## ⚠️ 严格失败处理原则
> **深度分析步骤必须成功,否则整个流程失败**
在执行工作流时,必须遵守以下原则:
1. **深度分析不可跳过**:kb-deep-analyzer 是必须成功执行的步骤
2. **失败即终止**:如果深度分析返回 `success: false`,必须立即停止并汇报失败
3. **禁止降级处理**:不能使用预设模板或简单摘要来"假装成功"
4. **每次执行都要使用 AI**:必须有真实的 AI API 调用记录
### 步骤 2: 链接解析
1. 获取目录路径:
```bash
python3 kb_workflow.py --pending
```
2. 启动子代理调用 kb-link-parser:
```bash
python3 /path/to/link-parser.py --url "[URL]"
```
3. 解析输出 JSON,提取 filename 和 platform
### 步骤 3: 元数据提取
1. 获取元数据目录路径:
```bash
python3 kb_workflow.py --metadata
# 返回: {"path": "/home/admin/work/knowledge-base/metadata"}
```
2. 构建输出文件路径:`${metadata_dir}/${platform}_${id}.meta.json`
3. 启动子代理调用对应平台的 metadata 技能(传入 `--output`):
```bash
# Bilibili
python3 /path/to/bilibili_metadata.py --id "[ID]" --type video --output "${metadata_dir}/bilibili_[ID].meta.json"
# Zhihu
python3 /path/to/zhihu_metadata.py --id "[ID]" --type question --output "${metadata_dir}/zhihu_[ID].meta.json"
```
4. 解析输出 JSON,提取 filename
### 步骤 4: 内容下载
1. 获取目录路径:
```bash
python3 kb_workflow.py --content_downloads
python3 kb_workflow.py --content
```
2. 启动子代理调用对应平台的 content 技能:
```bash
# Bilibili 字幕下载(自动检测是否有官方字幕)
python3 /path/to/bilibili_content.py --id "[ID]" --type subtitle-download --output [content_dir]
# 返回 JSON 包含 has_subtitle 字段
```
3. 解析输出 JSON:
- **has_subtitle = true**: 有官方字幕,直接进入步骤 6
- **has_subtitle = false**: 没有官方字幕,继续步骤 5
> ⚠️ **重要提示**:没有找到官方字幕是**正常的**!很多视频没有自动生成的字幕。不要因为没有字幕就停止工作流,继续执行 Whisper 步骤即可。
### 步骤 5: 语音识别转文字(仅当没有官方字幕时执行)
> ⚠️ **重要**:此步骤仅在**没有官方字幕**时执行。如果有官方字幕,**跳过此步骤**。
此步骤使用 Whisper 进行语音识别:
- **严禁跳过**:必须等待识别完成
- **定时汇报**:每2-3分钟向用户汇报进度
- **超时时间**:30分钟
- **耐心等待**:不要中途启动其他步骤,必须串行执行
1. 获取目录路径:
```bash
python3 kb_workflow.py --content
```
2. 启动子代理调用 kb-whisper(传入视频路径):
```bash
# 必须传入 --output 完整路径
python3 /path/to/whisper.py --input [video_path] --model base --output [content_dir]
```
3. 等待 Whisper 完成(定时汇报进度)
4. 解析输出 JSON,生成 content 文件供步骤6使用
### 步骤 6: 深度分析
1. 获取目录路径:
```bash
python3 kb_workflow.py --analyzed
```
2. 准备 content JSON 文件(包含 subtitle/description)
3. 如果有官方字幕,使用官方字幕的 SRT 文件
4. 如果没有官方字幕,使用 Whisper 识别的结果
5. 启动子代理调用 kb-deep-analyzer(使用 g5 模型):
```bash
python3 /path/to/deep_analyzer.py --metadata [meta_path] --content [content_path] --output [analyzed_dir]/[filename].md --provider zhipu --model glm-5
```
**环境变量设置**(在子代理中设置):
- `AI_PROVIDER=zhipu`
- `AI_MODEL=glm-5`
6. 解析输出 JSON,检查 `success` 字段:
> ⚠️ **深度分析失败 = 整个流程失败**
> - 如果 `success = true`:继续步骤 7(归档)
> - 如果 `success = false`:**立即终止流程**,向用户汇报失败原因
> - **严禁**继续执行后续步骤或使用降级内容"假装成功"
### 步骤 7: 归档
运行归档命令:
```bash
python3 kb_workflow.py --archive-file "[分析报告文件路径]"
```
### 步骤 8: 汇报完成
向用户汇报:
- 视频标题
- 处理阶段完成情况
- 最终文件路径
- Git commit 信息
## 子技能清单
| 步骤 | 子技能 | 命令示例 | 输出 | 备注 |
|------|--------|----------|------|------|
| 2 | kb-link-parser | `--url "BVxxx"` | `{p}_{id}.link.json` | |
| 3 | kb-bilibili-metadata | `--id "BVxxx" --type video` | `{p}_{id}.meta.json` | |
| 3 | kb-zhihu-metadata | `--id "xxx" --type question` | `{p}_{id}.meta.json` | |
| 4 | kb-bilibili-content | `--id "BVxxx" --type subtitle-download` | `{p}_{id}.srt` | 如果没有字幕是正常的 |
| 4 | kb-bilibili-content | `--id "BVxxx" --type video` | `{p}_{id}.mp4` | 如果需要下载视频 |
| 5 | kb-whisper | `--input xxx.mp4 --model base` | `{p}_{id}.json`, `{p}_{id}.txt` | 仅当没有官方字幕时执行 |
| 6 | kb-deep-analyzer | `--metadata x.meta.json --content x.json --output x.md --provider zhipu --model glm-5` | `{p}_{id}.md` | **使用 g5 模型,必须成功** |
## 条件分支逻辑
```
步骤3: 获取元数据
↓
步骤4: 下载字幕 (--type subtitle-download)
↓
┌─────────────────────────────────────┐
│ has_subtitle = true? │
├─────────────────────────────────────┤
│ 是 → 直接进入步骤6(深度分析) │
│ 否 → 进入步骤5(Whisper语音识别) │
└─────────────────────────────────────┘
↓
步骤6: 深度分析
↓
┌─────────────────────────────────────┐
│ kb-deep-analyzer 执行结果 │
├─────────────────────────────────────┤
│ success = true → 步骤7(归档) │
│ success = false → ❌ 整个流程失败 │
└─────────────────────────────────────┘
```
> ⚠️ **重要:深度分析失败则整个流程失败**
> - 深度分析必须使用正确的 AI 模型(glm-4.7)完整分析字幕内容
> - 如果 `kb-deep-analyzer` 返回 `success: false`,**必须汇报失败**,不能继续执行后续步骤
> - 不能使用预设模板或降级内容来"假装成功"
> - 失败时必须向用户报告具体的错误原因
> ⚠️ **重要**:没有找到官方字幕是**正常的**!很多视频没有自动生成的字幕。
> - 如果 `has_subtitle = false`,继续执行 Whisper 步骤
> - 如果 `has_subtitle = true`,跳过 Whisper 步骤,直接使用官方字幕
## 目录结构
```
~/work/knowledge-base/
├── archive/
│ └── YYYY-MM-DD/
├── pending/
│ └── {platform}_{id}.link.json
├── metadata/
│ └── {platform}_{id}.meta.json
├── content/
│ ├── downloads/
│ │ └── {platform}_{id}.mp4
│ ├── {platform}_{id}.txt # Whisper 识别的文本
│ └── {platform}_{id}.json # Whisper 完整结果
└── analyzed/
└── {platform}_{id}.md
```
## 辅助命令
```bash
# ------------------- 查重与归档 -------------------
# 查重
python3 kb_workflow.py --check --url "[URL]"
# 归档分析报告
python3 kb_workflow.py --archive-file "[分析报告路径]"
# ------------------- 目录路径查询 -------------------
# 获取知识库根目录(默认 ~/work/knowledge-base)
python3 kb_workflow.py --base-path "~/custom/path"
# 获取待处理目录(pending)
python3 kb_workflow.py --pending
# 获取元数据目录
python3 kb_workflow.py --metadata
# 获取内容目录
python3 kb_workflow.py --content
# 获取内容下载目录
python3 kb_workflow.py --content_downloads
# 获取字幕目录
python3 kb_workflow.py --content_subtitles
# 获取深度分析目录
python3 kb_workflow.py --analyzed
# 获取归档目录
python3 kb_workflow.py --archive
# 获取订阅信息目录
python3 kb_workflow.py --archive_subscriptions
# ------------------- 工具函数 -------------------
# 从文件名提取平台名称
python3 kb_workflow.py --extract_platform "bilibili_BVxxx.mp4"
```
+101
View File
@@ -0,0 +1,101 @@
# SkillBug.md
## 问题记录
### 2026-02-10: 子代理权限问题
**问题:** `sessions_spawn` 调用子代理时返回 `forbidden` 错误。
**原因:** OpenClaw 配置中 allowlist 为空,Python 脚本没有权限调用子代理。
**解决方案:** 改用 Prompt-based 模式(方案B)。
---
## 架构重构
### 之前(Script-based)
```
kb_workflow.py (Python脚本)
↓
sessions_spawn() # ❌ 失败(脚本没有权限)
```
### 之后(Prompt-based)
```
主代理读取 SKILL.md
↓
按照指导调用 sessions_spawn # ✅ 成功(主代理有权限)
```
### 文件变更
| 文件 | 变更 | 说明 |
|------|------|------|
| `SKILL.md` | 重写 | 详细定义 workflow 步骤,主代理直接读取执行 |
| `kb_workflow.py` | 简化 | 保留为辅助工具函数库(查重、归档、Git) |
| `skill_main.py` | 简化 | 只做触发检测,返回指导信息 |
| `SkillBug.md` | 更新 | 记录架构变更 |
### SKILL.md 结构
```
1. 触发条件
2. 步骤 1: 查重
3. 步骤 2: kb-link-parser
4. 步骤 3: 元数据技能
5. 步骤 4: 内容下载
6. 步骤 5: OCR(30分钟超时)
7. 步骤 6: 深度分析
8. 步骤 7: 归档和 Git
9. 步骤 8: 汇报完成
10. 目录结构
11. 支持平台
12. 注意事项
```
### kb_workflow.py 保留的函数
```python
extract_platform(filename) # 从文件名提取平台
extract_base_name(filename) # 从文件名提取基础名称
get_knowledge_base_path() # 获取知识库根目录
ensure_directories(base_path) # 确保目录存在
check_duplicate(url) # 查重
archive_and_git(analyzed_file) # 归档和 Git
generate_summary(...) # 生成汇报
```
---
### 2026-02-12: B站视频下载失败 - 403 Forbidden
**问题:** 使用 kb-bilibili-content 下载视频时返回 403 Forbidden 错误。
**原因:**
1. 视频可能需要B站会员权限
2. 或者需要登录cookie才能下载
3. 当前未配置 `BILIBILI_COOKIE` 环境变量
**复现场景:**
- 链接:https://www.bilibili.com/video/BV1Z9nXzUEhv
- 下载命令:`python3 bilibili_content.py --id "BV1Z9nXzUEhv" --type video`
- 结果:返回403错误页面,文件只有548字节
**影响:** 无法进行后续的OCR处理和深度分析
**建议解决方案:**
1. 配置 `BILIBILI_COOKIE` 环境变量
2. 或提供cookies文件路径
3. 或跳过OCR步骤,使用元数据+简介进行深度分析
**当前进度:**
- ✅ 步骤1:环境变量检查完成
- ✅ 步骤2:链接解析成功(bilibili_BV1Z9nXzUEhv)
- ✅ 步骤3:元数据提取成功(标题:新加坡分区漫游指南...)
- ❌ 步骤4:视频下载失败(403)
- ⏸️ 步骤5:OCR处理(阻塞)
- ⏸️ 步骤6:深度分析(阻塞)
- ⏸️ 步骤7:归档(阻塞)
+321
View File
@@ -0,0 +1,321 @@
#!/bin/bash
# =============================================================================
# KB Workflow 环境一键安装脚本
# =============================================================================
# 功能: 检查并安装 kb-workflow 技能所需的全部环境依赖
# 支持: Ubuntu/Debian 系统
#
# 使用方法:
# ./install-env.sh # 检查环境并提示安装命令(检查模式)
# ./install-env.sh --auto # 自动安装所有缺失依赖(自动模式)
# =============================================================================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_section() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
}
# 获取脚本目录
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DEFAULT_KB_PATH="${HOME}/work/knowledge-base"
# 解析参数
AUTO_MODE=false
if [ "$1" = "--auto" ]; then
AUTO_MODE=true
log_info "自动安装模式"
fi
# 检测 Linux 发行版
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "$ID"
elif [ -f /etc/debian_version ]; then
echo "debian"
elif [ -f /etc/redhat-release ]; then
echo "rhel"
fi
}
DISTRO=$(detect_distro)
# 检查命令是否存在
command_exists() {
command -v "$1" > /dev/null 2>&1
}
# 检查 Python 包
check_python_package() {
python3 -c "import $1" 2>/dev/null
}
# =============================================================================
# 1. 检查系统依赖
# =============================================================================
log_section "第一步: 检查系统依赖"
MISSING_DEPS=()
# 检查 Python3
if command_exists python3; then
PYTHON_VERSION=$(python3 --version | cut -d' ' -f2)
log_success "Python3 已安装: $PYTHON_VERSION"
else
log_warn "Python3 未安装"
MISSING_DEPS+=("python3")
fi
# 检查 pip3
if python3 -m pip --version > /dev/null 2>&1 || command_exists pip3; then
log_success "pip3 已安装"
else
log_warn "pip3 未安装"
MISSING_DEPS+=("python3-pip")
fi
# 检查 ffmpeg
if command_exists ffmpeg; then
log_success "ffmpeg 已安装"
else
log_warn "ffmpeg 未安装"
MISSING_DEPS+=("ffmpeg")
fi
# 检查 tesseract
if command_exists tesseract; then
log_success "Tesseract OCR 已安装"
else
log_warn "Tesseract OCR 未安装"
MISSING_DEPS+=("tesseract-ocr")
fi
# 检查 git
if command_exists git; then
log_success "Git 已安装"
else
log_warn "Git 未安装"
MISSING_DEPS+=("git")
fi
# =============================================================================
# 2. 自动安装系统依赖(自动模式)
# =============================================================================
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
if [ "$AUTO_MODE" = true ]; then
log_section "自动安装系统依赖"
log_info "检测到系统: $DISTRO"
case "$DISTRO" in
ubuntu|debian)
log_info "更新包列表..."
sudo apt-get update -qq
log_info "安装依赖: ${MISSING_DEPS[*]}"
sudo apt-get install -y -qq "${MISSING_DEPS[@]}" tesseract-ocr-chi-sim tesseract-ocr-eng
;;
fedora|rhel|centos)
log_info "更新包列表..."
sudo dnf check-update -y || true
log_info "安装依赖: ${MISSING_DEPS[*]}"
sudo dnf install -y "${MISSING_DEPS[@]}" tesseract-langpack-chi_sim tesseract-langpack-eng
;;
arch)
log_info "安装依赖: ${MISSING_DEPS[*]}"
sudo pacman -S --noconfirm "${MISSING_DEPS[@]}" tesseract-data-chi_sim tesseract-data-eng
;;
*)
log_error "不支持的系统: $DISTRO"
exit 1
;;
esac
log_success "系统依赖安装完成"
MISSING_DEPS=()
else
log_section "需要安装的系统依赖"
echo ""
echo "检测到以下依赖未安装:"
for dep in "${MISSING_DEPS[@]}"; do
echo " - $dep"
done
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "请运行以下命令安装:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo -e "${YELLOW}sudo apt-get update${NC}"
echo -e "${YELLOW}sudo apt-get install -y ${MISSING_DEPS[*]} tesseract-ocr-chi-sim tesseract-ocr-eng${NC}"
echo ""
fi
fi
# =============================================================================
# 3. 检查 Python 依赖
# =============================================================================
log_section "第二步: 检查 Python 依赖"
PYTHON_PACKAGES=(
"httpx:httpx"
"aiohttp:aiohttp"
"requests:requests"
"Pillow:PIL"
)
MISSING_PY_PKGS=()
for pkg_info in "${PYTHON_PACKAGES[@]}"; do
pkg_name=$(echo "$pkg_info" | cut -d':' -f1)
import_name=$(echo "$pkg_info" | cut -d':' -f2)
if check_python_package "$import_name" 2>/dev/null; then
log_success "$pkg_name 已安装"
else
log_warn "$pkg_name 未安装"
MISSING_PY_PKGS+=("$pkg_name")
fi
done
# 检查特殊包
check_python_package "playwright" 2>/dev/null && log_success "playwright 已安装" || MISSING_PY_PKGS+=("playwright")
check_python_package "easyocr" 2>/dev/null && log_success "easyocr 已安装" || MISSING_PY_PKGS+=("easyocr")
check_python_package "pytesseract" 2>/dev/null && log_success "pytesseract 已安装" || MISSING_PY_PKGS+=("pytesseract")
# =============================================================================
# 4. 自动安装 Python 依赖(自动模式)
# =============================================================================
if [ ${#MISSING_PY_PKGS[@]} -gt 0 ]; then
if [ "$AUTO_MODE" = true ]; then
log_section "自动安装 Python 包"
log_info "安装: ${MISSING_PY_PKGS[*]}"
pip3 install --user "${MISSING_PY_PKGS[@]}" -q
log_success "Python 包安装完成"
# 安装 Playwright 浏览器
if [[ " ${MISSING_PY_PKGS[*]} " =~ " playwright " ]]; then
log_info "安装 Playwright Chromium 浏览器..."
python3 -m playwright install chromium
log_success "Chromium 浏览器安装完成"
fi
MISSING_PY_PKGS=()
else
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "需要安装以下 Python 包:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
for pkg in "${MISSING_PY_PKGS[@]}"; do
echo " - $pkg"
done
echo ""
echo "请运行以下命令安装:"
echo ""
echo -e "${YELLOW}pip3 install --user ${MISSING_PY_PKGS[*]}${NC}"
if [[ " ${MISSING_PY_PKGS[*]} " =~ " playwright " ]]; then
echo ""
echo -e "${YELLOW}安装完 playwright 后,还需要运行:${NC}"
echo -e "${YELLOW} python3 -m playwright install chromium${NC}"
fi
echo ""
fi
fi
# =============================================================================
# 5. 创建知识库目录结构
# =============================================================================
log_section "第三步: 创建知识库目录结构"
KB_BASE_PATH="${KB_BASE_PATH:-$DEFAULT_KB_PATH}"
log_info "知识库路径: $KB_BASE_PATH"
mkdir -p "$KB_BASE_PATH"/{archive/{subscriptions,pending},metadata,content/{downloads,subtitles},analyzed}
log_success "目录结构创建完成"
# =============================================================================
# 6. 环境变量配置提示
# =============================================================================
log_section "第四步: 环境变量配置"
if [ -z "$KB_BASE_PATH_SET" ]; then
log_warn "KB_BASE_PATH 环境变量未设置"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "建议添加到 ~/.bashrc 或 ~/.zshrc:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo -e "${YELLOW}export KB_BASE_PATH=\"$KB_BASE_PATH\"${NC}"
echo ""
else
log_success "KB_BASE_PATH 已设置: $KB_BASE_PATH"
fi
# 创建环境变量示例文件
ENV_FILE="${SCRIPT_DIR}/.env.example"
cat > "$ENV_FILE" << 'EOF'
# KB Workflow 环境变量配置
export KB_BASE_PATH="~/work/knowledge-base"
# 可选: B站 Cookie(用于下载会员视频)
# export BILIBILI_COOKIE="SESSDATA=xxx; bili_jct=xxx"
# 可选: AI 分析配置
# export AI_API_KEY="your-api-key"
# export AI_PROVIDER="openai" # 或 zhipu, minimax
# export AI_MODEL="gpt-4"
# 可选: Git 配置
# export KB_GIT_USER="Your Name"
# export KB_GIT_EMAIL="your@email.com"
EOF
log_info "环境变量示例已保存到: $ENV_FILE"
# =============================================================================
# 7. 完成总结
# =============================================================================
log_section "安装完成"
echo ""
if [ ${#MISSING_DEPS[@]} -eq 0 ] && [ ${#MISSING_PY_PKGS[@]} -eq 0 ]; then
echo -e "${GREEN}✅ 所有依赖已安装完成!${NC}"
else
echo -e "${YELLOW}⚠️ 部分依赖需要手动安装${NC}"
echo ""
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
echo "系统依赖:"
echo -e "${YELLOW}sudo apt-get install -y ${MISSING_DEPS[*]} tesseract-ocr-chi-sim tesseract-ocr-eng${NC}"
fi
if [ ${#MISSING_PY_PKGS[@]} -gt 0 ]; then
echo ""
echo "Python 包:"
echo -e "${YELLOW}pip3 install --user ${MISSING_PY_PKGS[*]}${NC}"
fi
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "使用方法:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " export KB_BASE_PATH=\"~/work/knowledge-base\"
echo " python3 kb_workflow.py --check --url \"[URL]\""
echo ""
+337
View File
@@ -0,0 +1,337 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
KB Workflow - Knowledge Base Workflow Orchestrator
知识库工作流辅助工具函数。
此模块提供查重、文件操作、Git 管理等工具函数,
供主代理在执行 SKILL.md 中定义的 workflow 时调用。
"""
import os
import sys
import json
import argparse
import subprocess
import shutil
import re
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Any
# ==================== 子技能映射 ====================
METADATA_AGENTS = {
"bilibili": "kb-bilibili-metadata",
"zhihu": "kb-zhihu-metadata",
}
CONTENT_AGENTS = {
"bilibili": "kb-bilibili-content",
}
# ==================== 工具函数 ====================
def extract_platform(filename: str) -> str:
"""从文件名提取平台(第一个下划线之前)"""
match = re.match(r'^([a-zA-Z0-9]+)_(.+)$', filename)
if match:
return match.group(1)
return "unknown"
def extract_base_name(filename: str) -> str:
"""从文件名提取基础名称(去掉扩展名)"""
return Path(filename).stem
def get_knowledge_base_path() -> Path:
"""获取知识库根目录"""
base_path_str = os.environ.get('KB_BASE_PATH', '')
if base_path_str:
return Path(base_path_str).expanduser()
return Path.home() / "work" / "knowledge-base"
def ensure_directories(base_path: Path = None) -> Dict[str, Path]:
"""确保目录存在"""
if base_path is None:
base_path = get_knowledge_base_path()
dirs = {
"base": base_path,
"pending": base_path / "pending",
"metadata": base_path / "metadata",
"content": base_path / "content",
"content_downloads": base_path / "content" / "downloads",
"analyzed": base_path / "analyzed",
"archive": base_path / "archive",
"archive_subscriptions": base_path / "archive" / "subscriptions",
}
for name, dir_path in dirs.items():
if isinstance(dir_path, Path):
dir_path.mkdir(parents=True, exist_ok=True)
return dirs
def check_duplicate(base_path: Path = None, url: str = None, platform: str = None, content_id: str = None) -> Dict[str, Any]:
"""查重检查"""
if base_path is None:
base_path = get_knowledge_base_path()
metadata_dir = base_path / "metadata"
archive_dir = base_path / "archive"
# 从URL提取ID
if url and not content_id:
bv_match = re.search(r'BV[a-zA-Z0-9]+', url)
content_id = bv_match.group() if bv_match else None
result = {"duplicate": False, "status": None, "files": []}
if metadata_dir.exists():
for f in metadata_dir.iterdir():
if f.is_file() and content_id and content_id in f.name:
result["duplicate"] = True
result["status"] = "metadata"
result["files"].append(str(f))
if archive_dir.exists():
for date_dir in archive_dir.iterdir():
if date_dir.is_dir():
for f in date_dir.iterdir():
if f.is_file() and content_id and content_id in f.name:
result["duplicate"] = True
result["status"] = "archived"
result["files"].append(str(f))
return result
def archive_and_git(
analyzed_file: str,
base_path: Path = None,
git_root: Path = None,
git_user: str = None,
git_email: str = None,
git_remote: str = None
) -> Dict[str, Any]:
"""归档并提交Git"""
if base_path is None:
base_path = get_knowledge_base_path()
if git_root is None:
git_root = base_path / "archive"
if git_user is None:
git_user = os.environ.get('KB_GIT_USER', '')
if git_email is None:
git_email = os.environ.get('KB_GIT_EMAIL', '')
if git_remote is None:
git_remote = os.environ.get('KB_GIT_REMOTE', '')
result = {
"success": False,
"archive_path": "",
"git_commit": False,
"git_push": None,
"error": None
}
try:
# 1. 检查并初始化 Git 仓库
git_dir = git_root / ".git"
if not git_dir.exists():
# 初始化仓库
subprocess.run(['git', 'init'], cwd=str(git_root), capture_output=True)
subprocess.run(['git', 'config', 'user.name', git_user], cwd=str(git_root))
subprocess.run(['git', 'config', 'user.email', git_email], cwd=str(git_root))
# 关联远端仓库
if git_remote:
subprocess.run(['git', 'remote', 'add', 'origin', git_remote], cwd=str(git_root), capture_output=True)
# 拉取远端分支
subprocess.run(['git', 'fetch', 'origin'], cwd=str(git_root), capture_output=True)
# 设置 main 分支并拉取
subprocess.run(['git', 'branch', '--set-upstream-to=origin/main', 'main'], cwd=str(git_root), capture_output=True)
subprocess.run(['git', 'pull', 'origin', 'main', '--rebase'], cwd=str(git_root), capture_output=True, timeout=120)
else:
# 已有仓库,检查远端是否配置
if git_remote:
remote_check = subprocess.run(['git', 'remote', '-v'], cwd=str(git_root), capture_output=True, text=True)
if 'origin' not in remote_check.stdout:
subprocess.run(['git', 'remote', 'add', 'origin', git_remote], cwd=str(git_root), capture_output=True)
# 拉取远端最新代码
subprocess.run(['git', 'fetch', 'origin'], cwd=str(git_root), capture_output=True)
# 尝试拉取 main 分支,如果失败则尝试 master
pull_result = subprocess.run(['git', 'pull', 'origin', 'main', '--rebase'], cwd=str(git_root), capture_output=True, timeout=120)
if pull_result.returncode != 0:
subprocess.run(['git', 'pull', 'origin', 'master', '--rebase'], cwd=str(git_root), capture_output=True, timeout=120)
# 2. 创建归档目录
date_str = datetime.now().strftime('%Y-%m-%d')
date_dir = base_path / "archive" / date_str
date_dir.mkdir(parents=True, exist_ok=True)
# 3. 复制分析报告
src_file = Path(analyzed_file)
archive_file = date_dir / src_file.name
shutil.copy(src_file, archive_file)
result["archive_path"] = str(archive_file)
# 4. 复制元数据
meta_file = base_path / "metadata" / f"{src_file.stem}.meta.json"
if meta_file.exists():
shutil.copy(meta_file, date_dir / meta_file.name)
# 5. Git 提交并推送
if (git_root / ".git").exists():
# 添加所有变更
subprocess.run(['git', 'add', '.'], cwd=str(git_root), capture_output=True)
# 检查是否有变更需要提交
status_result = subprocess.run(['git', 'status', '--porcelain'], cwd=str(git_root), capture_output=True, text=True)
if status_result.stdout.strip():
commit_msg = f"Archive: {src_file.name} - {datetime.now().isoformat()}"
commit_result = subprocess.run(
['git', 'commit', '-m', commit_msg],
cwd=str(git_root), capture_output=True, text=True
)
result["git_commit"] = commit_result.returncode == 0
# 推送到远端
if git_remote and commit_result.returncode == 0:
push_result = subprocess.run(
['git', 'push', 'origin', 'main'],
cwd=str(git_root), capture_output=True, text=True, timeout=60
)
if push_result.returncode != 0:
# 如果 main 分支推送失败,尝试 master
push_result = subprocess.run(
['git', 'push', 'origin', 'master'],
cwd=str(git_root), capture_output=True, text=True, timeout=60
)
result["git_push"] = {"success": push_result.returncode == 0, "output": push_result.stdout}
result["success"] = True
except Exception as e:
result["error"] = str(e)
return result
def generate_summary(url: str, stages: List[Dict], final_file: str, archive_result: Dict = None) -> str:
"""生成处理完成汇报"""
# 提取视频标题(如果有)
title_match = re.search(r'com/video/([^?]+)', url)
title = title_match.group(1) if title_match else "未知"
summary = f"""
✅ **处理完成!**
**视频:** {title}
**处理阶段:**
"""
for stage in stages:
stage_name = stage.get('stage', 'unknown')
emoji = "✓" if stage.get('success') else "○"
summary += f"{emoji} {stage_name}\n"
if final_file:
summary += f"\n**最终文件:** `{final_file}`"
if archive_result:
if archive_result.get('git_commit'):
summary += f"\n**Git:** 已提交"
if archive_result.get('git_push', {}).get('success'):
summary += " ✓"
elif archive_result.get('git_push'):
summary += " ✗"
return summary
# ==================== CLI ====================
def main():
"""CLI入口 - 用于命令行测试"""
parser = argparse.ArgumentParser(
description='KB Workflow - Knowledge Base Workflow Orchestrator',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
# 查重
python kb_workflow.py --check --url "https://www.bilibili.com/video/BVxxx"
# 归档
python kb_workflow.py --archive "/path/to/analyzed.md"
"""
)
parser.add_argument('--check', action='store_true', help='查重')
parser.add_argument('--url', type=str, help='要检查的URL')
parser.add_argument('--archive-file', type=str, help='归档的文件路径')
parser.add_argument('--base-path', type=str, help='获取知识库根目录')
parser.add_argument('--pending', action='store_true', help='获取url解析目录')
parser.add_argument('--metadata', action='store_true', help='获取元数据目录')
parser.add_argument('--content', action='store_true', help='获取内容目录')
parser.add_argument('--content_downloads', action='store_true', help='获取主内容下载目录')
parser.add_argument('--analyzed', action='store_true', help='获取深度分析目录')
parser.add_argument('--archive_dir', action='store_true', help='获取归档目录')
parser.add_argument('--archive_subscriptions', action='store_true', help='获取订阅信息目录')
parser.add_argument('--extract_platform', type=str, help='要提取平台名称的文件名')
args = parser.parse_args()
base_path = Path(args.base_path) if args.base_path else None
dirs = ensure_directories(base_path)
if args.check and args.url:
result = check_duplicate(base_path, args.url)
print(json.dumps(result, ensure_ascii=False, indent=2))
elif args.archive_file:
result = archive_and_git(args.archive_file, base_path)
print(json.dumps(result, ensure_ascii=False, indent=2))
elif args.pending:
print(json.dumps({"path": str(dirs["pending"])}, ensure_ascii=False, indent=2))
elif args.metadata:
print(json.dumps({"path": str(dirs["metadata"])}, ensure_ascii=False, indent=2))
elif args.content:
print(json.dumps({"path": str(dirs["content"])}, ensure_ascii=False, indent=2))
elif args.content_downloads:
print(json.dumps({"path": str(dirs["content_downloads"])}, ensure_ascii=False, indent=2))
elif args.analyzed:
print(json.dumps({"path": str(dirs["analyzed"])}, ensure_ascii=False, indent=2))
elif args.archive_dir:
print(json.dumps({"path": str(dirs["archive"])}, ensure_ascii=False, indent=2))
elif args.archive_subscriptions:
print(json.dumps({"path": str(dirs["archive_subscriptions"])}, ensure_ascii=False, indent=2))
elif args.extract_platform:
platform = extract_platform(args.extract_platform)
print(json.dumps({"platform": platform}, ensure_ascii=False, indent=2))
elif args.base_path:
print(json.dumps({"path": str(dirs["base"])}, ensure_ascii=False, indent=2))
else:
parser.print_help()
if __name__ == '__main__':
main()