supeng 14 часов назад
Родитель
Сommit
6ee20e425a

+ 34 - 5
examples/content_finder/content_finder.prompt

@@ -41,7 +41,7 @@ $system$
   - need_gender=True: 性别分布
   - need_province=True: 省份分布
   - need_city_level=True: 城市等级分布
-- 偏好度(tgi)> 1.0 表示该人群偏好高于平均水平
+- 偏好度(tgi)> 100 表示该人群偏好高于平均水平,= 100 表示平均水平,< 100 表示低于平均
 
 ### 4. get_account_fans_portrait(账号粉丝画像)
 - 参数 account_id 使用 author.sec_uid
@@ -68,14 +68,43 @@ $system$
 - 检查参数是否正确(ID格式、关键词等)
 - 调整参数后重试
 
-请按照 content_finding_strategy 和 content_filtering_strategy 中的方法论执行任务。
+## 执行要求(必须遵守)
+
+**画像获取(必须执行)**:
+1. 对每条候选内容,先调用 get_content_fans_portrait 获取点赞用户画像
+2. 如果返回空数据或只有标题无画像内容,必须调用 get_account_fans_portrait 获取账号粉丝画像作为兜底
+3. 在结果中明确标注数据来源("内容点赞画像"或"账号粉丝画像")
+
+**判断画像缺失的标准**:
+- 工具返回的 output 中只有标题行和链接,没有【年龄分布】等画像数据
+- 或者 output 中包含"暂无画像数据"
+
+**优质账号扩展(必须执行)**:
+- 如果账号粉丝画像中,目标人群占比 > 60% 且 tgi > 120
+- 必须调用 douyin_user_videos 获取该账号的 5-10 条作品
+- 对扩展作品做基础筛选(热度、相关性)
+
+**Token 管理(必须遵守)**:
+- 如果用户要求 M 条内容,只搜索 N = M × 2 条,不超过
+- 分批处理:先处理 10 条候选内容,不足再继续下一批
+- 每次最多并行调用 3 个画像工具
+- 避免一次性处理过多内容导致响应被截断
+
+**输出格式(必须包含)**:
+每条推荐内容必须包含以下链接:
+- 内容链接:https://www.douyin.com/video/{aweme_id}
+- 作者链接:https://www.douyin.com/user/{author.sec_uid}
+- 画像链接(如果获取了画像数据):
+  - 内容点赞画像:https://douhot.douyin.com/video/detail?active_tab=video_fans&video_id={aweme_id}
+  - 账号粉丝画像:https://douhot.douyin.com/creator/detail?active_tab=creator_fans_portrait&creator_id={author.sec_uid}
+
+请按照 content_finding_strategy_v2 和 content_filtering_strategy_v2 中的方法论执行任务。
 
 $user$
-孩子军抗日,让人感动。找这样的视频。
+找10个和“伊朗局势”相关的,老年人感兴趣的视频。
 
 要求:
-- 内容要有情感共鸣
-- 适合老年人观看
+- 适合老年人分享观看
 - 热度要高,质量要好
 
 请开始执行内容寻找任务。记住要多步推理,每次只执行一小步,然后思考下一步该做什么。

+ 1 - 0
examples/content_finder/run.py

@@ -96,6 +96,7 @@ async def main():
         temperature=temperature,
         max_iterations=max_iterations,
         tools=allowed_tools,  # 限制工具范围
+        extra_llm_params={"max_tokens": 8192},  # 增加输出 token 限制,避免被截断
     )
 
     # 执行

+ 30 - 1
examples/content_finder/skills/content_filtering_strategy.md

@@ -18,15 +18,44 @@
 
 ### 第二步:评估每条内容
 
+**分阶段筛选**:
+- 阶段一:先进行基础质量筛选(热度、相关性),快速过滤明显不符合的内容
+- 阶段二:对通过基础筛选的内容,分批获取画像数据进行精细评估
+- 分批策略:先处理 10 条,不足再继续下一批
+- 目的:减少不必要的画像 API 调用,提高效率,避免 token 超限
+
+**画像数据获取策略**:
+- 优先使用内容点赞用户画像(get_content_fans_portrait)
+- 当点赞画像数据缺失时(返回只有标题无画像内容,或包含"暂无画像数据"),使用该内容作者的账号粉丝画像(get_account_fans_portrait)作为兜底
+- 在结果中明确标注数据来源("内容点赞画像"或"账号粉丝画像")
+
+**优质账号扩展**:
+- 如果某个账号的粉丝画像非常符合要求(如目标人群占比 > 60% 且 tgi > 120)
+- 可以获取该账号的其他作品作为补充(5-10条)
+- 对扩展作品仅做基础筛选,不再递归获取画像
+
+**输出链接要求**:
+每条推荐内容必须包含:
+- 内容链接:https://www.douyin.com/video/{aweme_id}
+- 作者链接:https://www.douyin.com/user/{author.sec_uid}
+- 画像链接(如果有):
+  - 内容点赞画像:https://douhot.douyin.com/video/detail?active_tab=video_fans&video_id={aweme_id}
+  - 账号粉丝画像:https://douhot.douyin.com/creator/detail?active_tab=creator_fans_portrait&creator_id={author.sec_uid}
+
 **量化指标**:有具体数字的直接比较(点赞量、分享率、年龄占比等)。
 
 **质性判断**:无法量化时,寻找间接信号:
 - 「感人」→ 看评论中是否有情感表达
 - 「有价值」→ 看收藏率是否高于平均水平
