Ver código fonte

Merge branch 'supeng-dev' into supeng-dev-dy

zhangliang 8 horas atrás
pai
commit
df9794813d

+ 1 - 1
examples/content_finder/.env.example

@@ -1,5 +1,5 @@
 # LLM 配置
-OPENROUTER_API_KEY=your-api-key-here
+OPEN_ROUTER_API_KEY=your-api-key-here
 MODEL=anthropic/claude-sonnet-4.5
 TEMPERATURE=0.3
 MAX_ITERATIONS=30

+ 25 - 0
examples/content_finder/content_finder.prompt

@@ -0,0 +1,25 @@
+---
+model: sonnet-4.6
+temperature: 0.3
+---
+
+$system$
+你是一个专业的内容寻找助手,帮助运营人员在抖音平台上寻找符合要求的视频内容。
+
+平台背景(仅供参考):
+- 平台载体:微信小程序
+- 核心用户群:95% 是 50 岁以上中老年人
+- 增长方式:微信分享裂变
+- 核心指标:分享率、DAU
+
+请按照 content_finding_strategy 和 content_filtering_strategy 中的方法论执行任务。
+
+$user$
+孩子军抗日,让人感动。找这样的视频。
+
+要求:
+- 内容要有情感共鸣
+- 适合老年人观看
+- 热度要高,质量要好
+
+请开始执行内容寻找任务。记住要多步推理,每次只执行一小步,然后思考下一步该做什么。

+ 75 - 217
examples/content_finder/run.py

