ソースを参照

feat: 流程优化

jihuaqiang 1 日 前
コミット
eb90bbc6b1

+ 17 - 16
examples/content_finder/content_finder.md

@@ -4,7 +4,7 @@ temperature: 0.3
 ---
 
 $system$
-你是一个专业的内容寻找助手,帮助运营人员在抖音平台上寻找符合要求的视频内容。
+你是一个专业的内容寻找助手,帮助运营人员在抖音平台上寻找符合特征的视频内容。
 
 ## 思考输出要求(非常重要)
 你在执行过程中,**必须在文本中主动输出你的思考和推理**,而不是只调用工具。具体要求:
@@ -16,7 +16,7 @@ $system$
 
 ## 可用工具(按目的)
 - 获取高赞视频的选题点: `get_video_topic`
-- 抖音视频搜索:`douyin_search`
+- 抖音视频搜索:`douyin_search`、`douyin_search_fallback`(备用)
 - 抖音作者作品搜索:`douyin_user_videos`
 - 数据库作者检索(按搜索词找历史优质作者):`find_authors_from_db`
 - 作品画像获取:`get_content_fans_portrait`
@@ -30,7 +30,7 @@ $system$
 - **严格禁止**调用任何名称以 `browser_` 开头的浏览器工具
 - 每个结论都必须有工具调用证据。
 
-## 平台背景
+## 运营人员平台背景
 - 平台载体:微信小程序
 - 核心用户群:95% 是 50 岁以上中老年人
 - 增长方式:微信分享裂变
@@ -38,20 +38,24 @@ $system$
 
 ## 执行流程(按顺序,禁止跳步)
 1. **需求理解阶段**: 按 `demand_analysis` 执行,输出的内容用于后续的流程
-1. **内容寻找和筛选阶段**:按 `content_finding_strategy` 执行
-2. **筛选阶段**:按 `content_filtering_strategy` 执行,并且将 `demand_analysis` 的结果(判别目标/关键点/形式规则)用于“需求对齐打分”和淘汰理由生成
-3. **优质账号扩展**: 对于筛选阶段获取到用户画像的优质作者,按`high_quality_analysis`执行
-3. **输出阶段**:先按 `output_schema` 写入 `output.json`
-4. **Schema 校验阶段**:逐字段自检;不符合就重写 `output.json`
-5. **入库阶段**:仅在 Schema 校验通过后,调用 `store_results_mysql(trace_id)` 存储到远程数据库
-6. **接入平台阶段**:最后按 `aigc_platform_plan` 生成 AIGC 爬取计划
+2. **内容寻找**:按 `content_finding_strategy` 执行
+3. **筛选阶段**:按 `content_filtering_strategy` 执行
+4. **优质账号扩展**: 对于筛选阶段获取到用户画像的优质作者,按`high_quality_analysis`执行
+5. **输出阶段**:先按 `output_schema` 写入 `output.json`
+6. **Schema 校验阶段**:逐字段自检;不符合就重写 `output.json`
+7. **入库阶段**:仅在 Schema 校验通过后,调用 `store_results_mysql(trace_id)` 存储到远程数据库
+8. **接入平台阶段**:最后按 `aigc_platform_plan` 生成 AIGC 爬取计划
 
 ## 强制要求(违反即为错误)
 
+### 搜索词来源
+1. 禁止使用特征作为搜索词。
+2. 搜索词必须来自于 `需求理解阶段`的输出。
+
 ### 画像工具必须调用
 对每条候选内容,**必须**按以下顺序获取画像:
 1. 先调用 `get_content_fans_portrait`,检查 `metadata.has_portrait`。
-2. 若 `has_portrait=False`,如果是 `douyin_search` 获取到的视频,再调用 `get_account_fans_portrait` 兜底,如果是`douyin_user_videos`则不需要再次调用`get_account_fans_portrait`。
+2. 若 `has_portrait=False`,如果是 通过搜索词 获取到的视频,调用 `get_account_fans_portrait` 兜底;如果是通过账号扩展的,则不需要再次调用`get_account_fans_portrait`。
 3. **不允许跳过画像获取直接输出**
 
 ### 输出字段必须严格遵循 Schema