-- 「适合XX」→ 看画像偏好度是否大于1.0
+- 「适合XX」→ 看画像偏好度是否大于 100(tgi > 100 表示高于平均)
 
 **相对比较**:在候选集中排序,而不是套用固定门槛。同一批结果里,谁更好就推谁。
 
+**去重机制**:
+- 按 aweme_id 去重,避免重复推荐相同内容
+- 不同来源(不同关键词、账号扩展)可能搜到相同内容
+- 保留第一次出现的版本,或合并多个来源的标签
+
 ### 第三步:分层输出
 
 不是非此即彼,而是按匹配度分层:

+ 202 - 0
examples/content_finder/skills/content_filtering_strategy_v2.md

@@ -0,0 +1,202 @@
+# 内容筛选方法论 V2(优化版)
+
+## 核心方法:分阶段筛选 → 画像兜底 → 分层输出
+
+本方案采用结构化的筛选流程,明确每个阶段的评估标准和执行顺序。
+
+---
+
+## 完整筛选流程
+
+### 阶段一:基础质量筛选
+
+在获取画像数据前,先进行快速的基础筛选,减少不必要的 API 调用。
+
+#### 热度评估
+
+**量化标准**(可根据需求调整):
+- 1000+: 一般热度
+- 5000+: 较高热度
+- 10000+: 高热度
+- 50000+: 爆款
+
+**评估维度**:
+- 点赞数(digg_count)
+- 评论数(comment_count)
+- 分享数(share_count)
+
+**筛选策略**:
+- 根据用户需求设定最低热度门槛
+- 如果用户未明确要求,保持宽松标准
+
+#### 相关性评估
+
+**评估依据**:
+- 内容描述(desc)是否包含关键词
+- 内容主题是否与需求相关
+- 明显不相关的内容直接过滤
+
+**输出**:保留通过基础筛选的候选内容列表
+
+---
+
+### 阶段二:画像匹配筛选
+
+对通过基础筛选的内容,进行精细的画像匹配评估。
+
+#### 画像数据获取策略
+
+**优先级1:内容点赞用户画像**
+- 调用 `get_content_fans_portrait(content_id=aweme_id)`
+- 如果返回有效画像数据:
+  - 评估是否符合目标人群
+  - 在结果中标注"数据来源:内容点赞画像"
+
+**优先级2:账号粉丝画像(兜底)**
+- 如果点赞画像数据缺失(返回只有标题无画像内容,或包含"暂无画像数据")
+- 调用 `get_account_fans_portrait(account_id=author.sec_uid)`
+- 如果返回有效画像数据:
+  - 评估是否符合目标人群
+  - 在结果中标注"数据来源:账号粉丝画像(内容点赞画像缺失)"
+
+**优先级3:无画像数据**
+- 如果两种画像都无法获取
+- 仅基于热度和相关性评估
+- 在结果中标注"数据来源:无画像数据"
+
+#### 画像评估标准
+
+**从需求中提取目标人群**:
+- 用户明确说的条件,作为硬性标准(必须满足)
+- 用户模糊描述的,作为软性标准(尽量满足)
+
+**量化评估**:
+- **占比(ratio/percentage)**:目标人群在总体中的占比
+- **偏好度(tgi/preference)**:> 100 表示该人群偏好高于平均水平,= 100 表示平均,< 100 表示低于平均
+
+**举例**:
+- 需求:"适合50岁以上老年人"
+- 评估:年龄分布中"50岁以上"的占比和 tgi
+- 判断:占比 > 40% 且 tgi > 100 为符合
+
+**质性判断**:
+- 无法量化时,寻找间接信号
+- 「感人」→ 看评论中是否有情感表达
+- 「有价值」→ 看收藏率是否高于平均水平
+
+**输出**:筛选出符合目标人群的内容
+
+---
+
+### 阶段三:优质账号扩展
+
+识别优质账号并扩展其作品,作为补充内容。
+
+#### 优质账号识别标准
+
+**量化标准**:
+- 账号粉丝画像中,目标人群占比 > 60%
+- 目标人群偏好度(tgi)> 120
+
+**判断时机**:
+- 在阶段二中,如果某个账号的粉丝画像非常符合要求
+- 标记为"优质账号"
+
+#### 扩展策略
+
+**获取账号作品**:
+- 调用 `douyin_user_videos(account_id=author.sec_uid)`
+- 限制数量:5-10 条近期作品
+
+**筛选扩展作品**:
+- **仅执行阶段一筛选**(热度、相关性)
+- **不再递归获取画像**,避免无限展开
+- 假设该账号的其他作品也符合目标人群
+
+**加入候选池**:
+- 将通过筛选的扩展作品加入候选池
+- 标注"来源:优质账号扩展"
+
+---
+
+### 阶段四:去重与排序
+
+#### 去重机制
+
+**去重依据**:
+- 按 aweme_id 去重
+- 不同关键词或不同来源可能搜到相同内容
+
+**去重策略**:
+- 保留第一次出现的版本
+- 合并多个来源的标签(如"关键词A + 优质账号扩展")
+
+#### 排序策略
+
+**综合排序**:
+- 优先级1:画像匹配度(目标人群占比 × tgi)
+- 优先级2:热度(点赞、评论、分享综合)
+- 优先级3:数据来源(内容点赞画像 > 账号粉丝画像 > 无画像)
+
+**相对比较**:
+- 在候选集中排序,而不是套用固定门槛
+- 同一批结果里,谁更好就推谁
+
+---
+
+### 阶段五:分层输出
+
+不是非此即彼,而是按匹配度分层。
+
+#### 输出分层
+
+**强烈推荐**:
+- 核心标准全部满足(画像匹配 + 热度高)
+- 数据来源可靠(内容点赞画像或账号粉丝画像)
+
+**推荐**:
+- 核心标准满足,有小瑕疵
+- 如:画像匹配但热度一般,或热度高但画像数据缺失
+
+**可选**:
+- 核心标准基本满足,作为补充
+- 如:优质账号扩展的作品
+
+**不推荐**:
+- 核心标准不满足,明确说明原因
+- 如:画像不匹配、热度过低
+
+#### 输出内容
+
+**每条内容包含**:
+- 内容基本信息(ID、描述、作者)
+- 热度数据(点赞、评论、分享)
+- 画像数据(目标人群占比、tgi)
+- 数据来源标注
+- 推荐理由
+- **必须包含链接**:
+  - 内容链接:https://www.douyin.com/video/{aweme_id}
+  - 作者链接:https://www.douyin.com/user/{author.sec_uid}
+  - 画像链接(如果有):
+    - 内容点赞画像:https://douhot.douyin.com/video/detail?active_tab=video_fans&video_id={aweme_id}
+    - 账号粉丝画像:https://douhot.douyin.com/creator/detail?active_tab=creator_fans_portrait&creator_id={author.sec_uid}
+
+**说明评估逻辑**:
+- 让用户理解为什么这条内容被推荐或排除
+- 透明展示评估过程
+
+---
+
+## 关键原则
+
+**标准来自需求**:评估维度随需求变化,不固化。
+
+**分阶段筛选**:先快速过滤,再精细评估,提高效率。
+
+**画像兜底策略**:优先内容画像,缺失时用账号画像,确保数据覆盖。
+
+**说明评估逻辑**:让用户理解为什么这条内容被推荐或排除。
+
+**承认不确定性**:数据不足以判断时,如实说明,而不是强行打分。
+
+**让用户决定**:提供充分信息,最终选择权在用户。

