| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- """
- 人群定向工具 — 定向策略与人群包管理
- """
- import logging
- from typing import Any, Dict, List, Optional
- from agent.tools import tool
- from agent.tools.models import ToolResult
- from examples.auto_put_ad.tools.ad_api import _post, _check, DEFAULT_ACCOUNT_ID
- logger = logging.getLogger(__name__)
- # 本业务常用的年龄区间定向配置
- AGE_TARGETING_PRESETS = {
- "18-24": [{"min": 18, "max": 24}],
- "25-30": [{"min": 25, "max": 30}],
- "25-35": [{"min": 25, "max": 35}],
- "31-40": [{"min": 31, "max": 40}],
- "35-45": [{"min": 35, "max": 45}],
- "18-35": [{"min": 18, "max": 35}],
- "25-45": [{"min": 25, "max": 35}, {"min": 35, "max": 45}],
- }
- @tool(description="生成定向设置(targeting结构体),根据人群包和年龄段组合生成可直接传入ad_create的targeting参数")
- async def audience_build_targeting(
- custom_audience_ids: Optional[List[int]] = None,
- excluded_audience_ids: Optional[List[int]] = None,
- age_preset: Optional[str] = None,
- age_ranges: Optional[List[Dict[str, int]]] = None,
- gender: Optional[str] = None,
- geo_regions: Optional[List[int]] = None,
- user_os: Optional[List[str]] = None,
- ) -> ToolResult:
- """生成腾讯广告 targeting 结构体,直接用于 ad_create 的 targeting 参数。
- Args:
- custom_audience_ids: 自有人群包ID列表(定向投放给这些人群)
- excluded_audience_ids: 排除人群包ID列表(不投给这些人)
- age_preset: 预设年龄区间名称,可选:
- "18-24", "25-30", "25-35", "31-40", "35-45", "18-35", "25-45"
- age_ranges: 自定义年龄区间,如 [{"min": 25, "max": 35}]
- (age_preset 和 age_ranges 二选一,age_ranges 优先)
- gender: 性别定向 "MALE" / "FEMALE",不传则不限
- geo_regions: 地域ID列表(省市区县,不传则全国)
- user_os: 操作系统 ["IOS"] / ["ANDROID"] / ["IOS", "ANDROID"],不传则不限
- Returns:
- targeting 字典,可直接传给 ad_create(targeting=...)
- """
- targeting: Dict[str, Any] = {}
- if custom_audience_ids:
- targeting["custom_audience"] = custom_audience_ids
- if excluded_audience_ids:
- targeting["excluded_custom_audience"] = excluded_audience_ids
- # 年龄定向
- if age_ranges:
- targeting["age"] = age_ranges
- elif age_preset:
- if age_preset not in AGE_TARGETING_PRESETS:
- return ToolResult(
- title="audience_build_targeting 失败",
- output=f"不支持的 age_preset: {age_preset},可选:{list(AGE_TARGETING_PRESETS.keys())}"
- )
- targeting["age"] = AGE_TARGETING_PRESETS[age_preset]
- if gender:
- targeting["gender"] = gender
- if geo_regions:
- targeting["geo_location"] = {"regions": geo_regions}
- if user_os:
- targeting["user_os"] = user_os
- if not targeting:
- return ToolResult(title="audience_build_targeting", output="警告:targeting 为空(宽泛定向),将投放给全量用户")
- # 生成可读描述
- desc_parts = []
- if custom_audience_ids:
- desc_parts.append(f"人群包: {custom_audience_ids}")
- if excluded_audience_ids:
- desc_parts.append(f"排除: {excluded_audience_ids}")
- if targeting.get("age"):
- ages = targeting["age"]
- desc_parts.append(f"年龄: {'-'.join(str(a.get('min', '')) + '~' + str(a.get('max', '')) for a in ages)}")
- if gender:
- desc_parts.append(f"性别: {'男' if gender == 'MALE' else '女'}")
- if geo_regions:
- desc_parts.append(f"地域: {len(geo_regions)}个地区")
- if user_os:
- desc_parts.append(f"系统: {'/'.join(user_os)}")
- return ToolResult(
- title="定向设置已生成",
- output="定向参数:" + ",".join(desc_parts),
- metadata={"targeting": targeting},
- )
- @tool(description="查询可用人群包并推荐最优定向组合(基于历史效果数据)")
- async def audience_recommend_targeting(
- optimization_goal: str = "OPTIMIZATIONGOAL_PAGE_VIEW",
- account_id: int = 0,
- ) -> ToolResult:
- """根据优化目标,推荐效果最好的人群包和定向组合方案。
- Args:
- optimization_goal: 优化目标,影响推荐策略
- OPTIMIZATIONGOAL_PAGE_VIEW(关键页面访问)
- OPTIMIZATIONGOAL_CLICK(点击)
- account_id: 广告主账号ID
- """
- from examples.auto_put_ad.tools.ad_api import audience_get_list
- # 查询账户下所有可用人群包
- result = await audience_get_list(account_id=account_id)
- if "失败" in result.title:
- return result
- audiences = (result.metadata or {}).get("list", [])
- if not audiences:
- return ToolResult(
- title="人群推荐",
- output="账户下暂无自定义人群包,建议先上传人群包后再进行精准定向。\n"
- "过渡期可使用宽泛定向(仅年龄+地域),让系统自动探索最优人群。"
- )
- # 按人数排序,推荐人数较大的人群包
- audiences_sorted = sorted(audiences, key=lambda x: x.get("user_count", 0), reverse=True)
- recommendations = []
- for ag in audiences_sorted[:5]:
- recommendations.append(
- f"- [{ag['audience_id']}] {ag.get('name', '未命名')} "
- f"| 覆盖人数: {ag.get('user_count', 0):,}"
- )
- age_presets = ["25-35", "18-35"] if optimization_goal == "OPTIMIZATIONGOAL_CLICK" else ["25-45", "31-40"]
- output = (
- f"推荐定向方案(优化目标: {optimization_goal}):\n\n"
- f"📦 推荐人群包(按覆盖量排序):\n" + "\n".join(recommendations) + "\n\n"
- f"📅 推荐年龄区间:{' / '.join(age_presets)}\n\n"
- f"💡 建议:先用 1-2 个大人群包测试,确认效果后再细分"
- )
- return ToolResult(title="人群定向推荐", output=output)
|