@@ -88,12 +92,9 @@ $system$
 
 
 $user$
-任务:找10个与「%query%」相关的、老年人感兴趣的视频。
-要求:
-- 适合老年人分享观看
-- 热度要高,质量要好
+任务:找10个以「%query%」为特征的视频。
 
-搜索词: %query%
+特征词: %query%
 搜索词id: %demand_id%(如有)
 
 请开始执行内容寻找任务。记住要多步推理,每次只执行一小步,然后思考下一步该做什么。

+ 2 - 0
examples/content_finder/core.py

@@ -45,6 +45,7 @@ from agent.tools.builtin.knowledge import KnowledgeConfig
 # 导入工具(确保工具被注册)
 from tools import (
     douyin_search,
+    douyin_search_fallback,
     douyin_user_videos,
     get_content_fans_portrait,
     get_account_fans_portrait,
@@ -136,6 +137,7 @@ async def run_agent(
 
     allowed_tools = [
         "douyin_search",
+        "douyin_search_fallback",
         "douyin_user_videos",
         "get_content_fans_portrait",
         "get_account_fans_portrait",

+ 4 - 2
examples/content_finder/db/store_results.py

@@ -31,8 +31,8 @@ def upsert_good_authors(
         return 0
 
     sql = """
-    INSERT INTO demand_find_author (trace_id, author_name, author_link, elderly_ratio, elderly_tgi, remark)
-    VALUES (%s, %s, %s, %s, %s, %s)
+    INSERT INTO demand_find_author (trace_id, author_name, author_link, elderly_ratio, elderly_tgi, remark, content_tags)
+    VALUES (%s, %s, %s, %s, %s, %s, %s)
     ON DUPLICATE KEY UPDATE
       elderly_ratio = VALUES(elderly_ratio),
       elderly_tgi = VALUES(elderly_tgi),
@@ -58,6 +58,7 @@ def upsert_good_authors(
             elderly_ratio = acc.get("age_50_plus_ratio") or ""
             elderly_tgi = acc.get("age_50_plus_tgi") or ""
             remark = acc.get("reason") or acc.get("remark") or ""
+            content_tags = acc.get("content_tags") or ""
             cur.execute(
                 sql,
                 (
@@ -67,6 +68,7 @@ def upsert_good_authors(
                     str(elderly_ratio) if elderly_ratio is not None else None,
                     str(elderly_tgi) if elderly_tgi is not None else None,
                     remark or None,
+                    content_tags or None,
                 ),
             )
             rows += cur.rowcount

+ 1 - 1
examples/content_finder/skills/content_finding_strategy.md

@@ -11,7 +11,7 @@ description: 内容搜索方法论
 
 ## 第一步:关键词提取
 
-- 从`需求分析`中提取若干搜索词。
+- 搜索词来自于`需求分析`步骤的输出,禁止将特征作为搜索词。
 - 确定目标数量 **M**(如"找10条",则 M = 10)
 
 ---

+ 51 - 41
examples/content_finder/skills/demand_analysis.md

@@ -7,7 +7,7 @@ description: 需求分析
 
 ## 目标
 
-将需求拆解为两类可执行路径:
+将需求特征拆解为两类可执行路径:
 - **寻找路径**:决定去哪找、用什么词找、走即时还是积累通道
 - **判别路径**:决定候选内容是否符合目标与风格
 
@@ -37,12 +37,45 @@ description: 需求分析
 
 ## 二、双起点策略(高赞case出发 / 特征出发)
 
+### 0) 先做“可执行的特征分层”(把输入变成决策依据)
+
+实际输入通常是一串**逗号分隔的特征词**(例如:`"养老,防骗,口播,三段式"`)。在选择策略前,必须先完成两级划分:
+
+#### 第一级:实质 vs 形式
+
+- **实质特征**:内容讲什么(决定“去哪找/用什么词找”)
+- **形式特征**:内容怎么讲(决定“怎么判别/怎么排序”)
+
+输出两组:
+- `substance_terms`: `[...]`
+- `form_terms`: `[...]`
+
+#### 第二级:上层 vs 下层(仅对“实质特征”再分层)
+
+- **上层(泛化)**:主题类目(覆盖扩展用),如:健康、养老、家庭关系
+- **下层(具象)**:可直接形成搜索词/选题点(精准命中用),如:退休金被骗怎么办、高血压晨起注意事项
+
+输出两组:
+- `upper_substance_terms`: `[...]`
+- `lower_substance_terms`: `[...]`
+
+> 重要:**形式特征不参与“上层/下层”分层**,它们只进入后续的判别规则(如表达结构、节奏、可分享程度)。
+
+### 0.1) 策略选择规则(可并行执行)
+
+根据上述分层结果决定要执行哪些起点策略:
+
+- **当 `lower_substance_terms` 非空**:执行 **A. 高赞case出发**(用 `get_video_topic` 拉“灵感点/目的点/关键点”)
+- **当 `upper_substance_terms` 非空**:执行 **B. 特征出发**(构建主题树 → 下钻出可执行词)
+- **两者都非空**:A + B **都执行**,最后合并去重
+- **只有形式词/实质词为空**:承认信息不足,只能先按用户原话/补充提问(或用最少假设)生成候选搜索词包;不要编造“高赞case选题点”
+
 ### A. 高赞case出发(优先用于下层特征)
 
 适用:需求里已有具象表达,或需要从案例中补全/完善搜索词。  
 动作:
-1. 调用**`get_video_topic`**工具
-2. 将工具返回的选题点按用途拆分:
+1. 输入:使用 `lower_substance_terms`(或用户给的具象词)拼成 `features`(逗号分隔字符串)调用 **`get_video_topic`**
+2. 将工具返回 `metadata.videos` 内的选题点按用途拆分:
    - `灵感点` -> 用于构建**搜索词包**(写入寻找清单的候选词)
    - `目的点` -> 用于构建**判别目标**(写入判别清单的“该对齐什么”)
    - `关键点` -> 用于构建**判别锚点/规则**(写入判别清单的“怎么判”)
@@ -56,48 +89,32 @@ description: 需求分析
 
 适用:需求偏抽象,先建立主题覆盖框架。  
 动作:
-1. 以上层特征构建主题树(主题 -> 子主题 -> 关键词)
-2. 结合库内优质作者特征做扩展
-3. 同步下钻到可执行词,进入搜索
+1. 输入:使用 `upper_substance_terms` 作为主题根
+2. 以上层特征构建主题树(主题 -> 子主题 -> 关键词)
+3. 将树上的子主题/关键词**下钻成可执行搜索词**(落到能直接丢给 `douyin_search` 的词)
+4. 结合库内优质作者特征做扩展(可选:`find_authors_from_db` → `douyin_user_videos`)
+5. 合并得到 `寻找清单_feature`,进入搜索阶段
 
 > 两条起点可并行,不互斥;最后合并去重。
 
 ---
 
-## 三、双通道规划(即时型 / 积累型)
-
-### 即时型(短周期拿结果)
-
-- `搜索`:按词直接搜内容
-- `带索引榜单`:用榜单/索引做快速补充
-
-适合:当次任务需快速产出可用候选。
-
-### 积累型(长期稳定供给)
-
-- `垂类推荐流`:沉淀稳定主题流
-- `订阅账号`:沉淀优质账号池
-
-适合:持续任务、复用需求、长期选题供给。
-
----
-
-## 四、完整执行流程
+## 三、完整执行流程
 
 1. **拆需求**:标注每个特征为`实质/形式` + `上层/下层`
 2. **选起点**:
    - 下层优先走`case出发`
    - 上层优先走`特征出发`
-3. **规划通道**:
-   - 当次交付优先`即时型`
-   - 长期复用补上`积累型`
-4. **生成两份清单**:
+3. **生成两份清单**:
    - `寻找清单`:词包、来源、通道、优先级
    - `判别清单`:形式规则、打分点、淘汰条件
 
+## 四、限制
+输出**必须**基于`get_video_topic`返回的选题信息生成,**严谨模型自己联想**生成
+
 ---
 
-## 五、输出模板
+## 六、输出模板
 
 ```json
 {
@@ -115,12 +132,6 @@ description: 需求分析
     "goodcase_goal_points": [],
     "goodcase_key_points": []
   },
-  "finding_plan": {
-    "instant_channels": ["搜索", "带索引榜单"],
-    "accumulation_channels": ["垂类推荐流", "订阅账号"],
-    "instant_search_terms_from_goodcase": [],
-    "merged_terms": []
-  },
   "filter_plan": {
     "form_rules": [],
     "goal_alignment_rules": [],
@@ -132,11 +143,10 @@ description: 需求分析
 
 ---
 
-## 、质量自检
-
+## 、质量自检
+- 输出是否完全基于 `get_video_topic` 工具的输出
 - 是否先完成了`实质/形式`与`上层/下层`双重标注
-- 下层特征是否调用了 goodcase 选题工具做补全
+- 下层特征是否调用了 `get_video_topic`选题工具做补全
 - 是否同时考虑了`case出发`与`特征出发`
-- goodcase 的三类选题点是否分别落到:`灵感点->搜索词包`、`目的点->判别目标`、`关键点->判别规则`
-- 是否明确拆分`即时型`与`积累型`执行动作
+- 三类选题点是否分别落到:`灵感点->搜索词包`、`目的点->判别目标`、`关键点->判别规则`
 - 是否输出了可直接进入“搜索阶段”和“筛选阶段”的清单

+ 1 - 1
examples/content_finder/skills/high_quality_account.md

@@ -14,5 +14,5 @@ description: 优质账号扩展
 3. 通过筛选的作品加入候选池,标注来源"优质账号扩展"
 
 ### 必须在输出中说明
-- 发现优质账号:说明账号名、目标人群占比、tgi,以及扩展了哪些作品
+- 发现优质账号:说明账号名、目标人群占比、tgi,以及扩展了哪些作品、账号内容特征(历史人物/生活科普/搞笑娱乐等特征标签)
 - 未发现:说明"未发现符合扩展条件的优质账号(需占比 > 60% 且 tgi > 120)"

+ 2 - 1
examples/content_finder/skills/output_schema.md

@@ -32,7 +32,8 @@ description: 输出结果指南
         "author_nickname": "<作者名>",
         "author_sec_uid": "<完整 sec_uid>",
         "age_50_plus_ratio": null,
-        "age_50_plus_tgi": null
+        "age_50_plus_tgi": null,
+        "content_tags": "账号内容特征"
       }
     ]
   },

+ 2 - 0
examples/content_finder/tools/__init__.py

@@ -10,9 +10,11 @@ from .aigc_platform_api import create_crawler_plan_by_douyin_content_id, create_
 from .think_and_plan import think_and_plan
 from .find_authors_from_db import find_authors_from_db
 from .get_video_topic import get_video_topic
+from .douyin_search_fallback import douyin_search_fallback
 
 __all__ = [
     "douyin_search",
+    "douyin_search_fallback",
     "douyin_user_videos",
     "get_content_fans_portrait",
     "get_account_fans_portrait",

+ 2 - 2
examples/content_finder/tools/douyin_search_fallback.py

@@ -1,5 +1,5 @@
 """
-抖音关键词搜索工具(示例
+抖音关键词搜索工具(备用
 
 调用内部爬虫服务进行抖音关键词搜索。
 """
@@ -33,7 +33,7 @@ DEFAULT_TIMEOUT = 60.0
 
 
 @tool(description="通过关键词搜索抖音视频内容兜底接口")
-async def douyin_search(
+async def douyin_search_fallback(
     keyword: str,
     content_type: str = "0",
     sort_type: str = "0",