ソースを参照

fix(web2): correct flow ledger pool totals

Sam Lee 1 ヶ月 前
コミット
892a988cb3

+ 10 - 1
content_agent/flow_ledger_service.py

@@ -612,6 +612,15 @@ class FlowLedgerService:
 
     def _asset_summary(self, decisions: list[dict[str, Any]], final_output: dict[str, Any]) -> dict[str, Any]:
         decision_ids = {_text(item.get("decision_id") or item.get("id")) for item in decisions}
+        if not decision_ids:
+            return {
+                "total": 0,
+                "pool_count": 0,
+                "review_count": 0,
+                "technical_retry_count": 0,
+                "reject_count": 0,
+                "headline": _asset_headline(0, 0, 0, 0),
+            }
         sections = {
             "content_assets": "入池",
             "review_records": "待复看",
@@ -624,7 +633,7 @@ class FlowLedgerService:
             rows = [
                 item
                 for item in _list(final_output.get(section))
-                if not decision_ids or _text(item.get("decision_id")) in decision_ids
+                if _text(item.get("decision_id")) in decision_ids
             ]
             counts[label] = len(rows)
             total += len(rows)

+ 432 - 0
tech_documents/AIGC 接口调用说明(create_crawler_plan_by_kuaishou_content_id 链路).md

@@ -0,0 +1,432 @@
+# AIGC 接口调用说明(create\_crawler\_plan\_by\_kuaishou\_content\_id 链路)
+
+本文档对应 `examples/content_finder/tools/aigc_platform_api.py` 中的调用逻辑,覆盖以下 3 个接口:
+
+
+
+1. 创建爬取计划:`/aigc/crawler/plan/save`
+
+2. 查询生成计划详情:`/aigc/produce/plan/detail`
+
+3. 保存生成计划(绑定输入源):`/aigc/produce/plan/save`
+
+## 1\. 通用约定
+
+
+
+- Base URL:`https://aigc-api.aiddit.com`
+
+- 请求方法:`POST`
+
+- Content\-Type:`application/json`
+
+- 统一请求体结构:
+
+```JSON
+{
+  "baseInfo": {
+    "token": "YOUR_TOKEN"
+  },
+  "params": {}
+}
+```
+
+
+
+- 统一响应结构(代码中按此结构读取):
+
+```JSON
+{
+  "code": 0,
+  "msg": "成功",
+  "data": {}
+}
+```
+
+
+
+字段说明:
+
+
+
+- `code`:`0` 表示成功;非 `0` 表示业务失败
+
+- `msg`:失败/提示信息
+
+- `data`:业务数据,具体结构因接口而异
+
+---
+
+
+
+## 2\. 接口一:创建爬取计划
+
+
+
+### 2\.1 URL
+
+
+
+`POST /aigc/crawler/plan/save`
+
+
+
+完整地址:`https://aigc-api.aiddit.com/aigc/crawler/plan/save`
+
+
+
+### 2\.2 关键入参(params)
+
+
+
+`create_crawler_plan_by_douyin_content_id` 在视频 ID 场景下会传:
+
+
+
+- `channel`:7(快手)
+
+- `contentModal`:`4`
+
+- `crawlerComment`:`0`
+
+- `crawlerMode`:`5`(按内容ID抓取)
+
+- `filterAccountMatchMode`:1
+
+- `filterContentMatchMode`:1
+
+- `frequencyType`:`2`
+
+- `inputModeValues`:`[aweme_id1, aweme_id2, ...]`(最多 100 个)
+
+- `name`:爬取计划名称(自动拼接时间)
+
+- `planType`:`2`
+
+- `searchModeValues`:`[]`
+
+- `srtExtractFlag`:`1`
+
+- `videoKeyFrameType`:`1`
+
+- `voiceExtractFlag`:`1`
+
+
+
+### 2\.3 返回数据(代码中实际使用)
+
+
+
+成功时主要读取:
+
+
+
+- `code == 0`
+
+- `data.id`:新建的**爬取计划 ID**(后续绑定生成计划要用)
+
+示例(成功):
+
+
+
+```JSON
+{
+  "code": 0,
+  "msg": "成功",
+  "data": {
+    "id": "1234567890123456789"
+  }
+}
+```
+
+
+
+示例(失败):
+
+
+
+```JSON
+{
+  "code": 10001,
+  "msg": "参数校验失败",
+  "data": null
+}
+```
+
+
+
+### 2\.4 curl 示例
+
+
+
+```Bash
+curl -X POST "https://aigc-api.aiddit.com/aigc/crawler/plan/save" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "baseInfo": {
+      "token": "YOUR_TOKEN"
+    },
+    "params": {
+      "channel": 2,
+      "contentModal": 4,
+      "crawlerComment": 0,
+      "crawlerMode": 5,
+      "filterAccountMatchMode": 2,
+      "filterContentMatchMode": 2,
+      "frequencyType": 2,
+      "inputModeValues": ["7481234567890123456", "7481234567890123457"],
+      "name": "【内容寻找Agent自动创建】抖音视频直接抓取-20260623103000-抖音",
+      "planType": 2,
+      "searchModeValues": [],
+      "srtExtractFlag": 1,
+      "videoKeyFrameType": 1,
+      "voiceExtractFlag": 1
+    }
+  }'
+```
+
+
+
+---
+
+
+
+## 3\. 接口二:查询生成计划详情
+
+
+
+### 3\.1 URL
+
+
+
+`POST /aigc/produce/plan/detail`
+
+
+
+完整地址:`https://aigc-api.aiddit.com/aigc/produce/plan/detail`
+
+
+
+### 3\.2 入参(params)
+
+
+
+- `id`:生成计划 ID(字符串)
+
+### 3\.3 返回数据(代码中实际使用)
+
+
+
+成功时读取:
+
+
+
+- `code == 0`
+
+- `data.name`:生成计划名称
+
+- `data.inputSourceGroups`:输入源分组列表(后续要在其中追加输入源)
+
+说明:
+
+
+
+- 如果 `code != 0` 或 `data` 为空,代码会认为查询失败
+
+示例(成功):
+
+
+
+```JSON
+{
+  "code": 0,
+  "msg": "成功",
+  "data": {
+    "id": "20260408085649635441036",
+    "name": "通用生成计划",
+    "inputSourceGroups": [
+      {
+        "inputSources": [
+          {
+            "inputSourceModal": 4,
+            "inputSourceChannel": 2,
+            "contentType": 1
+          }
+        ]
+      }
+    ]
+  }
+}
+```
+
+
+
+### 3\.4 curl 示例
+
+
+
+```Bash
+curl -X POST "https://aigc-api.aiddit.com/aigc/produce/plan/detail" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "baseInfo": {
+      "token": "YOUR_TOKEN"
+    },
+    "params": {
+      "id": "20260408085649635441036"
+    }
+  }'
+```
+
+
+
+---
+
+
+
+## 4\. 接口三:保存生成计划(绑定输入源)
+
+
+
+### 4\.1 URL
+
+
+
+`POST /aigc/produce/plan/save`
+
+
+
+完整地址:`https://aigc-api.aiddit.com/aigc/produce/plan/save`
+
+
+
+### 4\.2 入参(params)
+
+
+
+代码逻辑是:
+
+
+
+1. 先调用接口二拿到完整 `data`
+
+2. 从 `data.inputSourceGroups` 里找到匹配组(按 `inputSourceModal/inputSourceChannel/contentType`)
+
+3. 在该组 `inputSources` 里追加:
+
+```JSON
+{
+    contentType: 1
+    fieldName: null
+    inputSourceChannel: 7
+    inputSourceLabel: "原始帖子-视频-快手-内容添加计划-快手填空用计划"
+    inputSourceModal: 4
+    inputSourceSubType: null
+    inputSourceType: 2
+    inputSourceValue: "20260622195055175874722" //改成**爬取计划 ID**
+}
+```
+
+
+
+4. 将“修改后的完整生成计划对象”作为 `params` 提交到保存接口
+
+### 4\.3 返回数据(代码中实际使用)
+
+
+
+成功条件:
+
+
+
+- `code == 0`
+
+- `data` 非空
+
+失败条件:
+
+
+
+- `code != 0` 或 `data` 为空(会记录 `msg` 为绑定失败原因)
+
+示例(成功):
+
+
+
+```JSON
+{
+  "code": 0,
+  "msg": "成功",
+  "data": {
+    "id": "20260408085649635441036"
+  }
+}
+```
+
+
+
+### 4\.4 curl 示例
+
+
+
+> 注意:建议把“接口二返回的完整 `data`”原样带上,只增量追加输入源,避免字段缺失导致保存失败。
+> 
+> 
+
+
+
+```Bash
+curl -X POST "https://aigc-api.aiddit.com/aigc/produce/plan/save" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "baseInfo": {
+      "token": "YOUR_TOKEN"
+    },
+    "params": {
+      "id": "20260408085649635441036",
+      "name": "通用生成计划",
+      "inputSourceGroups": [
+        {
+          "inputSources": [
+            {
+              "inputSourceModal": 4,
+              "inputSourceChannel": 2,
+              "contentType": 1
+            },
+            {
+              "contentType": 1,
+              "inputSourceType": 2,
+              "inputSourceValue": "CRAWLER_PLAN_ID_FROM_STEP1",
+              "inputSourceLabel": "原始帖子-视频-抖音-内容添加计划-示例",
+              "inputSourceModal": 4,
+              "inputSourceChannel": 2
+            }
+          ]
+        }
+      ]
+    }
+  }'
+```
+
+
+
+---
+
+
+
+## 5\. 建议调用顺序(与代码一致)
+
+
+
+1. 调用接口一创建爬取计划,拿到 `data.id`(crawler\_plan\_id)
+
+2. 调用接口二查询生成计划详情,拿到完整 `data`
+
+3. 在 `inputSourceGroups` 追加 `crawler_plan_id` 对应输入源
+
+4. 调用接口三保存生成计划
+
+如果你只验证“创建爬取计划”能力,执行接口一即可;如果需要完成“绑定生成计划”,要走完整 1\-\>2\-\>3 链路。
+
+
+

+ 1 - 0
tests/test_flow_ledger_api.py

@@ -545,6 +545,7 @@ def test_flow_ledger_api_returns_business_v4_ledger(tmp_path, monkeypatch):
     assert "分类树:Pattern 执行 581" in category["source"]["details"]
     assert "分类路径:/理念/观念/个人观念/人生观" in category["source"]["details"]
     assert "所在层级:第 4 层" in category["source"]["details"]
+    assert category["asset_summary"]["pool_count"] == 0
 
     videos = client.get(f"/runs/{run_id}/flow-ledger/queries/q_001/videos").json()
     assert videos["summary"]["total"] == 1

+ 6 - 3
web2/features/LedgerPage.tsx

@@ -185,13 +185,16 @@ function HeaderSummaryChips({
   const walkPooled = Number(summary.walk_pooled_count || 0);
   const walkActions = Number(summary.walk_action_total || 0);
   const walkDepth = Number(summary.walk_max_depth || 0);
+  const poolCount = Number(summary.pool_count || 0);
+  const reviewCount = Number(summary.review_count || 0);
+  const technicalRetryCount = Number(summary.technical_retry_count || 0);
   return (
     <div className="header-summary-chips">
       <span className="chip blue">搜索词 {rows.length}</span>
       <span className="chip green">首轮视频 {rows.reduce((sum, row) => sum + row.firstRoundVideoTotal, 0)}</span>
-      <span className="chip yellow">待复看 {rows.reduce((sum, row) => sum + row.ruleSummary.reviewCount, 0)}</span>
-      <span className="chip red">技术问题 {rows.reduce((sum, row) => sum + row.ruleSummary.technicalRetryCount, 0)}</span>
-      <span className="chip pool">入池 {rows.reduce((sum, row) => sum + row.finalAssets.assetCount, 0)}</span>
+      <span className="chip yellow">待复看 {reviewCount}</span>
+      <span className="chip red">技术问题 {technicalRetryCount}</span>
+      <span className="chip pool">入池 {poolCount}</span>
       <span className="chip green">游走到 {walkVideo} 条视频(入池 {walkPooled})</span>
       <span className="chip">游走 {walkActions} 次 · 最深 {walkDepth} 层</span>
       <span className="chip blue">{dataOriginLabel}</span>