From cf7cbd2ad0adaddf245bf5d696587b3b77f45991 Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Fri, 13 Mar 2026 11:03:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20kb-deep-analyzer=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E7=B1=BB=E5=9E=8B=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 --type article 参数 - 文章类型生成 300 字摘要 + 完整原文 - 视频类型保持原有时间轴分析逻辑 - 更新相关文档 🤖 Generated by AI --- .planning/article-analysis-plan.md | 99 ++++++++++++++++++++++++ kb-deep-analyzer/SKILL.md | 30 +++++--- kb-deep-analyzer/deep_analyzer.py | 117 ++++++++++++++++++++++++----- kb-workflow/SKILL.md | 23 +++++- 4 files changed, 237 insertions(+), 32 deletions(-) create mode 100644 .planning/article-analysis-plan.md diff --git a/.planning/article-analysis-plan.md b/.planning/article-analysis-plan.md new file mode 100644 index 0000000..42acc07 --- /dev/null +++ b/.planning/article-analysis-plan.md @@ -0,0 +1,99 @@ +# kb-deep-analyzer 文章支持 - Implementation Plan + +**Goal:** 让 kb-deep-analyzer 对文章内容生成 300 字摘要 + 原文 + 元数据,而非视频的时间轴分析 +**Approach:** 在 deep_analyzer.py 中根据 `--type` 参数选择不同的 prompt 模板 +**Estimated Total Time:** 30 分钟 + +## Checkpoint 1: 修改 deep_analyzer.py 核心逻辑 (~15 min) + +- [ ] Task 1.1: 修改 `_build_prompt` 方法签名,添加 `content_type` 参数 (~2 min) + - **Action:** 在方法签名中添加 `content_type: str = "video"` + - **Verify:** 方法签名正确 + +- [ ] Task 1.2: 提取视频 prompt 为 `_build_video_prompt` 方法 (~3 min) + - **Action:** 将现有逻辑移到独立方法 + - **Verify:** 原有视频分析功能不受影响 + +- [ ] Task 1.3: 新增 `_build_article_prompt` 方法 (~5 min) + - **Action:** 创建文章专用 prompt: + - 300 字以内摘要,描述核心观点 + - 主要主题(不超过 5 个) + - 标签(3-5 个) + - **不包含时间轴** + - **Verify:** prompt 模板符合需求 + +- [ ] Task 1.4: 修改 `_build_prompt` 根据 `content_type` 分发 (~2 min) + - **Action:** + ```python + if content_type == "article": + return self._build_article_prompt(...) + else: + return self._build_video_prompt(...) + ``` + - **Verify:** 类型判断正确 + +- [ ] Task 1.5: 修改 `analyze` 方法,传递 `content_type` (~2 min) + - **Action:** 将 content_type 传递到 _build_prompt + - **Verify:** 调用链正确 + +- [ ] Task 1.6: 修改 `main` 函数,传递 type 参数 (~1 min) + - **Action:** `analyzer.analyze(metadata, content, args.type)` + - **Verify:** 命令行参数正确传递 + +## Checkpoint 2: 修改输出格式逻辑 (~5 min) + +- [ ] Task 2.1: 文章输出时保留原文 (~3 min) + - **Action:** 在文章类型分析完成后,追加原文内容 + - **格式:** + ```markdown + ## 摘要 + [AI 生成的 300 字摘要] + + ## 主要主题 + - 主题1 + - 主题2 + + ## 原文 + [完整的文章原文] + ``` + - **Verify:** 输出格式正确 + +- [ ] Task 2.2: 调整 frontmatter 生成逻辑 (~2 min) + - **Action:** 文章类型不包含 duration 字段 + - **Verify:** frontmatter 格式正确 + +## Checkpoint 3: 更新文档 (~5 min) + +- [ ] Task 3.1: 更新 kb-deep-analyzer/SKILL.md (~3 min) + - **Action:** 添加 `--type article` 的使用说明 + - **Verify:** 文档清晰 + +- [ ] Task 3.2: 确认 kb-workflow/SKILL.md 已正确传递 type 参数 (~2 min) + - **Action:** 检查步骤 6 的命令是否包含 `--type` 参数 + - **Verify:** workflow 文档正确 + +## Checkpoint 4: 测试验证 (~5 min) + +- [ ] Task 4.1: 单元测试 - 视频类型不受影响 (~2 min) + - **Action:** 确认 `--type video` 行为不变 + - **Verify:** 无回归 + +- [ ] Task 4.2: 集成测试 - 文章类型输出正确 (~3 min) + - **Action:** 用一篇知乎文章测试 `--type article` + - **Verify:** 输出包含摘要 + 原文,无时间轴 + +## Verification Criteria + +- [ ] `python deep_analyzer.py --type video` 行为与之前一致 +- [ ] `python deep_analyzer.py --type article` 生成 300 字摘要 + 原文 +- [ ] 文章输出无"时间轴"部分 +- [ ] frontmatter 正确(文章无 duration) +- [ ] SKILL.md 文档已更新 + +## Execution Options + +**1. Single-Agent (this session)** - 我在本会话中顺序执行任务,每个 checkpoint 汇报 + +**2. Dispatch Sub-Agent** - 启动子代理执行开发任务 + +选择哪种方式? diff --git a/kb-deep-analyzer/SKILL.md b/kb-deep-analyzer/SKILL.md index f421c76..0977cab 100644 --- a/kb-deep-analyzer/SKILL.md +++ b/kb-deep-analyzer/SKILL.md @@ -1,27 +1,35 @@ # KB Deep Analyzer Skill -**kb-deep-analyzer** - AI-powered deep content analysis. +**kb-deep-analyzer** - AI 深度内容分析工具。 ## Features -- Multi-type analysis (tutorial, documentation, news) -- Template-based output -- Quality scoring -- Automatic tagging +- 多类型分析:视频(video)、文章(article) +- 模板化输出 +- 质量评分 +- 自动标签 - **多格式元数据支持**: 支持扁平结构 `{"title":"xxx"}` 和嵌套结构 `{"data":{"metadata":{...}}}` +## 内容类型 + +| 类型 | 参数 | 输出格式 | +|------|------|----------| +| 视频 | `--type video` | 摘要 + 主题 + 见解 + 时间轴 | +| 文章 | `--type article` | 300字摘要 + 主题 + 原文 | + ## Commands ```bash -# 分析内容(推荐:使用 -u 参数强制无缓冲输出) -AI_API_KEY="your-api-key" AI_PROVIDER="zhipu" AI_MODEL="glm-4.7" \ - python3 -u deep_analyzer.py --metadata meta.json --content content.json --output analysis.md +# 分析视频(默认) +AI_API_KEY="your-api-key" AI_PROVIDER="zhipu" AI_MODEL="glm-5" \ + python3 -u deep_analyzer.py --metadata meta.json --content content.json --output analysis.md --type video -# 指定类型 -python deep_analyzer.py --metadata meta.json --content content.json --type tutorial --output tutorial.md +# 分析文章(300字摘要 + 原文) +AI_API_KEY="your-api-key" AI_PROVIDER="zhipu" AI_MODEL="glm-5" \ + python3 -u deep_analyzer.py --metadata meta.json --content content.json --output analysis.md --type article # 指定 AI 提供商 -python deep_analyzer.py -m meta.json -c content.json -o out.md --provider zhipu --model glm-4 +python3 -u deep_analyzer.py -m meta.json -c content.json -o out.md --type video --provider zhipu --model glm-5 ``` ## 环境变量配置 diff --git a/kb-deep-analyzer/deep_analyzer.py b/kb-deep-analyzer/deep_analyzer.py index 057289a..1c1342b 100644 --- a/kb-deep-analyzer/deep_analyzer.py +++ b/kb-deep-analyzer/deep_analyzer.py @@ -29,6 +29,7 @@ class AnalysisResult: key_insights: List[str] = field(default_factory=list) recommended_tags: List[str] = field(default_factory=list) full_analysis: str = "" + original_text: str = "" # 文章类型的原文内容 # ============== AI 客户端 ============== @@ -129,8 +130,14 @@ class DeepAnalyzer: def __init__(self, ai_client: SimpleAIClient = None): self.ai_client = ai_client or SimpleAIClient() - def analyze(self, metadata: Dict[str, Any], content: Dict[str, Any]) -> AnalysisResult: - """一次性调用 AI 生成完整分析报告""" + def analyze(self, metadata: Dict[str, Any], content: Dict[str, Any], content_type: str = "video") -> AnalysisResult: + """一次性调用 AI 生成完整分析报告 + + Args: + metadata: 元数据字典 + content: 内容字典 + content_type: 内容类型,"video" 或 "article" + """ # 标准化元数据 metadata = self._normalize_metadata(metadata) @@ -141,18 +148,15 @@ class DeepAnalyzer: pubtime = metadata.get('pubtime', '') url = metadata.get('url', '') bvid = metadata.get('bvid', '') + platform = metadata.get('platform', 'bilibili') # 获取内容 - 支持多种字段名 text = content.get('text', '') or content.get('subtitle', '') or content.get('subtitles', '') or content.get('description', '') print(f"[DeepAnalyzer] 开始分析: {title}") - print(f"[DeepAnalyzer] 时长: {duration}秒, 内容长度: {len(text)} 字符") + print(f"[DeepAnalyzer] 类型: {content_type}, 内容长度: {len(text)} 字符") - # 计算分片数(按10分钟分片) - num_chunks = max(1, (duration + 599) // 600) # 向上取整到10分钟 - print(f"[DeepAnalyzer] 将分 {num_chunks} 段分析") - - # 构建 prompt + # 构建 prompt(根据类型选择不同模板) prompt = self._build_prompt( title=title, duration=duration, @@ -161,8 +165,9 @@ class DeepAnalyzer: pubtime=pubtime, url=url, bvid=bvid, + platform=platform, text=text, - num_chunks=num_chunks + content_type=content_type ) # 调用 AI @@ -173,13 +178,29 @@ class DeepAnalyzer: raise # 解析结果 - result = self._parse_analysis(full_analysis, text, duration) + result = self._parse_analysis(full_analysis, text, duration, content_type) + + # 文章类型:追加原文 + if content_type == "article": + result.original_text = text return result def _build_prompt(self, title: str, duration: int, duration_str: str, author: str, - pubtime: str, url: str, bvid: str, text: str, num_chunks: int) -> str: - """构建分析 prompt""" + pubtime: str, url: str, bvid: str, platform: str, text: str, + content_type: str = "video") -> str: + """构建分析 prompt,根据内容类型选择不同模板""" + if content_type == "article": + return self._build_article_prompt(title, author, pubtime, url, platform, text) + else: + return self._build_video_prompt(title, duration, duration_str, author, pubtime, url, bvid, platform, text) + + def _build_video_prompt(self, title: str, duration: int, duration_str: str, author: str, + pubtime: str, url: str, bvid: str, platform: str, text: str) -> str: + """构建视频分析 prompt""" + + # 计算分片数(按10分钟分片) + num_chunks = max(1, (duration + 599) // 600) # 截取适当长度的文本(20-30K 字符) max_chars = 30000 @@ -195,7 +216,7 @@ class DeepAnalyzer: 作者:{author} 时长:{duration_str}({duration}秒) 发布日期:{pubtime} -平台:bilibili +平台:{platform} URL:{url} 【内容】(完整字幕) @@ -240,7 +261,56 @@ URL:{url} return prompt - def _parse_analysis(self, analysis_text: str, original_text: str, duration: int) -> AnalysisResult: + def _build_article_prompt(self, title: str, author: str, pubtime: str, + url: str, platform: str, text: str) -> str: + """构建文章分析 prompt""" + + # 文章保留完整内容(不截断,因为需要追加原文) + # 但 prompt 中只取前 20000 字符供 AI 分析 + max_chars = 20000 + if len(text) > max_chars: + text_sample = text[:max_chars] + "\n\n[...内容过长已截断,但原文会完整保留...]" + else: + text_sample = text + + prompt = f"""你是一个专业的文章内容分析师。请根据以下信息生成摘要: + +【文章信息】 +标题:{title} +作者:{author} +发布日期:{pubtime} +平台:{platform} +URL:{url} + +【内容】 +{text_sample} + +【输出要求】必须使用以下格式: + +## 摘要 +[300字以内的摘要,描述文章的核心观点和主要内容] + +## 主要主题 +[从内容中提取不超过5个主题,每个不超过10字] +- 主题1 +- 主题2 +... + +## 标签 +[从内容中提取3-5个精炼标签,每个不超过10字] +- 标签1 +- 标签2 +... + +【重要】 +1. 摘要应精炼概括文章核心观点,让读者快速了解文章主旨 +2. 不要输出时间轴 +3. 不要输出关键见解(除非是特别重要的观点)""" + + return prompt + + def _parse_analysis(self, analysis_text: str, original_text: str, duration: int, + content_type: str = "video") -> AnalysisResult: """解析 AI 返回的分析结果""" result = AnalysisResult() result.full_analysis = analysis_text @@ -363,7 +433,7 @@ def load_json(file_path: str) -> Dict: return json.load(f) -def generate_frontmatter(metadata: Dict, result: AnalysisResult) -> str: +def generate_frontmatter(metadata: Dict, result: AnalysisResult, content_type: str = "video") -> str: """生成 Markdown frontmatter""" # 提取标签 tags = result.recommended_tags or [] @@ -378,11 +448,16 @@ def generate_frontmatter(metadata: Dict, result: AnalysisResult) -> str: fm = ["---", f"title: \"{metadata.get('title', '未命名')}\"", f"date: {date_str}", - f"duration: {metadata.get('duration_str', metadata.get('duration', 0))}", f"author: {metadata.get('author', '未知')}", - f"bvid: {metadata.get('bvid', '')}", f"platform: {metadata.get('platform', 'bilibili')}"] + # 视频类型添加 duration 和 bvid + if content_type == "video": + fm.insert(3, f"duration: {metadata.get('duration_str', metadata.get('duration', 0))}") + bvid = metadata.get('bvid', '') + if bvid: + fm.insert(5, f"bvid: {bvid}") + # 添加 URL url = metadata.get('url', '') if url: @@ -454,7 +529,7 @@ def main(): # 执行分析 print(f"[Main] 开始分析...") - result = analyzer.analyze(metadata, content) + result = analyzer.analyze(metadata, content, args.type) # 使用 AI 生成的原始内容(保留 Markdown 格式) full_analysis = result.full_analysis @@ -464,6 +539,10 @@ def main(): full_analysis = re.sub(r'(## ?)?标签\n[\s\S]*?(?=\n## ?|\n时间轴|---|\Z)', '', full_analysis) # 清理多余的空行 full_analysis = re.sub(r'\n{3,}', '\n\n', full_analysis) + + # 文章类型:追加原文 + if args.type == "article" and result.original_text: + full_analysis = full_analysis.rstrip() + "\n\n## 原文\n\n" + result.original_text # 写入输出 os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True) @@ -471,7 +550,7 @@ def main(): with open(args.output, 'w', encoding='utf-8') as f: # 添加 frontmatter if not args.no_frontmatter: - fm = generate_frontmatter(metadata, result) + fm = generate_frontmatter(metadata, result, args.type) f.write(fm + "\n\n") # 写入分析内容 diff --git a/kb-workflow/SKILL.md b/kb-workflow/SKILL.md index 8859300..e88bb8a 100644 --- a/kb-workflow/SKILL.md +++ b/kb-workflow/SKILL.md @@ -332,10 +332,24 @@ python3 kb_workflow.py --analyzed 4. 如果没有官方字幕,使用 Whisper 识别的结果 5. 启动子代理调用 kb-deep-analyzer(使用 g5 模型): + +**根据内容类型选择 --type 参数:** +- 视频(video/zvideo):`--type video` +- 文章(article/zhuanlan/question):`--type article` + ```bash -python3 /path/to/deep_analyzer.py --metadata [meta_path] --content [content_path] --output [analyzed_dir]/[filename].md --provider zhipu --model glm-5 +# 视频 +python3 /path/to/deep_analyzer.py --metadata [meta_path] --content [content_path] --output [analyzed_dir]/[filename].md --type video --provider zhipu --model glm-5 + +# 文章 +python3 /path/to/deep_analyzer.py --metadata [meta_path] --content [content_path] --output [analyzed_dir]/[filename].md --type article --provider zhipu --model glm-5 ``` +**文章类型输出格式:** +- 300 字以内的摘要(核心观点) +- 主要主题 +- 完整原文 + **环境变量设置**(在子代理中设置): - `AI_PROVIDER=zhipu` - `AI_MODEL=glm-5` @@ -381,7 +395,8 @@ python3 kb_workflow.py --archive-file "[分析报告文件路径]" | 5 | kb-whisper-diarization | `--input xxx.mp4 --model base --diarize` | `{p}_{id}_diarized.json` | 带说话者分离(需 HF_TOKEN) | | 5 | kb-funasr | `--input xxx.mp4 --model sensevoice` | `{p}_{id}.json`, `{p}_{id}.txt` | **推荐用于中文视频** | | 5 | kb-ocr-processor | `--input xxx.mp4` | `{p}_{id}_ocr.json` | 语音识别困难时的替代方案 | -| 6 | kb-deep-analyzer | `--metadata x.meta.json --content x.json --output x.md --provider zhipu --model glm-5` | `{p}_{id}.md` | **使用 g5 模型,必须成功** | +| 6 | kb-deep-analyzer | `--metadata x.meta.json --content x.json --output x.md --type video --provider zhipu --model glm-5` | `{p}_{id}.md` | **视频分析,使用 g5 模型** | +| 6 | kb-deep-analyzer | `--metadata x.meta.json --content x.json --output x.md --type article --provider zhipu --model glm-5` | `{p}_{id}.md` | **文章分析,300字摘要+原文** | ## 条件分支逻辑 @@ -430,6 +445,10 @@ python3 kb_workflow.py --archive-file "[分析报告文件路径]" 步骤4: 提取专栏/文章文字内容 │ 命令: python3 bilibili_content.py --id "cvxxx" --type article ↓ +步骤6: 深度分析(使用 --type article) + │ 命令: python3 deep_analyzer.py --type article + │ 输出: 300字摘要 + 原文 + ↓ 步骤6: 深度分析(直接使用提取的文字) ↓ ┌─────────────────────────────────────┐