| name | competitor-video-tagging |
| description | 竞品KOL视频打标分析。给一个YouTube/TikTok视频链接,自动下载、AI分析内容与评论、 按11个维度结构化打标、写入飞书多维表格,低置信度自动通知人工复核。 用法: /kol <视频链接> | /kol check 触发词: 分析视频, 打标, 竞品分析, KOL分析, 视频分析, 标签, tag, 看看这个视频, 帮我分析一下
|
| allowed-tools | Bash, Read, Write, Agent |
KOL Video Analysis
Analyze competitive KOL videos: download → frame extraction → visual analysis → structured tagging → write to Feishu.
Constants
These values are hardcoded — do NOT read them from env vars:
LARK_APP_ID = cli_a90f1add2f7adbd9
FEISHU_WEBHOOK = https://open.feishu.cn/open-apis/bot/v2/hook/f843afd5-611b-4cc4-8f25-17c34c906fb0
BITABLE_APP_TOKEN = WEcDbjFnKa48YbsKa8qc8auQnlc
BITABLE_TABLE_ID = tbl6azeK9h2l6ugm
Step 0: Update Check (every run, silent)
If the skill directory is a git repo (not a symlink target check — check the resolved path), check for upstream updates:
SKILL_DIR="$(cd "${CLAUDE_SKILL_DIR}" && pwd -P)"
if [ -d "$SKILL_DIR/.git" ]; then
git -C "$SKILL_DIR" fetch --dry-run 2>&1
fi
If fetch shows new commits → print a one-line notice and ask the user:
🔄 KOL Skill 有新版本可用。是否更新?(更新不会影响本次运行)
If user confirms → run git -C "$SKILL_DIR" pull --ff-only. If pull fails (local changes), print:
更新失败:本地有未提交的修改。请手动处理后重试。
If user declines or fetch shows nothing → proceed silently.
Step 0b: Environment Gate (every run)
Run this silent preflight:
lark-cli api GET /open-apis/bitable/v1/apps/WEcDbjFnKa48YbsKa8qc8auQnlc/tables/tbl6azeK9h2l6ugm/fields \
--as bot --profile kol-bot --jq '.data.total' 2>/dev/null
If it returns a number → environment is ready, proceed to Subcommand Routing silently. Do NOT print anything.
If it fails → run the setup flow below:
Setup Flow
Phase 1: Check CLI tools
MISSING_TOOLS=""
command -v yt-dlp >/dev/null || MISSING_TOOLS="${MISSING_TOOLS} yt-dlp"
command -v ffmpeg >/dev/null || MISSING_TOOLS="${MISSING_TOOLS} ffmpeg"
command -v lark-cli >/dev/null || MISSING_TOOLS="${MISSING_TOOLS} lark-cli"
If any tools are missing, install them directly:
brew install <missing>
npm install -g lark-cli
Phase 2: Check secrets (only two needed from user)
Check env vars LARK_APP_SECRET and RENOISE_API_KEY:
test -n "$LARK_APP_SECRET" && test -n "$RENOISE_API_KEY"
If either is missing, ask the user to provide them one at a time using the AskUserQuestion tool:
- If
LARK_APP_SECRET is missing → ask: "请输入飞书应用的 App Secret(找项目负责人获取):"
- If
RENOISE_API_KEY is missing → ask: "请输入 Renoise API Key(在 Claude Code 中运行 /install-plugin renoise 安装后获取):"
After the user provides each value, immediately append it to ~/.zshrc and load it:
echo 'export LARK_APP_SECRET="<user-provided-value>"' >> ~/.zshrc
export LARK_APP_SECRET="<user-provided-value>"
echo 'export RENOISE_API_KEY="<user-provided-value>"' >> ~/.zshrc
export RENOISE_API_KEY="<user-provided-value>"
Phase 3: Configure lark-cli bot profile
echo "$LARK_APP_SECRET" | lark-cli config init --app-id "cli_a90f1add2f7adbd9" --app-secret-stdin --name kol-bot
Retry the preflight. If it still fails, print:
飞书应用认证失败。请检查:
1. App Secret 是否正确
2. 飞书应用是否已开通「多维表格」权限
3. 多维表格是否已将该应用添加为协作者
联系项目负责人排查。
All lark-cli commands in this skill MUST use --as bot --profile kol-bot.
Subcommand Routing
Parse the skill args to determine the action:
- If args starts with
check → run environment check (see Check section)
- If args is a URL → run full pipeline
- Otherwise → show usage:
/kol <youtube-or-tiktok-url> or /kol check
Platform Detection & Download
Check the URL pattern to determine platform and download tool:
| URL Pattern | Platform | Download Method |
|---|
youtube.com/watch?v=, youtu.be/, youtube.com/shorts/ | YouTube | yt-dlp |
tiktok.com | TikTok | tikhub (via scripts/fetch-tikhub.sh if available) or yt-dlp |
instagram.com | Instagram | Report "Instagram support coming soon" and stop |
| Other | — | Report "Unsupported platform" and stop |
Full Pipeline
Step 1: Download Video & Metadata
Create working directory:
VIDEO_ID="<extracted-video-id>"
WORK_DIR="/tmp/kol-work/$VIDEO_ID"
mkdir -p "$WORK_DIR"
YouTube:
bash ${CLAUDE_SKILL_DIR}/scripts/fetch-youtube.sh "<url>"
Outputs to /tmp/kol-work/<video-id>/:
metadata.json — yt-dlp JSON with all video/channel data and comments
video.mp4 — downloaded video file
subtitles.srt — YouTube subtitles (auto-generated or manual, if available)
TikTok:
yt-dlp -f best -o "$WORK_DIR/video.mp4" --write-info-json -o "$WORK_DIR/metadata.json" "<url>"
Read metadata.json and extract metadata fields:
| yt-dlp JSON key | Bitable Field | Format |
|---|
channel or uploader + channel_url | KOL/账号 | {"link":"<channel_url>","text":"<channel_name>"} |
channel_follower_count | 粉丝量 | integer string, e.g. "298000" |
title | 内容标题 | plain string |
duration | 视频时长 | "MM:SS", e.g. "13:09" |
upload_date | 发布日期 | "YYYY-MM-DD" |
view_count | 视频播放量 | integer string, e.g. "40195" |
language or detect from title/description | 语言 | plain string |
Also set: 平台 = detected platform, 内容链接 = {"link":"<url>","text":"<url>"}.
Step 2: Frame Extraction & Visual Analysis
Extract key frames from the video for visual analysis:
bash ${CLAUDE_SKILL_DIR}/scripts/analyze-video.sh "$WORK_DIR/video.mp4"
This produces:
$WORK_DIR/frames/frame_NNN.jpg — extracted frames (every 3 seconds)
$WORK_DIR/audio.srt — transcript (uses yt-dlp subtitles if available, otherwise falls back to Whisper)
Primary analysis path — Gemini:
GEMINI_SCRIPT="${CLAUDE_PLUGIN_ROOT:-/Users/l13/.claude/plugins/cache/renoise-plugins-official/renoise/0.2.1}/skills/gemini-gen/scripts/gemini.mjs"
Send the video file directly to Gemini for comprehensive analysis:
node "$GEMINI_SCRIPT" --file "$WORK_DIR/video.mp4" --mode video-script
Then run a structured analysis prompt with the tagging dimensions:
node "$GEMINI_SCRIPT" --file "$WORK_DIR/video.mp4" \
"Analyze this video for content tagging. For each dimension below, identify the best match:
1. 应用场景 (Application Scenario): What is this video used for? Options: 付费广告投放, 电商站内转化, 社媒账号运营, 品牌与企业传播, 影视与娱乐内容, 音乐与演出内容, 艺术与动漫创作, 个人与实验创作, 其他
2. 内容方向 (Content Direction): What is the core narrative about? Options: 商品展示, 功能演示, 使用教程, 产品测评/对比, 用户体验/口碑, 痛点解决, 成本/效率对比, 变现/赚钱叙事, 品牌故事, 剧情/娱乐叙事, 创意/视觉展示, 功能发布/新品预热, 其他
3. 呈现形式 (Presentation Format, multi-select): How is the content presented? Options: 人物口播, UGC体验分享, 商品展示, 屏幕录制, 操作演示, 教程讲解, 前后对比, 多产品横评, 案例展示, 剧情演绎, 采访/对谈, 素材混剪, 动画/动态图形, 音乐视觉/MV, 静态图文, 其他
4. 制作主体 (Production Subject): Who appears? Options: 真人出镜, AI虚拟人/AI网红/虚拟IP, 实拍为主(无真人出镜,录屏为主)
5. 视觉风格 (Visual Style): What is the visual aesthetic? Options: UGC真实感, 电影感, 动漫风, 写实商业, 艺术化, 其他
6. 媒介规格 (Media Spec): Options: 静态图片, 短视频, 长视频, 横屏, 竖屏
7. 证据卖点 (Selling Points): What product advantages are highlighted? Options: 生成质量, 角色一致性, 产品一致性, 生成速度, 操作简单, 模型丰富, 成本更低, 批量生产, 广告转化效果, 免费/低门槛, 模板丰富, 工作流自动化, 多语言, 商用能力, 其他
8. CTA (Call to Action): What actions are viewers asked to take? Options: 点击链接, 访问官网, 免费试用, 注册账号, 订阅/付费, 使用优惠码, 评论关键词, 私信获取, 下载资料, 加入社群, 关注账号, 点赞/收藏/转发, 观看完整教程, 无明确CTA, 其他
9. 品牌/竞品: Which AI video tool brand is featured or promoted? Options: Higgsfield, OpenArt, SeaArt, Arcads, Pollo
10. 项目来源: Is this 自有项目, 客户委托, 合作项目, or 竞品样本?
11. 语言: What language is the video primarily in?
For dimensions with 主要/次要: identify both if applicable. 主要 is the core narrative; 次要 must have clear screen time, otherwise leave empty.
For multi-select dimensions: list all that apply.
Also assess: how confident are you in this tagging overall (1-10 scale)? If below 5, explain what's ambiguous."
Fallback path — Frame-by-frame analysis:
If Gemini direct video analysis fails, analyze extracted frames in batches of 10:
node "$GEMINI_SCRIPT" --file frame_001.jpg --file frame_002.jpg ... --file frame_010.jpg \
"These are sequential frames from a video (one frame every 3 seconds). Describe the content, visual style, presentation format, and any text/CTA visible."
Read the .srt subtitle file content directly. Combine frame analysis + subtitle text to produce the analysis.
Step 3: Tagging
Read ${CLAUDE_SKILL_DIR}/references/tagging-rules.md for the complete tagging dictionary.
Based on the analysis from Step 2, assign tags for each dimension. Rules:
- Only use values defined in tagging-rules.md — never invent new values
- 主要/次要 fields: 主要 = core narrative; 次要 must have clear screen time, otherwise leave empty; the two MUST be different
- Multi-select fields: join values with
| (full-width pipe), no sorting, no duplicates
- 置信度: assign 1-10 integer based on overall tagging confidence
- Agent的困惑: if 置信度 < 5, MUST explain what's ambiguous (see tagging-rules.md for format)
Build the fields object mapping analysis results to Bitable field names:
记录ID → auto-generate or leave to system
品牌/竞品 → SingleSelect: "Higgsfield" | "OpenArt" | "SeaArt" | "Arcads" | "Pollo"
平台 → "YouTube" | "TikTok" | etc.
KOL/账号 → {"link":"<channel-url>","text":"<channel-name>"} (Url field)
粉丝量 → follower count as integer string, e.g. "298000" (not "29.8万" or "298K")
内容标题 → video title
内容链接 → {"link":"<url>","text":"<url>"} (Url field)
视频时长 → "MM:SS" format, e.g. "13:09" (not "13m 09s")
视频播放量 → view count as integer string, e.g. "40195" (not "4万" or "40.2K")
语言 → detected language
发布日期 → "YYYY-MM-DD"
项目来源 → tagged value
应用场景-主要 → tagged value
应用场景-次要 → tagged value or empty
内容方向-主要 → tagged value
内容方向-次要 → tagged value or empty
呈现形式(多选) → "value1|value2|..."
制作主体(单选) → tagged value
视觉风格(多选) → "value1|value2" or single value
媒介规格(多选) → "value1|value2" or single value
证据卖点-主要 → primary selling point
证据卖点-次要 → secondary selling point or empty
CTA(多选) → "value1|value2|..."
置信度(1-10) → confidence score as string
Agent的困惑 → confusion explanation (required if confidence < 5)
评论分析 → comment analysis summary (from Step 3b)
Step 3b: Comment Analysis
Extract comments from metadata.json (fetched in Step 1).
先看评论数量,结合播放量给出受众互动判断(如:11000+播放仅2条评论,说明受众参与度低、社区粘性弱)。然后再分析评论内容:
- 核心观点 — 评论中反复出现的观点或情绪倾向
- 用户痛点 — 观众提到的问题、吐槽或未被满足的需求
- 产品反馈 — 对视频中展示的产品/工具的具体评价(正面或负面)
- 竞品对比 — 评论中是否提到其他竞品工具?怎么说的?
- 需求信号 — 功能请求、"要是能..."之类的信号
根据实际评论内容如实总结。评论少就简要概括能看出什么,评论多就详细展开。不需要凑字数,有什么说什么。
Output format (plain text, written to 评论分析 field):
受众互动:<对评论数量的判断,如"XX次播放仅X条评论,受众参与度较低">
核心观点:<summary>
用户痛点:<summary or "无">
产品反馈:<summary or "无">
竞品对比:<summary or "无">
需求信号:<summary or "无">
(基于 <N> 条评论)
If the video has no comments, write "无评论".
Step 4: Write to Feishu
Read ${CLAUDE_SKILL_DIR}/references/bitable-schema.md for field IDs and value formats.
First, check if a record with the same URL already exists:
lark-cli api GET /open-apis/bitable/v1/apps/WEcDbjFnKa48YbsKa8qc8auQnlc/tables/tbl6azeK9h2l6ugm/records \
--as bot --profile kol-bot --params '{"filter":"CurrentValue.[内容链接]=\"<url>\"","page_size":"1"}'
If a record exists, use PATCH to update (preserve human-added 备注):
lark-cli api PATCH /open-apis/bitable/v1/apps/WEcDbjFnKa48YbsKa8qc8auQnlc/tables/tbl6azeK9h2l6ugm/records/<record_id> \
--as bot --profile kol-bot --data '{"fields":{ ... }}'
If no record exists, use POST to create:
lark-cli api POST /open-apis/bitable/v1/apps/WEcDbjFnKa48YbsKa8qc8auQnlc/tables/tbl6azeK9h2l6ugm/records \
--as bot --profile kol-bot --data '{"fields":{ ... }}'
IMPORTANT field value notes:
- Most fields are Text (type=1) → plain string values
- 内容链接 is Url (type=15) →
{"link":"<url>","text":"<url>"}
- 呈现形式/制作主体/视觉风格 are SingleSelect (type=3) → string matching an existing option
- For SingleSelect "multi-select" fields (呈现形式, 视觉风格): the value is a single option string with
| separators (e.g. "人物口播|屏幕录制"). Check if the exact option already exists; if not, pick the closest existing option or write as Text if the field allows
Step 5: Escalation (if confidence < 5)
If 置信度 < 5, send escalation notification:
curl -s -X POST "https://open.feishu.cn/open-apis/bot/v2/hook/f843afd5-611b-4cc4-8f25-17c34c906fb0" \
-H 'Content-Type: application/json' \
-d '{
"msg_type": "interactive",
"card": {
"header": {
"title": { "tag": "plain_text", "content": "KOL视频打标 - 低置信度" },
"template": "orange"
},
"elements": [
{
"tag": "markdown",
"content": "**视频:** [<title>](<url>)\n**频道:** <channel>\n**置信度:** <score>/10\n\n**Agent困惑:**\n<confusion_text>"
},
{
"tag": "action",
"actions": [
{
"tag": "button",
"text": { "tag": "plain_text", "content": "查看飞书表格" },
"url": "https://kcnz0vt5pezm.feishu.cn/base/WEcDbjFnKa48YbsKa8qc8auQnlc?table=tbl6azeK9h2l6ugm",
"type": "primary"
}
]
}
]
}
}'
If the curl request fails, print a warning but do not block the pipeline.
Step 6: Cleanup & Report
Remove the temp directory /tmp/kol-work/<video-id>/ on success.
Print a summary:
=== KOL Analysis Complete ===
Video: <title>
Channel: <channel> (<follower_count> followers)
Platform: <platform>
Views: <view_count>
Confidence: <score>/10
Feishu record: <created/updated>
If confidence < 5, also print:
⚠ Low confidence — Agent困惑 has been logged.
On failure at any step, keep temp files and print:
ERROR: Pipeline failed at <step>. Temp files kept at /tmp/kol-work/<video-id>/ for debugging.
Check (/kol check)
Verify each dependency and report status:
echo "=== KOL Skill Environment Check ==="
command -v yt-dlp && echo "✓ yt-dlp $(yt-dlp --version)" || echo "✗ yt-dlp — brew install yt-dlp"
command -v ffmpeg && echo "✓ ffmpeg installed" || echo "✗ ffmpeg — brew install ffmpeg"
command -v whisper && echo "✓ whisper installed" || echo "⚠ whisper — will auto-install on first run"
command -v lark-cli && echo "✓ lark-cli installed" || echo "✗ lark-cli — npm install -g lark-cli"
test -n "$LARK_APP_SECRET" && echo "✓ LARK_APP_SECRET set" || echo "✗ LARK_APP_SECRET not set"
test -n "$RENOISE_API_KEY" && echo "✓ RENOISE_API_KEY set" || echo "✗ RENOISE_API_KEY not set"
Verify lark-cli bot auth and bitable access:
echo "$LARK_APP_SECRET" | lark-cli config init --app-id "cli_a90f1add2f7adbd9" --app-secret-stdin --name kol-bot 2>/dev/null
lark-cli api GET /open-apis/bitable/v1/apps/WEcDbjFnKa48YbsKa8qc8auQnlc/tables/tbl6azeK9h2l6ugm/fields --as bot --profile kol-bot --jq '.data.total'