jihuaqiang vor 1 Tag
Ursprung
Commit
3f90074b56

+ 1 - 1
examples/content_finder/README.md

@@ -23,7 +23,7 @@ content_finder/
 ├── run.py                         # 命令行入口(流式输出)
 ├── server.py                      # HTTP 服务入口(FastAPI + APScheduler)
 ├── core.py                        # 共享 Agent 执行逻辑
-├── content_finder.prompt          # System Prompt + User Prompt 模板
+├── content_finder.md          # System Prompt + User Prompt 模板
 ├── .env.example                   # 环境变量模板
 ├── SERVICE.md                     # 服务模式详细说明
 ├── tools/                         # 自定义工具

+ 7 - 4
examples/content_finder/content_finder.prompt → examples/content_finder/content_finder.md

@@ -27,6 +27,7 @@ $system$
 ## 重要约束
 - 只在抖音平台搜索,不要切换到其他平台(小红书、B站等)
 - **严格禁止**调用任何名称以 `browser_` 开头的浏览器工具
+- 每个结论都必须有工具调用证据。
 
 ## 平台背景
 - 平台载体:微信小程序
@@ -35,8 +36,10 @@ $system$
 - 核心指标:分享率、DAU
 
 ## 执行流程(按顺序,禁止跳步)
-1. **搜索阶段**:按 `content_finding_strategy` 执行
-2. **筛选阶段**:按 `content_filtering_strategy` 执行
+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)` 存储到远程数据库
@@ -46,8 +49,8 @@ $system$
 
 ### 画像工具必须调用
 对每条候选内容,**必须**按以下顺序获取画像:
-1. 先调用 `get_content_fans_portrait`,检查 `metadata.has_portrait`
-2. 若 `has_portrait=False`,再调用 `get_account_fans_portrait` 兜底
+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`。
 3. **不允许跳过画像获取直接输出**
 
 ### 输出字段必须严格遵循 Schema

+ 14 - 11
examples/content_finder/core.py

