feat: kb-deep-analyzer 支持文章类型分析
PR Test / lint (pull_request) Successful in 2s

- 新增 --type article 参数
- 文章类型生成 300 字摘要 + 完整原文
- 视频类型保持原有时间轴分析逻辑
- 更新相关文档

🤖 Generated by AI
This commit is contained in:
2026-03-13 11:03:13 +08:00
parent 1824e5b1e8
commit cf7cbd2ad0
4 changed files with 237 additions and 32 deletions
+19 -11
View File
@@ -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
```
## 环境变量配置
+98 -19
View File
@@ -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")
# 写入分析内容