Преглед изворни кода

素材召回使用 growth 项目转发请求,鉴权使用合作平台鉴权

wangyunpeng пре 1 недеља
родитељ
комит
42c8f117e7

+ 2 - 2
examples/auto_put_ad_mini/.env.example

@@ -279,8 +279,8 @@ EXTERNAL_RECALL_UV_WINDOW_DAYS=90
 # PIAOQUANTV_VIDEO_MAX_PAGES=3
 # 落地计划保存接口地址
 # PIAOQUANTV_XCX_SAVE_URL=https://tp-open.piaoquantv.com/contentPlatform/plan/xcx/save
-# 视频向量检索服务地址
-# VECTOR_BASE_URL=https://api-internal.piaoquantv.com/videoVector
+# 视频向量检索转发地址(经 growth-manager JWT 校验)
+# VECTOR_BASE_URL=https://tp-open.piaoquantv.com/contentPlatform/videoVector
 # 人群包→素材来源映射(JSON),为空时使用内置默认值
 # VIDEO_RECALL_CROWD_PACKAGE_MAP=
 # 素材来源标签映射(JSON),为空时使用内置默认值

+ 53 - 17
examples/auto_put_ad_mini/tools/material_recall.py

@@ -3,8 +3,8 @@
 业务模型(用户 2026-06-08 确认):
   对一个承接视频(LandingVideo)执行 3 种策略召回素材,合并去重,按 score 取 top N。
 
-接口:POST https://api-internal.piaoquantv.com/videoVector/recallTest/matchByText
-鉴权:不需要(内部 API)
+接口:POST https://tp-open.piaoquantv.com/contentPlatform/videoVector/recallTest/matchByText
+鉴权:复用 contentPlatform 动态登录 token
 
 本质(用户 2026-06-08 确认):**多路召回 → 排序 → 去重**
 
@@ -30,28 +30,68 @@ from typing import Iterable, List, Optional
 
 import httpx
 
-from tools.video_recall import LandingVideo
+from tools.video_recall import (
+    LandingVideo,
+    _build_auth_headers,
+    _is_not_login_error,
+    get_piaoquantv_token,
+    refresh_piaoquantv_token,
+)
 
 logger = logging.getLogger(__name__)
 
 VECTOR_BASE = os.getenv(
     "VECTOR_BASE_URL",
-    "https://api-internal.piaoquantv.com/videoVector",
+    "https://tp-open.piaoquantv.com/contentPlatform/videoVector",
 )
 VECTOR_MATCH_BY_TEXT = f"{VECTOR_BASE}/recallTest/matchByText"
 VECTOR_BATCH_BY_TEXT = f"{VECTOR_BASE}/recallTest/batchByText"
 VECTOR_ALL_CONFIG_CODES = f"{VECTOR_BASE}/videoSearch/getAllConfigCodes"
 
 
+def _request_vector(
+    method: str,
+    url: str,
+    *,
+    json_body: Optional[dict] = None,
+    timeout: int = 30,
+) -> dict:
+    """调用 growth-manager vector 转发接口,token 失效时刷新并重试一次。"""
+    token = get_piaoquantv_token()
+
+    def _do_request(current_token: str):
+        return httpx.request(
+            method,
+            url,
+            json=json_body,
+            headers=_build_auth_headers(current_token),
+            timeout=timeout,
+        )
+
+    resp = _do_request(token)
+    if resp.status_code == 401:
+        logger.warning("[material_recall] vector 转发接口返回 401,刷新 token 并重试")
+        token = refresh_piaoquantv_token()
+        resp = _do_request(token)
+
+    resp.raise_for_status()
+    data = resp.json()
+    if _is_not_login_error(data):
+        logger.warning("[material_recall] vector 转发接口返回 Not_LOGIN(1000),刷新 token 并重试")
+        token = refresh_piaoquantv_token()
+        resp = _do_request(token)
+        resp.raise_for_status()
+        data = resp.json()
+    return data
+
+
 def fetch_all_config_codes() -> dict[str, str]:
     """动态获取 vector 服务支持的全部 configCode → 中文名。
 
     返回 {configCode: 中文名}。
     召回前先调一次,避免硬编码列表过时。
     """
-    resp = httpx.get(VECTOR_ALL_CONFIG_CODES, timeout=15)
-    resp.raise_for_status()
-    data = resp.json()
+    data = _request_vector("GET", VECTOR_ALL_CONFIG_CODES, timeout=15)
     if data.get("code") not in (0, 200):
         raise RuntimeError(
             f"getAllConfigCodes 失败:code={data.get('code')} msg={data.get('msg')}"
@@ -196,14 +236,12 @@ def _call_match_by_text(
         "[material_recall] matchByText q=%r configCode=%s topN=%d",
         query_text[:40], config_code, material_top_n,
     )
-    resp = httpx.post(
+    data = _request_vector(
+        "POST",
         VECTOR_MATCH_BY_TEXT,
-        json=body,
-        headers={"content-type": "application/json", "accept": "application/json"},
+        json_body=body,
         timeout=timeout,
     )
-    resp.raise_for_status()
-    data = resp.json()
 
     code = data.get("code")
     # CommonResponse 可能用 0 或 200 表示成功;以 data 字段为准
@@ -327,14 +365,12 @@ def _call_batch_by_text(
         "[material_recall] batchByText q=%r configCodes=%d displayK=%d simT=%.2f wCtr=%.2f",
         query_text[:40], len(config_codes), display_k, sim_threshold, w_ctr,
     )
-    resp = httpx.post(
+    data = _request_vector(
+        "POST",
         VECTOR_BATCH_BY_TEXT,
-        json=body,
-        headers={"content-type": "application/json", "accept": "application/json"},
+        json_body=body,
         timeout=timeout,
     )
-    resp.raise_for_status()
-    data = resp.json()
     if data.get("code") not in (0, 200, "0", "200"):
         raise RuntimeError(
             f"batchByText 失败:code={data.get('code')} msg={data.get('msg') or data.get('message')}"