@@ -1,10 +1,5 @@
 """
-内容寻找 Agent - MVP 版本
-
-功能:
-- 根据用户需求智能搜索抖音视频内容
-- 基于画像数据筛选符合老年人群体的内容
-- 深度挖掘优质作者的其他作品
+内容寻找 Agent
 
 使用示例:
     python run.py
@@ -15,10 +10,7 @@ import logging
 import sys
 import os
 from pathlib import Path
-from typing import Optional, Dict, Any
-from datetime import datetime
 
-# 添加项目根目录到 Python 路径
 sys.path.insert(0, str(Path(__file__).parent.parent.parent))
 
 from dotenv import load_dotenv
@@ -32,6 +24,7 @@ from agent import (
     Message,
 )
 from agent.llm import create_openrouter_llm_call
+from agent.llm.prompts import SimplePrompt
 
 # 导入工具(确保工具被注册)
 from tools import (
@@ -56,217 +49,82 @@ logging.basicConfig(
 logger = logging.getLogger(__name__)
 
 
-# ===== 配置管理 =====
-
-class ContentFinderConfig:
-    """内容寻找 Agent 配置"""
-
-    def __init__(self):
-        # LLM 配置
-        self.model = os.getenv("MODEL", "anthropic/claude-sonnet-4.5")
-        self.api_key = os.getenv("OPEN_ROUTER_API_KEY")
-        self.temperature = float(os.getenv("TEMPERATURE", "0.3"))
-        self.max_iterations = int(os.getenv("MAX_ITERATIONS", "30"))
-
-        # 存储配置
-        self.trace_dir = os.getenv("TRACE_DIR", ".cache/traces")
-        self.output_dir = os.getenv("OUTPUT_DIR", ".cache/output")
-
-        # Skills 配置 - 使用绝对路径
-        default_skills_dir = str(Path(__file__).parent / "skills")
-        self.skills_dir = os.getenv("SKILLS_DIR", default_skills_dir)
-
-        # 验证配置
-        self._validate()
-
-    def _validate(self):
-        """验证配置"""
-        if not self.api_key:
-            raise ValueError("OPEN_ROUTER_API_KEY 未设置,请在 .env 文件中配置")
-
-        # 创建必要的目录
-        Path(self.trace_dir).mkdir(parents=True, exist_ok=True)
-        Path(self.output_dir).mkdir(parents=True, exist_ok=True)
-        Path('.cache').mkdir(exist_ok=True)
-
-
-# ===== Agent 封装 =====
-
-class ContentFinderAgent:
-    """内容寻找 Agent"""
-
-    def __init__(self, config: ContentFinderConfig):
-        self.config = config
-
-        # 初始化 Trace Store
-        self.trace_store = FileSystemTraceStore(base_path=config.trace_dir)
-
-        # 初始化 LLM
-        self.llm_call = create_openrouter_llm_call(model=config.model)
-
-        # 初始化 Runner
-        self.runner = AgentRunner(
-            llm_call=self.llm_call,
-            trace_store=self.trace_store,
-            skills_dir=config.skills_dir,
-        )
-
-        logger.info(f"ContentFinderAgent 初始化完成")
-        logger.info(f"  模型: {config.model}")
-        logger.info(f"  Skills 目录: {config.skills_dir}")
-
-    async def find_content(
-        self,
-        user_request: str,
-        trace_id: Optional[str] = None,
-    ) -> Dict[str, Any]:
-        """
-        根据用户需求寻找内容
-
-        Args:
-            user_request: 用户需求描述
-            trace_id: 可选的 trace_id,用于恢复之前的会话
-
-        Returns:
-            包含执行结果的字典
-        """
-        logger.info(f"开始处理用户需求: {user_request}")
-
-        # 构建系统提示
-        system_prompt = """你是一个专业的内容寻找助手,擅长根据用户需求在抖音平台上寻找合适的视频内容。
-
-⚠️ 重要提示:当前所有工具返回的都是模拟数据,仅用于测试和演示。
-- 在分析和推荐时,请明确说明这是基于模拟数据的演示
-- 不要给出过于自信的评分和推荐
-- 提醒用户实际使用时需要对接真实API
-
-你的工作流程:
-1. 理解用户需求,提取关键词和目标受众特征
-2. 使用 douyin_search 工具搜索相关内容(返回模拟数据)
-3. 使用画像工具分析内容是否符合目标受众(返回模拟数据)
-4. 对于优质内容,使用 douyin_user_videos 获取作者的其他作品(返回模拟数据)
-5. 综合评估并推荐最合适的内容,但要说明这是基于模拟数据的演示
-
-重点关注:
-- 内容是否符合老年人群体的偏好(年龄分布、偏好度)
-- 内容热度(点赞量、评论量、分享量)
-- 互动率(评论率、分享率)
-- 时效性(发布时间)
-- 作者的内容质量和稳定性
-
-请按照 content_finding_strategy 和 content_filtering_strategy 中的策略执行。"""
-
-        # 构建配置
-        config = RunConfig(
-            model=self.config.model,
-            temperature=self.config.temperature,
-            max_iterations=self.config.max_iterations,
-            agent_type="content_finder",
-            trace_id=trace_id,
-        )
-
-        # 构建消息
-        messages = [
-            {"role": "system", "content": system_prompt},
-            {"role": "user", "content": user_request},
-        ]
-
-        # 执行
-        result = {
-            "trace_id": None,
-            "status": "unknown",
-            "messages_count": 0,
-            "response": "",
-            "error": None,
-        }
-
-        try:
-            last_assistant_message = None
-            message_count = 0
-
-            async for item in self.runner.run(messages=messages, config=config):
-                if isinstance(item, Trace):
-                    result["trace_id"] = item.trace_id
-                    result["status"] = item.status
-                    logger.info(f"Trace 状态: {item.status}")
-
-                elif isinstance(item, Message):
-                    message_count += 1
-                    if item.role == "assistant":
-                        last_assistant_message = item
-                        # 实时输出助手响应
-                        content = item.content
-                        if isinstance(content, str):
-                            print(f"\n{content}")
-                        elif isinstance(content, dict):
-                            text = content.get("text", "")
-                            if text:
-                                print(f"\n{text}")
-
-            result["messages_count"] = message_count
-
-            # 提取最后的助手响应
-            if last_assistant_message:
-                content = last_assistant_message.content
-                if isinstance(content, str):
-                    result["response"] = content
-                elif isinstance(content, dict):
-                    result["response"] = content.get("text", "")
-
-            logger.info(f"执行完成,共 {message_count} 条消息")
-
-        except Exception as e:
-            logger.error(f"执行失败: {e}", exc_info=True)
-            result["error"] = str(e)
-            result["status"] = "failed"
-
-        return result
-
-
-# ===== 主函数 =====
-
 async def main():
-    """主函数"""
+    print("\n" + "=" * 60)
+    print("内容寻找 Agent")
+    print("=" * 60)
+    print("开始执行...\n")
+
+    # 加载 prompt
+    prompt_path = Path(__file__).parent / "content_finder.prompt"
+    prompt = SimplePrompt(prompt_path)
+
+    # 构建消息
+    messages = prompt.build_messages()
+
+    # 初始化
+    api_key = os.getenv("OPEN_ROUTER_API_KEY")
+    if not api_key:
+        raise ValueError("OPEN_ROUTER_API_KEY 未设置,请在 .env 文件中配置")
+
+    model = os.getenv("MODEL", f"anthropic/claude-{prompt.config.get('model', 'sonnet-4.6')}")
+    temperature = float(prompt.config.get("temperature", 0.3))
+    max_iterations = int(os.getenv("MAX_ITERATIONS", "30"))
+    trace_dir = os.getenv("TRACE_DIR", ".cache/traces")
+    skills_dir = str(Path(__file__).parent / "skills")
+
+    Path(trace_dir).mkdir(parents=True, exist_ok=True)
+
+    store = FileSystemTraceStore(base_path=trace_dir)
+    runner = AgentRunner(
+        llm_call=create_openrouter_llm_call(model=model),
+        trace_store=store,
+        skills_dir=skills_dir,
+    )
+
+    config = RunConfig(
+        model=model,
+        temperature=temperature,
+        max_iterations=max_iterations,
+    )
+
+    # 执行
     try:
-        # 加载配置
-        config = ContentFinderConfig()
-
-        # 创建 Agent
-        agent = ContentFinderAgent(config)
-
-        # 用户需求
-        user_request = """
-孩子军抗日,让人感动。找这样的视频。
-
-要求:
-- 内容要感人,有情感共鸣
-- 适合老年人观看
-- 热度要高,质量要好
-"""
-
-        print("\n" + "="*60)
-        print("内容寻找 Agent - MVP 版本")
-        print("="*60)
-        print(f"\n用户需求:\n{user_request}")
-        print("\n" + "="*60)
-        print("开始执行...\n")
-
-        # 执行任务
-        result = await agent.find_content(user_request)
-
-        # 输出结果摘要
-        print("\n" + "="*60)
-        print("执行结果摘要")
-        print("="*60)
-        print(f"Trace ID: {result.get('trace_id')}")
-        print(f"状态: {result.get('status')}")
-        print(f"消息数: {result.get('messages_count')}")
-        if result.get('error'):
-            print(f"错误: {result.get('error')}")
-        print("="*60 + "\n")
-
+        async for item in runner.run(messages=messages, config=config):
+            if isinstance(item, Trace):
+                if item.status == "completed":
+                    print(f"\n[完成] trace_id={item.trace_id}")
+                elif item.status == "failed":
+                    print(f"\n[失败] {item.error_message}")
+
+            elif isinstance(item, Message):
+                if item.role == "assistant":
+                    content = item.content
+                    if isinstance(content, dict):
+                        text = content.get("text", "")
+                        tool_calls = content.get("tool_calls")
+                        if text and not tool_calls:
+                            print(f"\n{text}")
+                        elif text:
+                            print(f"[思考] {text[:100]}..." if len(text) > 100 else f"[思考] {text}")
+                        if tool_calls:
+                            for tc in tool_calls:
+                                tool_name = tc.get("function", {}).get("name", "unknown")
+                                print(f"[工具] {tool_name}")
+                    elif isinstance(content, str) and content:
+                        print(f"\n{content}")
+
+                elif item.role == "tool":
+                    content = item.content
+                    if isinstance(content, dict):
+                        tool_name = content.get("tool_name", "unknown")
+                        print(f"[结果] {tool_name} ✓")
+
+    except KeyboardInterrupt:
+        print("\n用户中断")
     except Exception as e:
-        logger.error(f"程序执行失败: {e}", exc_info=True)
+        logger.error(f"执行失败: {e}", exc_info=True)
+        print(f"\n执行失败: {e}")
         sys.exit(1)
 
 

+ 33 - 209
examples/content_finder/skills/content_filtering_strategy.md

@@ -1,223 +1,47 @@
-# 内容筛选策略
+# 内容筛选方法论
 
-## 目标
-对获取到的视频内容进行多维度筛选,确保内容符合目标受众(主要是老年人群体)的偏好和质量要求。
+## 核心方法:从需求中提取标准 → 按标准评估 → 分层输出
 
-## 重要说明
-⚠️ **抖音渠道无法获取播放量数据**,筛选策略主要依赖点赞量、评论量、分享量等可获取的指标。
+### 第一步:从需求中提取评估标准
 
-## 筛选维度
+用户的要求即评估标准,不要引入用户没有提到的标准。
 
-### 1. 热度指标筛选
+**如何提取**:
+- 用户明确说的条件,作为硬性标准(必须满足)
+- 用户模糊描述的,作为软性标准(尽量满足)
+- 用户没提的,不要主动引入
 
-**点赞量**(主要指标)
-- 最低门槛:2,000+
-- 优质内容:5,000+
-- 爆款内容:20,000+
+**举例**:
+- 用户说「热度高」→ 评估热度数据,其他维度作参考
+- 用户说「感人」→ 评估情感共鸣信号(标题、描述等),而不只看数字
+- 用户说「适合XX人群」→ 评估画像数据中该人群的占比和偏好度
 
-**评论量**
-- 最低门槛:200+
-- 优质内容:500+
-- 爆款内容:2,000+
+### 第二步:评估每条内容
 
-**分享量**
-- 最低门槛:100+
-- 优质内容:500+
-- 爆款内容:2,000+
+**量化指标**:有具体数字的直接比较(点赞量、分享率、年龄占比等)。
 
-**收藏量**(如果可获取)
-- 最低门槛:500+
-- 优质内容:1,000+
-- 爆款内容:5,000+
+**质性判断**:无法量化时,寻找间接信号:
+- 「感人」→ 看评论中是否有情感表达
+- 「有价值」→ 看收藏率是否高于平均水平
+- 「适合XX」→ 看画像偏好度是否大于1.0
 
-### 2. 互动质量评估
+**相对比较**:在候选集中排序,而不是套用固定门槛。同一批结果里,谁更好就推谁。
 
-**互动总量** = 点赞量 + 评论量 + 分享量
-- 合格:≥ 2,500
-- 优秀:≥ 6,000
-- 极佳:≥ 22,000
+### 第三步:分层输出
 
-**评论/点赞比**(反映内容讨论度)
-- 合格:≥ 8%
-- 优秀:≥ 10%
-- 极佳:≥ 15%
+不是非此即彼,而是按匹配度分层:
 
-**分享/点赞比**(反映内容传播力)
-- 合格:≥ 3%
-- 优秀:≥ 5%
-- 极佳:≥ 8%
+- **强烈推荐**:核心标准全部满足,其他维度也好
+- **推荐**:核心标准满足,有小瑕疵
+- **可选**:核心标准基本满足,作为补充
+- **不推荐**:核心标准不满足,明确说明原因
 
-**收藏/点赞比**(反映内容价值)
-- 合格:≥ 15%
-- 优秀:≥ 20%
-- 极佳:≥ 30%
+## 关键原则
 
-### 3. 时效性评估
-
-**发布时间**(重要指标)
-- 最近7天:新鲜内容,优先推荐
-- 7-30天:较新内容,正常推荐
-- 30-90天:中等时效,需结合热度判断
-- 90天以上:老内容,需要更高的热度指标才推荐
-
-**时效性调整策略**:
-```
-如果发布时间 < 7天:
-  - 点赞量门槛降低30%(1,400+ 即可)
-  - 适合挖掘潜力内容
-
-如果发布时间 > 90天:
-  - 点赞量门槛提高50%(3,000+ 才考虑)
-  - 需要验证内容是否仍有价值
-```
-
-### 4. 观众画像筛选
-
-使用 `get_video_audience_profile` 获取画像数据:
-
-**年龄分布(老年人群体)**
-- 41-50岁占比:≥ 20%
-- 51-60岁占比:≥ 10%
-- 60岁以上占比:≥ 3%
-- 或:41岁以上总占比 ≥ 35%
-
-**年龄偏好度**
-- 41-50岁偏好度:≥ 1.1
-- 51-60岁偏好度:≥ 1.2
-- 60岁以上偏好度:≥ 1.3
-
-**城市等级分布**
-- 二线及以下城市占比:≥ 50%
-
-### 5. 粉丝画像筛选
-
-使用 `get_user_fans_profile` 获取作者粉丝画像:
-
-**粉丝量**
-- 最低门槛:10,000+
-- 优质作者:50,000+
-- 头部作者:200,000+
-
-**粉丝年龄分布**
-- 41岁以上占比:≥ 40%
-- 51岁以上占比:≥ 15%
-
-**粉丝偏好度**
-- 41-50岁偏好度:≥ 1.2
-- 51-60岁偏好度:≥ 1.3
-
-## 筛选流程
-
-### 第一轮:基础筛选
-1. 检查点赞量、评论量、分享量是否达到最低门槛
-2. 检查发布时间,根据时效性调整门槛
-3. 计算互动总量和互动比例
-4. 过滤掉不符合基础标准的内容
-
-### 第二轮:画像筛选
-1. 对通过基础筛选的内容获取观众画像
-2. 检查年龄分布和偏好度
-3. 检查城市等级分布
-4. 保留符合老年人群体特征的内容
-
-### 第三轮:作者筛选
-1. 对优质内容的作者获取粉丝画像
-2. 检查粉丝量和粉丝年龄分布
-3. 识别擅长老年人内容的优质作者
-4. 获取这些作者的更多作品
-
-## 评分机制
-
-为每个视频计算综合得分(满分100分):
-
-```
-综合得分 = 热度得分(30分) + 互动质量得分(25分) + 时效性得分(15分) + 画像匹配度(30分)
-
-热度得分 = (点赞量得分 + 评论量得分 + 分享量得分) / 3
-互动质量得分 = (互动总量得分 + 评论/点赞比得分 + 分享/点赞比得分) / 3
-时效性得分 = 根据发布时间计算(7天内满分,90天以上0分)
-画像匹配度 = (年龄匹配度 + 偏好度匹配度 + 城市等级匹配度) / 3
-```
-
-**推荐标准**:
-- 综合得分 ≥ 70分:强烈推荐
-- 综合得分 60-69分:推荐
-- 综合得分 50-59分:可选
-- 综合得分 < 50分:不推荐
-
-## 使用示例
-
-```
-# 第一轮:基础筛选
-视频A: 点赞8k, 评论1.2k, 分享500, 发布3天前
-- 互动总量: 9,700 ✓
-- 评论/点赞比: 15% ✓
-- 分享/点赞比: 6.25% ✓
-- 时效性: 新鲜内容 ✓
-结果: 通过(优先推荐)
-
-视频B: 点赞15k, 评论2.3k, 分享1.2k, 发布120天前
-- 互动总量: 18,500 ✓
-- 评论/点赞比: 15.3% ✓
-- 分享/点赞比: 8% ✓
-- 时效性: 老内容,但热度高 ✓
-结果: 通过
-
-视频C: 点赞2.5k, 评论300, 分享150, 发布150天前
-- 互动总量: 2,950 ✓
-- 评论/点赞比: 12% ✓
-- 分享/点赞比: 6% ✓
-- 时效性: 老内容且热度一般 ✗
-结果: 不通过(时效性差)
-
-# 第二轮:画像筛选
-获取视频A观众画像:
-- 41-50岁: 20.8% (偏好度1.25) ✓
-- 51-60岁: 10.5% (偏好度1.45) ✓
-- 60岁以上: 2.7% (偏好度1.65) ✓
-- 41岁以上总占比: 34% ✗
-结果: 勉强通过
-
-# 第三轮:作者筛选
-获取作者粉丝画像:
-- 粉丝量: 125k ✓
-- 41岁以上占比: 43.2% ✓
-- 51岁以上占比: 19% ✓
-结果: 通过,该作者擅长老年人内容
-```
-
-## 筛选策略建议
-
-### 1. 优先新鲜内容
-- 7天内的内容优先推荐
-- 可以挖掘潜力内容
-- 适当降低热度门槛
-
-### 2. 重视互动质量
-- 评论/点赞比反映讨论度
-- 分享/点赞比反映传播力
-- 比绝对数值更能反映内容质量
-
-### 3. 结合画像数据
-- 画像数据可以精准定位受众
-- 通过年龄分布和偏好度判断匹配度
-- 作者粉丝画像比单个视频更可靠
-
-### 4. 关注作者稳定性
-- 通过作者的多个作品判断质量
-- 优先选择有多个优质作品的作者
-- 粉丝画像符合目标受众的作者
-
-### 5. 灵活调整门槛
-- 根据实际数据分布调整标准
-- 可以通过 A/B 测试优化门槛值
-- 定期根据效果调整策略
-
-## 注意事项
-- ⚠️ 无法获取播放量,评估标准有一定局限性
-- ⚠️ 时效性很重要,优先推荐新鲜内容
-- 建议结合多个维度综合判断
-- 画像数据可能存在误差,需结合实际效果验证
-- 对于新发布的内容,可适当放宽热度要求
-- 关注内容质量,数据只是参考
-- 定期根据实际效果调整筛选策略
+**标准来自需求**:评估维度随需求变化,不固化。
+
+**说明评估逻辑**:让用户理解为什么这条内容被推荐或排除。
+
+**承认不确定性**:数据不足以判断时,如实说明,而不是强行打分。
+
+**让用户决定**:提供充分信息,最终选择权在用户。

+ 46 - 238
examples/content_finder/skills/content_finding_strategy.md

@@ -1,238 +1,46 @@
-# 内容寻找策略
-
-## 目标
-根据用户输入的需求,智能解析关键词并通过抖音搜索获取相关视频内容。对于符合条件的内容,进一步获取作者的其他作品进行深度挖掘。
-
-## 重要说明
-⚠️ **抖音渠道无法获取播放量数据**,筛选时主要依赖点赞量、评论量、分享量等可获取的指标。
-⚠️ **优先推荐新鲜内容**,时效性是重要的筛选维度。
-
-## 策略流程
-
-### 1. 需求解析
-- 从用户输入中提取核心关键词
-- 识别情感倾向(感人、励志、搞笑等)
-- 识别主题类型(抗战、历史、情感等)
-- 识别目标受众特征(如:老年人)
-
-### 2. 初步搜索
-使用 `douyin_search` 工具进行关键词搜索:
-- 使用提取的核心关键词
-- 设置合理的筛选条件(只能使用点赞数筛选)
-- 获取初步结果集
-
-**搜索参数建议**:
-```python
-douyin_search(
-    keywords="孩子军抗日",
-    min_likes=2000,      # 最低点赞数
-    max_results=20       # 返回数量
-)
-```
-
-### 3. 内容筛选
-对搜索结果进行初步筛选:
-
-**可用指标**:
-- ✅ 点赞量
-- ✅ 评论量
-- ✅ 分享量
-- ✅ 收藏量(如果可获取)
-- ✅ 发布时间(时效性)
-
-**筛选标准**:
-- 点赞量:≥ 2,000
-- 评论量:≥ 200
-- 分享量:≥ 100
-- 互动总量:≥ 2,500
-- 评论/点赞比:≥ 8%
-- 分享/点赞比:≥ 3%
-
-**时效性筛选**:
-```
-发布时间 < 7天:
-  - 优先推荐
-  - 点赞量门槛降低30%(1,400+ 即可)
-
-发布时间 7-30天:
-  - 正常推荐
-  - 使用标准门槛
-
-发布时间 30-90天:
-  - 需结合热度判断
-  - 热度高才推荐
-
-发布时间 > 90天:
-  - 谨慎推荐
-  - 点赞量门槛提高50%(3,000+ 才考虑)
-```
-
-### 4. 深度挖掘
-对于符合条件的优质内容:
-- 记录作者ID
-- 使用 `douyin_user_videos` 获取该作者的其他作品
-- 对作者作品进行二次筛选
-- 识别作者的内容风格和质量稳定性
-
-**深度挖掘策略**:
-```python
-# 对于点赞量 > 5000 的优质内容
-if video['like_count'] > 5000:
-    # 获取作者的其他作品
-    author_videos = douyin_user_videos(
-        author_id=video['author_id'],
-        max_results=10
-    )
-    # 筛选作者的优质作品(考虑时效性)
-    quality_videos = [
-        v for v in author_videos
-        if v['like_count'] > 2000
-        and days_since_publish(v) < 90  # 90天内的内容
-    ]
-```
-
-### 5. 画像验证
-使用画像数据精准定位目标受众:
-
-**视频观众画像**:
-- 使用 `get_video_audience_profile` 获取点赞观众画像
-- 检查年龄分布(41岁以上占比 ≥ 35%)
-- 检查年龄偏好度(41-50岁 ≥ 1.1,51-60岁 ≥ 1.2)
-- 检查城市等级分布(二线及以下 ≥ 50%)
-
-**作者粉丝画像**:
-- 使用 `get_user_fans_profile` 获取作者粉丝画像
-- 检查粉丝量(≥ 10,000)
-- 检查粉丝年龄分布(41岁以上 ≥ 40%)
-- 检查粉丝偏好度(51-60岁 ≥ 1.3)
-
-## 使用示例
-
-**用户需求**: "孩子军抗日,让人感动。找这样的视频。"
-
-**执行步骤**:
-
-1. **解析关键词**:
-   - 核心关键词:抗日、孩子军
-   - 情感倾向:感人
-   - 目标受众:老年人
-
-2. **初步搜索**:
-   ```python
-   douyin_search(
-       keywords="孩子军抗日",
-       min_likes=2000,
-       max_results=20
-   )
-   ```
-
-3. **内容筛选**(考虑时效性):
-   ```
-   视频A: 点赞8k, 评论1.2k, 分享500, 发布3天前
-   - 互动总量: 9,700 ✓
-   - 评论/点赞比: 15% ✓
-   - 分享/点赞比: 6.25% ✓
-   - 时效性: 新鲜内容 ✓✓
-   结果: 通过(优先推荐)
-
-   视频B: 点赞15k, 评论2.3k, 分享1.2k, 发布120天前
-   - 互动总量: 18,500 ✓
-   - 评论/点赞比: 15.3% ✓
-   - 分享/点赞比: 8% ✓
-   - 时效性: 老内容,但热度高 ✓
-   结果: 通过
-
-   视频C: 点赞2.5k, 评论300, 分享150, 发布150天前
-   - 互动总量: 2,950 ✓
-   - 评论/点赞比: 12% ✓
-   - 分享/点赞比: 6% ✓
-   - 时效性: 老内容且热度一般 ✗
-   结果: 不通过
-   ```
-
-4. **深度挖掘**:
-   ```python
-   # 对视频A的作者进行深度挖掘
-   author_videos = douyin_user_videos(
-       author_id="user_001",
-       max_results=10
-   )
-   # 筛选30天内的优质作品
-   recent_quality = [
-       v for v in author_videos
-       if v['like_count'] > 2000
-       and days_since_publish(v) < 30
-   ]
-   # 发现该作者有3个近期优质作品
-   ```
-
-5. **画像验证**:
-   ```python
-   # 获取视频A的观众画像
-   profile = get_video_audience_profile(video_id="video_001")
-   # 检查:41岁以上占比 38% ✓
-
-   # 获取作者粉丝画像
-   fans_profile = get_user_fans_profile(author_id="user_001")
-   # 检查:粉丝量 125k ✓,41岁以上占比 43.2% ✓
-   ```
-
-## 筛选优先级
-
-建议按以下优先级筛选:
-
-1. **时效性**(新增,权重高)
-   - 7天内的内容优先
-   - 门槛:发布时间越近越好
-
-2. **点赞量**
-   - 最直接的热度指标
-   - 门槛:≥ 2,000(新内容可降低30%)
-
-3. **互动总量**
-   - 综合反映内容受欢迎程度
-   - 门槛:≥ 2,500
-
-4. **互动比例**
-   - 评论/点赞比 ≥ 8%
-   - 分享/点赞比 ≥ 3%
-
-5. **画像匹配度**
-   - 年龄分布符合老年人群体
-   - 偏好度 ≥ 1.1
-
-6. **作者质量**
-   - 粉丝量 ≥ 10,000
-   - 有多个近期优质作品
-
-## 注意事项
-
-### 关于时效性
-- ⚠️ **优先推荐新鲜内容**(7天内)
-- ⚠️ 老内容(90天以上)需要更高的热度才推荐
-- ⚠️ 时效性可以弥补热度不足
-
-### 关于数据限制
-- ⚠️ 无法获取播放量,不要使用 `min_views` 参数
-- ⚠️ 评估标准需要调整,主要依赖点赞量和互动比例
-- ⚠️ 画像数据可以弥补播放量缺失
-
-### 关键词策略
-- 关键词要准确,避免过于宽泛
-- 可以尝试多个关键词组合
-- 根据搜索结果调整关键词
-
-### 筛选策略
-- 不要过于严格,避免结果过少
-- 结合多个维度综合判断
-- 时效性和热度需要平衡
-
-### 深度挖掘
-- 注意作者内容的一致性
-- 优先选择有多个近期优质作品的作者
-- 关注作者的粉丝画像
-
-### 灵活调整
-- 根据实际数据分布调整门槛
-- 定期根据效果优化策略
-- 可以通过 A/B 测试验证效果
+# 内容寻找方法论
+
+## 核心方法:需求拆解 → 搜索执行 → 结果验证
+
+### 第一步:拆解用户需求
+
+在搜索前,先理解需求的结构:
+
+**主题维度**:用户要找什么内容?提取核心关键词。
+
+**约束维度**:有哪些限制条件?(热度要求、受众特征、时间范围等)
+
+**优先级**:哪个维度是最重要的?用户明确说的 > 用户暗示的 > 你的默认假设。
+
+### 第二步:制定搜索策略
+
+**关键词选择**:
+- 优先使用用户原话中的关键词
+- 必要时补充同义词或相关词
+- 不要替换用户的核心意图
+
+**参数设置**:
+- 根据约束条件设置搜索参数
+- 不确定时使用宽松参数,宁可多搜再筛选
+
+**迭代策略**:
+- 第一轮搜索结果不够好时,调整关键词或参数再搜
+- 不要在一次失败后就放弃
+
+### 第三步:验证结果
+
+拿到搜索结果后,对照需求逐一验证:
+
+- 每个结果是否满足用户的核心要求?
+- 约束条件是否达标?
+- 有无更好的候选?
+
+**如果结果不满足要求**:调整搜索策略,再次尝试,而不是凑合推荐。
+
+## 关键原则
+
+**忠实需求**:用户要什么就找什么,不要基于自己的判断替换用户意图。
+
+**透明过程**:说明为什么选择这些关键词,用了什么筛选逻辑。
+
+**承认局限**:如果真的找不到符合要求的内容,如实说明,而不是推荐不符合要求的内容。