+ 22 - 0
examples/content_finder/skills/content_finding_strategy.md

@@ -23,6 +23,12 @@
 - 根据约束条件设置搜索参数
 - 不确定时使用宽松参数,宁可多搜再筛选
 
+**搜索数量控制**:
+- 如果用户要求找 M 条内容,只搜索 N = M × 2 条,不要超过
+- 如果第一次搜索返回超过 N 条,只保留前 N 条处理
+- 目的:控制 token 消耗,确保筛选后有足够余量
+- 示例:用户要10条,只搜索20条候选
+
 **分页策略**:
 - 第一次搜索使用默认 cursor("0" 或 "")
 - 如果结果不够,从返回数据中提取 cursor 值继续获取下一页
@@ -32,6 +38,22 @@
 - 第一轮搜索结果不够好时,调整关键词或参数再搜
 - 不要在一次失败后就放弃
 
+**优质账号扩展**:
+- 如果发现某个账号的粉丝画像非常符合目标人群(如目标人群占比 > 60% 且 tgi > 120)
+- 可以获取该账号的其他作品作为补充:`douyin_user_videos(account_id=author.sec_uid)`
+- 建议限制数量(如5-10条),避免过度依赖单一账号
+- 对扩展作品进行基础筛选(热度、相关性),不需要再次获取画像
+
+**分批处理策略**:
+- 不要一次性处理所有候选内容
+- 先处理前 10 条,筛选后如果符合要求的内容 >= M,停止处理
+- 如果不足,继续处理下一批 10 条
+- 目的:避免一次性调用过多工具导致 token 超限
+
+**工具调用建议**:
+- 每次最多并行调用 3 个画像工具
+- 避免一次性调用过多工具导致响应被截断
+
 ### 第三步:验证结果
 
 拿到搜索结果后,对照需求逐一验证:

+ 173 - 0
examples/content_finder/skills/content_finding_strategy_v2.md

@@ -0,0 +1,173 @@
+# 内容寻找方法论 V2(优化版)
+
+## 核心方法:串行搜索 → 分阶段筛选 → 按需补充
+
+本方案采用更结构化的执行流程,提供明确的数量控制和资源优化策略。
+
+---
+
+## 完整执行流程
+
+### 第一步:需求分析与关键词提取
+
+**提取多个搜索关键词**:
+- 从用户需求中提取核心关键词和扩展关键词
+- 优先使用用户原话中的关键词
+- 必要时补充同义词或相关词
+
+**关键词排序**:
+- 按相关性排序:核心关键词优先,扩展关键词其次
+- 优先级:用户明确说的 > 用户暗示的 > 推测的
+
+**确定目标数量 M**:
+- M = 用户要求的内容数量(如"找10条内容",则 M = 10)
+
+---
+
+### 第二步:串行关键词搜索
+
+**选择关键词**:
+- 从关键词列表中选择优先级最高的未使用关键词
+
+**搜索数量控制**:
+- 只搜索 N 条内容,其中 **N = M × 2,不要超过**
+- 如果第一次搜索返回超过 N 条,只保留前 N 条处理
+- 目的:控制 token 消耗,确保筛选后有足够余量
+
+**分页策略**:
+- 第一次搜索使用默认 cursor("0" 或 "")
+- 如果需要更多结果,使用返回的 cursor 值继续获取
+- 示例:`douyin_search(keyword="...", cursor="返回的cursor值")`
+
+---
+
+### 第三步:分阶段内容筛选
+
+#### 阶段一:基础质量筛选
+
+**热度筛选**:
+- 根据点赞、评论、分享数据过滤低质量内容
+- 参考标准(可根据需求调整):
+  - 1000+: 一般热度
+  - 5000+: 较高热度
+  - 10000+: 高热度
+
+**相关性筛选**:
+- 根据内容描述过滤明显不相关的内容
+- 保留候选内容列表
+
+#### 阶段二:画像匹配筛选
+
+**分批处理策略**:
+- 不要一次性处理所有候选内容
+- 先处理前 10 条候选内容
+- 筛选后如果符合要求的内容 >= M,停止处理
+- 如果不足,继续处理下一批 10 条
+- 目的:避免一次性调用过多工具导致 token 超限
+
+**工具调用限制**:
+- 每次最多并行调用 3 个画像工具
+- 避免一次性调用过多工具导致响应被截断
+
+**对每条候选内容**:
+
+1. **优先获取内容点赞用户画像**:
+   - 调用 `get_content_fans_portrait(content_id=aweme_id)`
+   - 如果画像数据存在:评估是否符合目标人群,标注"内容点赞画像"
+
+2. **画像缺失时的兜底策略**:
+   - 如果点赞画像数据缺失(返回空数据)
+   - 获取该内容作者的账号粉丝画像:`get_account_fans_portrait(account_id=author.sec_uid)`
+   - 评估是否符合目标人群,标注"账号粉丝画像"
+
+3. **画像评估**:
+   - 根据目标人群的占比和偏好度(tgi)判断
+   - 偏好度 > 100 表示该人群偏好高于平均水平,= 100 表示平均,< 100 表示低于平均
+   - 筛选出符合要求的内容
+
+#### 阶段三:优质账号扩展(可选)
+
+**识别优质账号**:
+- 标准:账号粉丝画像匹配度高
+- 量化指标:目标人群占比 > 60% 且 tgi > 120
+
+**扩展策略**:
+- 对优质账号,获取其近期作品:`douyin_user_videos(account_id=author.sec_uid)`
+- 限制数量:5-10 条
+- 对这些作品**仅执行阶段一筛选**(热度、相关性)
+- **不再递归获取画像**,避免无限展开
+- 作为补充内容加入候选池
+
+---
+
+### 第四步:结果评估与补充
+
+**统计当前符合要求的内容数量 C**:
+- C = 通过所有筛选阶段的内容数量
+
+**判断是否需要补充**:
+- 如果 **C >= M**:完成,进入第五步
+- 如果 **C < M × 0.8**:内容不足,选择下一个关键词,回到第二步
+- 如果 **M × 0.8 <= C < M**:接近目标,可选择继续补充或直接输出
+
+---
+
+### 第五步:去重与排序
+
+**去重机制**:
+- 按 aweme_id 去重(不同关键词可能搜到相同内容)
+- 保留第一次出现的版本
+
+**排序策略**:
+- 按匹配度和热度综合排序
+- 优先推荐匹配度高且热度高的内容
+
+**输出结果**:
+- 按分层输出:强烈推荐、推荐、可选
+- 说明每条内容的推荐理由和数据来源
+- **必须包含链接**:
+  - 内容链接:https://www.douyin.com/video/{aweme_id}
+  - 作者链接:https://www.douyin.com/user/{author.sec_uid}
+  - 画像链接(如果有):
+    - 内容点赞画像:https://douhot.douyin.com/video/detail?active_tab=video_fans&video_id={aweme_id}
+    - 账号粉丝画像:https://douhot.douyin.com/creator/detail?active_tab=creator_fans_portrait&creator_id={author.sec_uid}
+
+---
+
+## 错误处理
+
+**服务级错误(HTTP 502/503/504)**:
+- 这是服务暂时不可用,不是参数问题
+- 不要重复尝试相同的调用(最多重试1次)
+- 直接告知用户"服务暂时不可用,请稍后再试"
+- 不要切换到其他平台或工具
+
+**参数错误(HTTP 400/404)**:
+- 检查参数格式是否正确
+- 调整参数后重试
+
+**网络错误(Timeout/Connection)**:
+- 可以重试1-2次
+- 如果持续失败,告知用户网络问题
+
+---
+
+## 关键参数
+
+- **搜索倍数**:N = M × 2
+- **不足阈值**:C < M × 0.8
+- **优质账号标准**:目标人群占比 > 60% 且 tgi > 120
+- **账号扩展数量**:5-10 条
+- **TGI 说明**:tgi > 100 表示高于平均,= 100 表示平均,< 100 表示低于平均
+
+---
+
+## 关键原则
+
+**结构化执行**:严格按照5步流程执行,确保可控性。
+
+**资源优化**:串行搜索,够了就停,避免浪费。
+
+**透明过程**:说明为什么选择这些关键词,用了什么筛选逻辑。
+
+**承认局限**:如果真的找不到符合要求的内容,如实说明。