@@ -100,7 +100,7 @@ async def run_agent(
     demand_id = demand_id or DEFAULT_DEMAND_ID
 
     # 加载 prompt
-    prompt_path = Path(__file__).parent / "content_finder.prompt"
+    prompt_path = Path(__file__).parent / "content_finder.md"
     prompt = SimplePrompt(prompt_path)
 
     # output 目录(相对路径相对 content_finder)
@@ -155,19 +155,22 @@ async def run_agent(
         name="内容寻找",
         model=model,
         temperature=temperature,
+        enable_research_flow = False,
+        goal_compression = "none",
+        force_side_branch = None,
         max_iterations=max_iterations,
         tools=allowed_tools,
         extra_llm_params={"max_tokens": 8192},
-        # knowledge=KnowledgeConfig(
-        #     enable_extraction=True,
-        #     enable_completion_extraction=True,
-        #     enable_injection=True,
-        #     owner="content_finder_agent",
-        #     default_tags={"project": "content_finder"},
-        #     default_scopes=["com.piaoquantv.supply"],
-        #     default_search_types=["tool", "usecase", "definition"],
-        #     default_search_owner="content_finder_agent"
-        # )
+        knowledge=KnowledgeConfig(
+            enable_extraction=False,
+            enable_completion_extraction=False,
+            enable_injection=False,
+            # owner="content_finder_agent",
+            # default_tags={"project": "content_finder"},
+            # default_scopes=["com.piaoquantv.supply"],
+            # default_search_types=["tool", "usecase", "definition"],
+            # default_search_owner="content_finder_agent"
+        )
     )
 
     # 执行

+ 1 - 1
examples/content_finder/server.py

@@ -211,7 +211,7 @@ async def create_task(request: TaskRequest):
                 from agent.llm.prompts import SimplePrompt
                 from agent.tools.builtin.knowledge import KnowledgeConfig
 
-                prompt_path = Path(__file__).parent / "content_finder.prompt"
+                prompt_path = Path(__file__).parent / "content_finder.md"
                 prompt = SimplePrompt(prompt_path)
                 trace_dir = os.getenv("TRACE_DIR", ".cache/traces")
                 demand_id_str = str(demand_id) if demand_id is not None else ""

+ 56 - 18
examples/content_finder/skills/content_filtering_strategy.md

@@ -9,6 +9,57 @@ description: 内容筛选方法论
 
 ---
 
+## 阶段零:需求理解结果驱动的需求对齐打分(先于画像)
+
+在进入“阶段一:基础质量筛选”前,使用 `demand_analysis` 的输出做一次需求对齐打分,目的在于:
+- 先把“明显不满足目的点/灵感点”的内容尽早淘汰,减少无效画像调用
+- 在每条内容的 `reason` 中给出“需求对齐的依据”(来自标题/描述可读信息 + goodcase 选题点)
+
+### 需要使用的 demand_analysis 信息
+
+- `entry_strategy.goodcase_goal_points`:用于判定“是否完成了需求的目的/解决了什么问题”
+- `entry_strategy.goodcase_inspiration_points`:用于判定“是否覆盖了可搜索的灵感点/核心表达方向”
+- `entry_strategy.goodcase_key_points`:用于判定“是否包含关键锚点要素(至少满足多数)”
+- `filter_plan.form_rules`:用于判定“形式表达是否与目标一致(结构/语气/可分享表达)”
+
+> 若 `demand_analysis` 输出为空,或无法从标题/描述落到上述要素,请承认不确定性,不要编造匹配结论。
+
+### 可用信息范围
+
+在未获取画像前,仅允许用以下字段做需求对齐判断:
+- `title`(若有)
+- `desc`(来自 `douyin_search` 的搜索结果)
+- 或候选对象里可见的简介/摘要文本(若检索来源不同,请只用已有字段)
+
+### 需求对齐判定规则(可直接执行)
+
+对每条候选内容,按以下规则给出结论(不需要输出数值):
+
+1. 目的点对齐(Goal Alignment,必须项)
+   - 命中:标题/描述里能看出“在做什么、解决什么、给了什么收益/动作”
+   - 不命中:直接淘汰
+2. 灵感点对齐(Inspiration Alignment,必须项)
+   - 命中:标题/描述里体现了 goodcase 的“可搜索灵感点”核心表达方向(允许同义/上下位)
+   - 不命中:直接淘汰
+3. 关键点命中(Key Point Anchors,加分项)
+   - 命中:进入后续阶段时给更高排序倾向
+   - 部分/缺失:也可进入,但排序会更低(在 `reason` 说明缺少哪些关键锚点要素或不确定点)
+4. 形式规则一致(Form-Rule Fit,低权重加分/可选)
+   - 命中:加分或作为排序 tie-breaker
+   - 不命中:不直接淘汰,但在 `reason` 中标注“不匹配/不确定”
+
+### 需求对齐淘汰/保留阈值
+
+- 若目的点对齐“不命中”,直接淘汰
+- 若灵感点对齐“不命中”,直接淘汰
+- 若关键点“部分/缺失”,允许进入后续阶段,但排序更低(并在 `reason` 写清缺失/不确定点)
+- 若形式规则“不命中/不确定”,允许进入后续阶段,但作为低权重扣分或 tie-breaker
+
+### 在输出 reason 中必须包含的要素
+
+对于进入后续画像阶段的候选,在其 `reason` 中至少写明:
+至少包含四项:命中的 `目的点` 状态;命中的 `灵感点` 状态;`关键点`(命中/部分/缺失)与缺失说明或不确定点;形式规则是命中还是不确定(如无法从标题/描述判断)
+
 ## 阶段一:基础质量筛选
 
 在获取画像前先快速过滤,减少不必要的 API 调用。
@@ -24,8 +75,6 @@ description: 内容筛选方法论
 
 评估维度:digg_count(点赞)、comment_count(评论)、share_count(分享)
 
-**相关性评估**:内容描述(desc)是否与需求相关,明显不相关直接过滤。
-
 ---
 
 ## 阶段二:画像匹配筛选
@@ -57,30 +106,19 @@ description: 内容筛选方法论
 
 ---
 
-## 阶段三:优质账号扩展(可选)
-
-### 触发条件
-账号粉丝画像中:目标人群占比 > 60% **且** tgi > 120
-
-### 扩展策略
-1. 调用 `douyin_user_videos(account_id=author.sec_uid)`,获取 5-10 条近期作品
-2. **仅执行阶段一筛选**(热度、相关性),不递归获取画像
-3. 通过筛选的作品加入候选池,标注来源"优质账号扩展"
 
-### 必须在输出中说明
-- 发现优质账号:说明账号名、目标人群占比、tgi,以及扩展了哪些作品
-- 未发现:说明"未发现符合扩展条件的优质账号(需占比 > 60% 且 tgi > 120)"
 
 ---
 
-## 阶段:去重与排序
+## 阶段三:去重与排序
 
 **去重**:按 aweme_id 去重,保留第一次出现的版本。
 
 **排序优先级**:
-1. 画像匹配度(目标人群占比 × tgi)
-2. 热度(点赞、评论、分享综合)
-3. 数据来源可靠性(content_like > account_fans > none)
+1. 需求对齐打分
+2. 画像匹配度(目标人群占比 × tgi)
+3. 热度(点赞、评论、分享综合)
+4. 数据来源可靠性(content_like > account_fans > none)
 
 ---
 

+ 140 - 0
examples/content_finder/skills/demand_analysis.md

@@ -0,0 +1,140 @@
+---
+name: demand_analysis
+description: 需求分析
+---
+
+# 需求分析方法论
+
+## 目标
+
+将需求拆解为两类可执行路径:
+- **寻找路径**:决定去哪找、用什么词找、走即时还是积累通道
+- **判别路径**:决定候选内容是否符合目标与风格
+
+---
+
+## 一、特征分层定义
+
+### 1) 实质特征 vs 形式特征
+
+- **实质特征(主要特征)**:内容“讲什么”,决定寻找方向。  
+  示例:养老、防骗、慢病管理、家庭矛盾。
+- **形式特征(次要特征)**:内容“怎么讲”,决定判别标准。  
+  示例:口播/剧情、强冲突开场、三段式结构、时长节奏。
+
+规则:**实质用于寻找,形式用于判别**。
+
+### 2) 下层特征(具象) vs 上层特征(泛化)
+
+- **下层特征(具象)**:可直接形成词或选题点。  
+  示例:`退休金被骗怎么办`、`高血压晨起注意事项`。
+- **上层特征(泛化)**:抽象类目,覆盖范围大。  
+  示例:`健康`、`养老`、`家庭关系`。
+
+规则:下层偏精准命中,上层偏覆盖扩展。
+
+---
+
+## 二、双起点策略(case出发 / 特征出发)
+
+### A. case出发(优先用于下层特征)
+
+适用:需求里已有具象表达,或需要从案例中补全搜索词。  
+动作:
+1. 调用**查看当前输入特征关联的 goodcase 视频选题内容**工具
+2. 将工具返回的选题点按用途拆分:
+   - `灵感点` -> 用于构建**搜索词包**(写入寻找清单的候选词)
+   - `目的点` -> 用于构建**判别目标**(写入判别清单的“该对齐什么”)
+   - `关键点` -> 用于构建**判别锚点/规则**(写入判别清单的“怎么判”)
+3. 生成两套可并行使用的清单:
+   - `寻找清单_case`:由 `灵感点` 扩展出的即时搜索词(允许 3-5 个同义/上下位词)
+   - `判别清单_case`:由 `目的点` + `关键点` 形成的打分点与淘汰条件草案
+
+### B. 特征出发(优先用于上层特征)
+
+适用:需求偏抽象,先建立主题覆盖框架。  
+动作:
+1. 以上层特征构建主题树(主题 -> 子主题 -> 关键词)
+2. 结合库内优质作者特征做扩展
+3. 同步下钻到可执行词,进入搜索
+
+> 两条起点可并行,不互斥;最后合并去重。
+
+---
+
+## 三、双通道规划(即时型 / 积累型)
+
+### 即时型(短周期拿结果)
+
+- `搜索`:按词直接搜内容
+- `带索引榜单`:用榜单/索引做快速补充
+
+适合:当次任务需快速产出可用候选。
+
+### 积累型(长期稳定供给)
+
+- `垂类推荐流`:沉淀稳定主题流
+- `订阅账号`:沉淀优质账号池
+
+适合:持续任务、复用需求、长期选题供给。
+
+---
+
+## 四、完整执行流程
+
+1. **拆需求**:标注每个特征为`实质/形式` + `上层/下层`
+2. **选起点**:
+   - 下层优先走`case出发`
+   - 上层优先走`特征出发`
+3. **规划通道**:
+   - 当次交付优先`即时型`
+   - 长期复用补上`积累型`
+4. **生成两份清单**:
+   - `寻找清单`:词包、来源、通道、优先级
+   - `判别清单`:形式规则、打分点、淘汰条件
+
+---
+
+## 五、输出模板
+
+```json
+{
+  "feature_parse": {
+    "substance_main_features": [],
+    "form_secondary_features": [],
+    "lower_specific_features": [],
+    "upper_generic_features": []
+  },
+  "entry_strategy": {
+    "case_entry_terms": [],
+    "feature_entry_terms": [],
+    "goodcase_tool_used": true,
+    "goodcase_inspiration_points": [],
+    "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": [],
+    "key_point_scoring_notes": [],
+    "reject_rules": []
+  }
+}
+```
+
+---
+
+## 六、质量自检
+
+- 是否先完成了`实质/形式`与`上层/下层`双重标注
+- 下层特征是否调用了 goodcase 选题工具做补全
+- 是否同时考虑了`case出发`与`特征出发`
+- goodcase 的三类选题点是否分别落到:`灵感点->搜索词包`、`目的点->判别目标`、`关键点->判别规则`
+- 是否明确拆分`即时型`与`积累型`执行动作
+- 是否输出了可直接进入“搜索阶段”和“筛选阶段”的清单

+ 18 - 0
examples/content_finder/skills/high_quality_account.md

@@ -0,0 +1,18 @@
+---
+name: high_quality_account
+description: 优质账号扩展
+---
+
+优质账号扩展(可选)
+
+### 触发条件
+账号粉丝画像中:目标人群占比 > 60% **且** tgi > 120
+
+### 扩展策略
+1. 调用 `douyin_user_videos(account_id=author.sec_uid)`,获取 5-10 条近期作品
+2. **仅执行阶段一筛选**(热度、相关性),不递归获取画像
+3. 通过筛选的作品加入候选池,标注来源"优质账号扩展"
+
+### 必须在输出中说明
+- 发现优质账号:说明账号名、目标人群占比、tgi,以及扩展了哪些作品
+- 未发现:说明"未发现符合扩展条件的优质账号(需占比 > 60% 且 tgi > 120)"

+ 0 - 0
examples/content_finder/tools/get_video_topic.py