|
|
@@ -1,7 +1,8 @@
|
|
|
"""
|
|
|
-在流程结束后写入**内容策略表格** JSON。
|
|
|
+在流程结束后写入**内容策略表格** JSON,并回写 MySQL。
|
|
|
|
|
|
输出路径:{OUTPUT_DIR}/{trace_id}/process_trace.json
|
|
|
+每条策略行另按 (trace_id, aweme_id) 更新 demand_find_content_result.process_trace(TEXT)。
|
|
|
"""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
@@ -15,6 +16,8 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
from agent.tools import tool, ToolResult
|
|
|
from utils.tool_logging import format_tool_result_for_log, log_tool_call
|
|
|
|
|
|
+from db import update_process_trace_by_aweme_id
|
|
|
+
|
|
|
_LOG_LABEL = "工具调用:exec_summary -> 写入过程 trace JSON"
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
@@ -292,10 +295,51 @@ def _write_process_trace(*, trace_id: str, payload: Dict[str, Any]) -> Path:
|
|
|
return path
|
|
|
|
|
|
|
|
|
+def _sync_process_trace_rows_to_mysql(*, trace_id: str, rows: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
+ """
|
|
|
+ 将每条归一化后的策略行序列化为 JSON 文本,按 (trace_id, aweme_id) 更新 process_trace 与 channel。
|
|
|
+
|
|
|
+ channel 当前统一为「抖音」(与 process_trace.json 内 channel「抖音搜索」区分)。
|
|
|
+ 表中无匹配行时 rowcount 为 0,计入 skipped。
|
|
|
+ """
|
|
|
+ updated = 0
|
|
|
+ skipped = 0
|
|
|
+ errors: List[str] = []
|
|
|
+ for row in rows:
|
|
|
+ aweme_id = str(row.get("aweme_id") or "").strip()
|
|
|
+ if not aweme_id:
|
|
|
+ skipped += 1
|
|
|
+ continue
|
|
|
+ text = json.dumps(row, ensure_ascii=False)
|
|
|
+ try:
|
|
|
+ n = update_process_trace_by_aweme_id(
|
|
|
+ trace_id=trace_id,
|
|
|
+ aweme_id=aweme_id,
|
|
|
+ process_trace_text=text,
|
|
|
+ channel="抖音",
|
|
|
+ )
|
|
|
+ if n > 0:
|
|
|
+ updated += 1
|
|
|
+ else:
|
|
|
+ skipped += 1
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(
|
|
|
+ "process_trace 回写 MySQL 失败 trace_id=%s aweme_id=%s: %s",
|
|
|
+ trace_id,
|
|
|
+ aweme_id,
|
|
|
+ e,
|
|
|
+ exc_info=True,
|
|
|
+ )
|
|
|
+ errors.append(f"{aweme_id}: {e}")
|
|
|
+ return {"updated": updated, "skipped": skipped, "errors": errors}
|
|
|
+
|
|
|
+
|
|
|
@tool(
|
|
|
description=(
|
|
|
"在**全部流程执行完毕之后**调用:把每条最终入选内容的「选择策略」整理成表格 JSON,"
|
|
|
- "写入当前任务的 output 目录下的 process_trace.json,便于后续复盘。"
|
|
|
+ "写入当前任务的 output 目录下的 process_trace.json,便于后续复盘;"
|
|
|
+ "并将每一行策略 JSON 序列化为文本,按 trace_id + aweme_id 回写到 "
|
|
|
+ "demand_find_content_result.process_trace,并同步将 channel 字段设为「抖音」。"
|
|
|
"参数 summary_json 为 JSON 字符串,可以是数组或对象(对象需包含 rows)。"
|
|
|
"可选参数 log_path/log_text 用于传入本次运行日志(便于复盘留档/未来扩展)。"
|
|
|
),
|
|
|
@@ -352,15 +396,26 @@ async def exec_summary(
|
|
|
log_tool_call(_LOG_LABEL, call_params, format_tool_result_for_log(err))
|
|
|
return err
|
|
|
|
|
|
+ rows = payload.get("rows") or []
|
|
|
+ mysql_meta: Dict[str, Any]
|
|
|
+ try:
|
|
|
+ mysql_meta = _sync_process_trace_rows_to_mysql(trace_id=tid, rows=rows if isinstance(rows, list) else [])
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning("process_trace 批量回写 MySQL 异常: %s", e, exc_info=True)
|
|
|
+ mysql_meta = {"updated": 0, "skipped": 0, "errors": [str(e)]}
|
|
|
+
|
|
|
out = ToolResult(
|
|
|
title="过程摘要",
|
|
|
- output=f"已写入 {path}",
|
|
|
+ output=f"已写入 {path};MySQL process_trace 已更新 {mysql_meta.get('updated', 0)} 条",
|
|
|
metadata={
|
|
|
"ok": True,
|
|
|
"trace_id": tid,
|
|
|
"path": str(path),
|
|
|
"log_path": (log_path or "").strip(),
|
|
|
"log_text_len": len((log_text or "").strip()),
|
|
|
+ "mysql_process_trace_updated": mysql_meta.get("updated", 0),
|
|
|
+ "mysql_process_trace_skipped": mysql_meta.get("skipped", 0),
|
|
|
+ "mysql_process_trace_errors": mysql_meta.get("errors") or [],
|
|
|
},
|
|
|
)
|
|
|
log_tool_call(_LOG_LABEL, {"trace_id": tid}, format_tool_result_for_log(out))
|