+ 4 - 4
examples/content_finder/tools/hotspot_profile.py

@@ -47,8 +47,8 @@ async def get_account_fans_portrait(
     Returns:
         ToolResult: 包含粉丝画像数据
         - ratio: 占比(百分比)
-        - tgi (偏好度): > 1.0 表示该人群偏好高于平均水平,< 1.0 表示低于平均
-          例如:50岁以上 tgi=1.5 表示该账号粉丝中50岁以上人群的占比是全平台平均的1.5倍
+        - tgi (偏好度): > 100 表示该人群偏好高于平均水平,< 100 表示低于平均,= 100 表示平均水平
+          例如:50岁以上 tgi=150 表示该账号粉丝中50岁以上人群的偏好度是平台平均的1.5倍
 
     注意:
         - 默认只返回年龄分布,需要其他维度时设置对应参数为 True
@@ -150,8 +150,8 @@ async def get_content_fans_portrait(
     Returns:
         ToolResult: 包含点赞用户画像数据
         - ratio: 占比(百分比)
-        - tgi (偏好度): > 1.0 表示该人群偏好高于平均水平,< 1.0 表示低于平均
-          例如:50岁以上 tgi=1.5 表示该视频点赞用户中50岁以上人群的占比是全平台平均的1.5倍
+        - tgi (偏好度): > 100 表示该人群偏好高于平均水平,< 100 表示低于平均,= 100 表示平均水平
+          例如:50岁以上 tgi=150 表示该视频点赞用户中50岁以上人群的偏好度是平台平均的1.5倍
 
     注意:
         - 默认只返回年龄分布,需要其他维度时设置对应参数为 True