diff --git a/collector-service/app/oneliner_features.py b/collector-service/app/oneliner_features.py index fdf932a..7b7dce6 100644 --- a/collector-service/app/oneliner_features.py +++ b/collector-service/app/oneliner_features.py @@ -2682,6 +2682,58 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: ) return bool(row) + def _build_agent_run_recommended_action(row: dict[str, Any], plan: dict[str, Any]) -> dict[str, Any]: + source_screen = str(plan.get("source_screen") or row.get("source_screen") or "").strip().lower() + source_action_key = str(plan.get("source_action_key") or row.get("source_action_key") or "").strip().lower() + intent_key = str(plan.get("intent_key") or row.get("intent_key") or "custom").strip().lower() or "custom" + + def route(action: str, screen: str, label: str, summary: str) -> dict[str, Any]: + return { + "action": action, + "screen": screen, + "label": label, + "summary": summary, + } + + source_routes = { + "strategy": route("goto-strategy", "strategy", "回到我的策略", "继续查看当前用户策略与覆盖状态。"), + "automation": route("goto-automation", "automation", "回到自动流程", "继续检查自动流程和依赖状态。"), + "playbook": route("goto-playbook", "playbook", "回到 Agent", "继续调整 Agent 与平台能力。"), + "agent": route("goto-playbook", "playbook", "回到 Agent", "继续调整 Agent 与平台能力。"), + "production": route("goto-production", "production", "回到生产中心", "继续推进生产任务与恢复动作。"), + "tracking": route("goto-tracking", "tracking", "回到跟踪账号", "继续查看跟踪摘要和更新提醒。"), + "review": route("goto-review", "review", "回到发布与复盘", "继续沉淀复盘结论和发布结果。"), + "discovery": route("goto-discovery", "discovery", "回到找对标", "继续查看账号拆解和高分样本。"), + "intake": route("goto-intake", "projects", "回到我的项目", "继续切换项目或补齐项目基础信息。"), + "projects": route("goto-intake", "projects", "回到我的项目", "继续切换项目或补齐项目基础信息。"), + } + if source_screen == "dashboard" and source_action_key == "homepage-primary-action": + return route("goto-discovery", "discovery", "回到找对标", "继续查看首页当前最优先的对标与高分样本动作。") + if source_screen == "dashboard" and source_action_key.startswith("homepage-secondary-action-"): + if intent_key == "track_account": + return route("goto-tracking", "tracking", "回到跟踪账号", "继续跟进首页建议的重点账号跟踪。") + return route("goto-production", "production", "回到生产中心", "继续处理首页建议的生产推进动作。") + if source_screen in source_routes: + return source_routes[source_screen] + + intent_routes = { + "analyze_account": route("goto-discovery", "discovery", "回到找对标", "继续拆解当前账号和对标对象。"), + "analyze_top_videos": route("goto-discovery", "discovery", "回到找对标", "继续查看高分作品分析结论。"), + "track_account": route("goto-tracking", "tracking", "回到跟踪账号", "继续更新账号跟踪与日报。"), + "ai_video": route("goto-production", "production", "回到生产中心", "继续推进 AI 视频生产任务。"), + "real_cut": route("goto-production", "production", "回到生产中心", "继续推进实拍剪辑任务。"), + "live_recorder": route("goto-production", "production", "回到生产中心", "继续查看录制维护与产物。"), + "review": route("goto-review", "review", "回到发布与复盘", "继续补齐复盘与发布总结。"), + "create_assistant": route("goto-playbook", "playbook", "回到 Agent", "继续创建或调整项目 Agent。"), + "create_project": route("goto-intake", "projects", "回到我的项目", "继续创建或切换当前项目。"), + "import_homepage": route("goto-discovery", "discovery", "回到找对标", "继续处理主页导入后的账号分析。"), + "ops_admin": route("goto-automation", "automation", "回到自动流程", "继续查看系统依赖和治理状态。"), + "storage_status": route("goto-automation", "automation", "回到自动流程", "继续查看存储与依赖健康状态。"), + } + if intent_key in intent_routes: + return intent_routes[intent_key] + return route("goto-production", "production", "回到生产中心", "继续推进当前主 Agent 任务的执行结果。") + def _complete_agent_run_for_read(row: dict[str, Any]) -> dict[str, Any]: current_status = str(row.get("run_status") or "") run_id = str(row.get("id") or "") @@ -2724,6 +2776,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: steps = ["读取当前项目上下文", "结合治理层生成执行计划", "收口为可执行建议"] summary_text = str(plan.get("summary") or row.get("summary") or "").strip() or "主 Agent 已根据当前计划完成第一版执行收口。" execution_summary = f"已完成「{str(plan.get('goal') or row.get('title') or '主 Agent 任务').strip() or '主 Agent 任务'}」的首轮执行建议。" + recommended_action = _build_agent_run_recommended_action(row, plan) result_payload = { "result_kind": "main_agent_plan", "goal": str(plan.get("goal") or row.get("title") or "主 Agent 任务").strip() or "主 Agent 任务", @@ -2733,6 +2786,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None: "intent_key": str(plan.get("intent_key") or row.get("intent_key") or "custom").strip() or "custom", "platform": str(plan.get("platform") or row.get("platform") or "").strip(), "platform_scope": str(plan.get("platform_scope") or row.get("platform_scope") or "single_platform").strip() or "single_platform", + "recommended_action": recommended_action, "active_admin_override_notice": _parse_json(row.get("active_admin_override_notice_json"), {}), } _log_agent_run_event( diff --git a/tests/test_main_agent_governance.py b/tests/test_main_agent_governance.py index d9f6d2f..32ff2c3 100644 --- a/tests/test_main_agent_governance.py +++ b/tests/test_main_agent_governance.py @@ -286,6 +286,8 @@ class MainAgentGovernanceTests(unittest.TestCase): self.assertEqual(payload["run_status"], "done") self.assertTrue(payload["finished_at"]) self.assertEqual(payload["result"]["result_kind"], "main_agent_plan") + self.assertEqual(payload["result"]["recommended_action"]["action"], "goto-discovery") + self.assertEqual(payload["result"]["recommended_action"]["screen"], "discovery") event_types = [item["event_type"] for item in payload["events"]] self.assertIn("run.progress", event_types) self.assertIn("run.done", event_types) diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index ecdd53c..c97b04b 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -977,6 +977,7 @@ function renderOneLinerRunsHtml() { const runEvents = safeArray(currentRun.events).slice(-3); const planSteps = safeArray(currentRun.plan?.steps).slice(0, 4); const resultPayload = currentRun.result && typeof currentRun.result === "object" ? currentRun.result : null; + const recommendedAction = resultPayload?.recommended_action || null; const hasResultPayload = Boolean(resultPayload && Object.keys(resultPayload).length); const runStatusLabel = { needs_confirmation: "待确认", @@ -1043,7 +1044,9 @@ function renderOneLinerRunsHtml() { ${escapeHtml(currentRun.status_summary || "主 Agent 正在推进中")} `} ${hasResultPayload ? `查看结果` : ""} + ${recommendedAction?.action ? `${escapeHtml(recommendedAction.label || "回到对应页面")}` : ""} + ${recommendedAction?.summary ? `
${escapeHtml(payload.recommended_action.summary)}
+${escapeHtml(appState.lastAction.summary)}
+ ${(runId || recommendedAction?.action) ? ` + + ` : ""}