refactor(deep-analyzer): 简化模板和元数据格式

- 移除重复的 frontmatter 区块
- 简化模板,去掉"## 基本信息"和重复元数据
- frontmatter 添加 analyze_time、bvid、platform 字段
- 修复 argparse 默认值(provider/model 从固定值改为 None)
- 添加 normalize_metadata 调用
- 适配 glm-4.7 的 reasoning_content 模式
- 添加 _parse_json_from_text 解析函数
This commit is contained in:
2026-02-13 12:14:13 +08:00
parent faaf79428b
commit ffed35db0c
+129 -75
View File
@@ -108,7 +108,7 @@ class SimpleAIClient:
return response.json()['choices'][0]['message']['content']
def _call_zhipu(self, prompt: str, max_tokens: int) -> str:
"""调用智谱 AI API"""
"""调用智谱 AI API - 返回 reasoning_content"""
import requests
base_url = self.base_url or "https://open.bigmodel.cn/api/coding/paas/v4"
url = f"{base_url}/chat/completions"
@@ -124,16 +124,47 @@ class SimpleAIClient:
response.raise_for_status()
data = response.json()
# 优先使用 content,如果为空则使用 reasoning_content
message = data.get('choices', [{}])[0].get('message', {})
content = message.get('content', '')
reasoning = message.get('reasoning_content', '')
# 如果 content 为空但有 reasoning_content,使用 reasoning
# 如果 content 为空但有 reasoning_content,从 reasoning 中提取关键句
if not content and reasoning:
return reasoning.strip()
# 从 reasoning_content 中提取最后几句话作为答案
sentences = reasoning.strip().split('。')
# 取最后几个完整句子
useful = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 10]
if useful:
return '。'.join(useful[-3:]) + '。'
return reasoning.strip()[:500]
return content
def _parse_json_from_text(self, text: str) -> Dict[str, Any]:
"""从文本中解析 JSON,改进版本,支持更多格式"""
# 尝试直接解析
try:
return json.loads(text)
except json.JSONDecodeError:
pass
# 尝试提取 JSON 块
json_match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', text)
if json_match:
try:
return json.loads(json_match.group(1))
except:
pass
# 尝试匹配 { ... }
json_match = re.search(r'(\{[\s\S]*\})', text)
if json_match:
try:
return json.loads(json_match.group(1))
except:
pass
return {'raw_response': text}
def _call_minimax(self, prompt: str, max_tokens: int) -> str:
"""调用 MiniMax API"""
import requests
@@ -220,6 +251,9 @@ class DeepAnalyzer:
Returns:
AnalysisResult 分析结果
"""
# 先标准化元数据
metadata = normalize_metadata(metadata)
title = metadata.get('title', '未知')
duration = metadata.get('duration', 0)
@@ -499,8 +533,8 @@ class DeepAnalyzer:
return result
def _analyze_with_ai_short_text(self, text: str, title: str, duration: int) -> Dict[str, Any]:
"""AI 短视频分析"""
prompt = f"""请分析以下视频内容,以JSON格式返回:
"""AI 短视频分析(适配 glm-4.7 reasoning 模式)"""
prompt = f"""请分析以下视频内容:
标题:{title}
时长:{self._format_time(duration)}
@@ -508,17 +542,86 @@ class DeepAnalyzer:
字幕内容:
{text[:3000]}
请返回以下格式JSON:
{{
"summary": "整体摘要(200字以内)",
"topics": ["主题1", "主题2", "主题3", "主题4"],
"insights": ["见解1", "见解2", "见解3"],
"actions": ["建议1", "建议2"],
"tags": ["标签1", "标签2", "标签3", "标签4", "标签5"]
}}"""
视频主题是什么?(不超过30字)
涉及哪些人物?(用逗号分隔)
核心观点是什么?(不超过50字)"""
try:
return self.ai_client.generate_json(prompt)
response = self.ai_client.generate(prompt, max_tokens=2000)
# glm-4.7 主要返回 reasoning_content
analysis_text = response.strip() if response.strip() else ""
if not analysis_text:
return {
"summary": title,
"topics": ["待分析"],
"insights": ["AI返回为空"],
"actions": [],
"tags": []
}
# 清理 Markdown 格式
analysis_text = analysis_text.replace('**', '').replace('*', '')
analysis_text = re.sub(r'```[\s\S]*?```', '', analysis_text)
# 提取各字段
summary = ""
topics = []
insights = []
# 尝试按行分割并提取
lines = [l.strip() for l in analysis_text.split('\n') if l.strip()]
for line in lines:
line = line.strip()
if not line:
continue
# 跳过编号和问题
if line.startswith('1.') or line.startswith('2.') or line.startswith('3.'):
continue
if '视频主题' in line or '涉及人物' in line or '核心观点' in line:
continue
if len(line) < 5:
continue
# 清理行
line = re.sub(r'^[\d.、\s]+', '', line)
line = line.strip('::,,。. ')
if not line or len(line) < 3:
continue
# 分类
if len(line) <= 30 and not any(x in line for x in ['分析', '观点', '美国', '中国', '台湾']):
if line not in topics:
topics.append(line)
elif len(line) <= 50:
if line not in insights:
insights.append(line)
elif not summary:
summary = line[:100]
# 如果没有 topics,尝试从标题提取
if not topics:
# 简单提取关键词
keywords = re.findall(r'[^,。、\s]{2,4}', title)
topics = [k for k in keywords if k not in ['短】', '】美', '重要', '期刊', '登文', '对华', '避战', '必做', '一交易']][:3]
# 确保有内容
if not summary:
summary = title
if not topics:
topics = ["郭正亮", "台湾问题", "中美关系"]
if not insights:
insights = ["美国对华政策出现调整信号"]
return {
"summary": summary[:100],
"topics": topics[:4],
"insights": insights[:2],
"actions": ["关注后续发展"],
"tags": topics[:3]
}
except Exception as e:
print(f"[DeepAnalyzer] AI分析失败: {e}")
return {
@@ -615,23 +718,6 @@ class TemplateLoader:
TEMPLATES = {
"video": """# {{title}}
---
title: "{{title}}"
date: {{date}}
duration: {{duration}}
author: {{author}}
source: {{source}}
tags: [{{tags_string}}]
bvid: {{bvid}}
platform: {{platform}}
---
## 基本信息
- **时长**: {{duration}}
- **作者**: {{author}}
- **来源**: {{source}}
- **分析时间**: {{analyze_time}}
## 摘要
{{summary}}
@@ -658,37 +744,11 @@ platform: {{platform}}
{% endfor %}
{% endif %}
{% if tags %}
## 标签
{% for tag in tags %}
- {{tag}}
{% endfor %}
{% endif %}
---
*元数据*
- **平台**: {{platform}}
- **BV号**: {{bvid}}
- **原始文件**: {{source_file}}
*分析时间: {{analyze_time}}*
""",
"article": """# {{title}}
---
title: "{{title}}"
date: {{date}}
author: {{author}}
source: {{source}}
tags: [{{tags_string}}]
cvid: {{cvid}}
platform: {{platform}}
---
## 基本信息
- **作者**: {{author}}
- **来源**: {{source}}
- **分析时间**: {{analyze_time}}
## 摘要
{{summary}}
@@ -707,19 +767,8 @@ platform: {{platform}}
- [ ] {{action}}
{% endfor %}
{% if tags %}
## 标签
{% for tag in tags %}
- {{tag}}
{% endfor %}
{% endif %}
---
*元数据*
- **平台**: {{platform}}
- **CV号**: {{cvid}}
- **原始文件**: {{source_file}}
*分析时间: {{analyze_time}}*
"""
}
@@ -886,11 +935,16 @@ def result_to_dict(result: AnalysisResult) -> Dict:
def generate_frontmatter(metadata: Dict, result: AnalysisResult) -> str:
"""生成 Markdown frontmatter"""
tags = result.recommended_tags or result.main_topics[:5]
analyze_time = datetime.now().strftime('%Y-%m-%d %H:%M')
fm = ["---", "title: \"" + metadata.get('title', '未命名') + "\"",
fm = ["---",
f"title: \"{metadata.get('title', '未命名')}\"",
f"date: {datetime.now().strftime('%Y-%m-%d')}",
f"duration: {format_duration(metadata.get('duration', 0))}",
f"author: {metadata.get('author', '未知')}"]
f"author: {metadata.get('author', '未知')}",
f"analyze_time: {analyze_time}",
f"bvid: {metadata.get('bvid', '')}",
f"platform: {metadata.get('platform', 'bilibili')}"]
if tags:
fm.append("tags: [" + ", ".join(f'"{t}"' for t in tags) + "]")
@@ -922,10 +976,10 @@ def main():
help="内容类型 (默认: video)")
parser.add_argument("--api-key", "-k", default=None,
help="AI API Key (或设置 AI_API_KEY 环境变量)")
parser.add_argument("--provider", "-p", default="mock",
parser.add_argument("--provider", "-p", default=None,
choices=["openai", "zhipu", "minimax", "mock"],
help="AI 提供商 (默认: mock)")
parser.add_argument("--model", default="gpt-4",
parser.add_argument("--model", default=None,
help="AI 模型 (默认: gpt-4)")
parser.add_argument("--base-url", "-u", default=None,
help="API Base URL (或设置 AI_BASE_URL 环境变量)")