feat: carry platform agent config into main agent runs
This commit is contained in:
@@ -103,6 +103,13 @@
|
||||
- run 的治理快照也收窄成“当前主配置 + 当前版本”最小运行态,不再把完整版本历史和审计链塞进每次执行记录,避免 `agent_runs.governance_json` 无限制膨胀。
|
||||
- Web 回归测试修正了 OneLiner 运行区函数边界,并新增了对执行链配置版本显示的断言;后端治理测试也补上了 run 完成态必须带配置版本的检查。
|
||||
|
||||
### 平台 Agent 配置进入执行链
|
||||
|
||||
- 主 Agent 在创建 run、重试 run 时,会把当前平台 Agent 的最小运行快照一起固化进治理快照,包括平台、Agent 名称、承接使命、assistant 名称和 readiness 状态。
|
||||
- 完成态结果现在会带上 `execution_card.platform_agent_profile`,前端浮窗、当前运行卡和结果卡都能直接看到“本轮平台 Agent”,执行链不会再丢失平台侧配置来源。
|
||||
- run 的平台 Agent 快照只保留运行时最小必要字段,不把完整平台 Agent 配置、技能列表和记忆列表塞进执行结果,避免执行记录继续膨胀。
|
||||
- Web 回归测试新增了对“本轮平台 Agent”结果渲染的断言;后端治理测试也补上了 run 创建态与完成态必须带平台 Agent 快照的检查。
|
||||
|
||||
### NAS 联调发布
|
||||
|
||||
- 最新 Web 已重新发布到 fnOS NAS:
|
||||
|
||||
@@ -1248,6 +1248,35 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"updated_at": (row or {}).get("updated_at", ""),
|
||||
}
|
||||
|
||||
def _platform_agent_runtime_snapshot(
|
||||
account: dict[str, Any],
|
||||
row: dict[str, Any] | None,
|
||||
*,
|
||||
platform: str,
|
||||
project_id: str = "",
|
||||
) -> dict[str, Any]:
|
||||
payload = _platform_agent_payload(account, row, platform=platform, project_id=project_id)
|
||||
assistant = payload.get("assistant") or {}
|
||||
recent_memory = payload.get("recent_memory") or {}
|
||||
recent_skill = payload.get("recent_skill") or {}
|
||||
return {
|
||||
"id": str(payload.get("id") or "").strip(),
|
||||
"platform": str(payload.get("platform") or platform or "").strip(),
|
||||
"platform_label": str(payload.get("platform_label") or legacy.platform_label(platform) or "").strip(),
|
||||
"assistant_id": str(payload.get("assistant_id") or "").strip(),
|
||||
"assistant_name": str((assistant or {}).get("name") or "").strip(),
|
||||
"name": str(payload.get("name") or "").strip(),
|
||||
"mission": str(payload.get("mission") or "").strip(),
|
||||
"notes": str(payload.get("notes") or "").strip(),
|
||||
"status": str(payload.get("status") or "").strip(),
|
||||
"memory_count": int(payload.get("memory_count") or 0),
|
||||
"skill_count": int(payload.get("skill_count") or 0),
|
||||
"readiness_score": int(payload.get("readiness_score") or 0),
|
||||
"readiness_label": str(payload.get("readiness_label") or "").strip(),
|
||||
"recent_memory_title": str(recent_memory.get("title") or "").strip(),
|
||||
"recent_skill_title": str(recent_skill.get("title") or "").strip(),
|
||||
}
|
||||
|
||||
def _memory_payload(row: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"id": row["id"],
|
||||
@@ -3190,6 +3219,7 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
or (governance.get("oneliner_profile") or {}).get("current_version")
|
||||
or {}
|
||||
)
|
||||
platform_agent_profile = governance.get("platform_agent_profile") or {}
|
||||
steps = [str(item).strip() for item in list(plan.get("steps") or []) if str(item).strip()]
|
||||
if not steps:
|
||||
steps = ["读取当前项目上下文", "结合治理层生成执行计划", "收口为可执行建议"]
|
||||
@@ -3222,6 +3252,16 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
"title": str(oneliner_profile_version.get("title") or "").strip(),
|
||||
"summary": str(oneliner_profile_version.get("summary") or "").strip(),
|
||||
},
|
||||
"platform_agent_profile": {
|
||||
"platform": str(platform_agent_profile.get("platform") or "").strip(),
|
||||
"platform_label": str(platform_agent_profile.get("platform_label") or "").strip(),
|
||||
"name": str(platform_agent_profile.get("name") or "").strip(),
|
||||
"assistant_name": str(platform_agent_profile.get("assistant_name") or "").strip(),
|
||||
"mission": str(platform_agent_profile.get("mission") or "").strip(),
|
||||
"status": str(platform_agent_profile.get("status") or "").strip(),
|
||||
"readiness_label": str(platform_agent_profile.get("readiness_label") or "").strip(),
|
||||
"readiness_score": int(platform_agent_profile.get("readiness_score") or 0),
|
||||
},
|
||||
"next_steps": steps,
|
||||
},
|
||||
}
|
||||
@@ -5087,11 +5127,21 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
platform=normalized_platform,
|
||||
)
|
||||
profile_row = _fetch_profile_row(account, project["id"]) or _ensure_oneliner_profile(account, project["id"])
|
||||
platform_profile_row = legacy.db.fetch_one(
|
||||
"SELECT * FROM platform_agent_profiles WHERE user_id = ? AND project_id = ? AND platform = ?",
|
||||
(account["id"], project["id"], normalized_platform),
|
||||
) if normalized_platform else None
|
||||
oneliner_profile_bundle = _oneliner_profile_runtime_snapshot(profile_row, account=account)
|
||||
governance_snapshot = {
|
||||
**governance,
|
||||
"oneliner_profile": oneliner_profile_bundle,
|
||||
"oneliner_profile_version": oneliner_profile_bundle.get("current_version") or {},
|
||||
"platform_agent_profile": _platform_agent_runtime_snapshot(
|
||||
account,
|
||||
platform_profile_row,
|
||||
platform=normalized_platform,
|
||||
project_id=project["id"],
|
||||
) if normalized_platform else {},
|
||||
}
|
||||
plan = _agent_run_plan_payload(
|
||||
request,
|
||||
@@ -5178,11 +5228,21 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
platform=platform,
|
||||
)
|
||||
profile_row = _fetch_profile_row(account, project_id) or _ensure_oneliner_profile(account, project_id)
|
||||
platform_profile_row = legacy.db.fetch_one(
|
||||
"SELECT * FROM platform_agent_profiles WHERE user_id = ? AND project_id = ? AND platform = ?",
|
||||
(account["id"], project_id, platform),
|
||||
) if platform else None
|
||||
oneliner_profile_bundle = _oneliner_profile_runtime_snapshot(profile_row, account=account)
|
||||
governance_snapshot = {
|
||||
**governance,
|
||||
"oneliner_profile": oneliner_profile_bundle,
|
||||
"oneliner_profile_version": oneliner_profile_bundle.get("current_version") or {},
|
||||
"platform_agent_profile": _platform_agent_runtime_snapshot(
|
||||
account,
|
||||
platform_profile_row,
|
||||
platform=platform,
|
||||
project_id=project_id,
|
||||
) if platform else {},
|
||||
}
|
||||
plan = _parse_json(row.get("plan_json"), {})
|
||||
session_id = str(row.get("session_id") or "").strip()
|
||||
|
||||
@@ -227,6 +227,8 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertIn("oneliner_profile", payload["governance"])
|
||||
self.assertIn("oneliner_profile_version", payload["governance"])
|
||||
self.assertGreaterEqual(payload["governance"]["oneliner_profile_version"]["version_no"], 1)
|
||||
self.assertIn("platform_agent_profile", payload["governance"])
|
||||
self.assertEqual(payload["governance"]["platform_agent_profile"]["platform"], "douyin")
|
||||
self.assertEqual(payload["events"][0]["event_type"], "run.created")
|
||||
|
||||
def test_agent_run_confirm_transitions_to_queue_or_running_and_logs_events(self) -> None:
|
||||
@@ -310,6 +312,7 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertGreaterEqual(len(payload["result"]["result_sections"]["cards"]), 2)
|
||||
self.assertEqual(payload["result"]["result_sections"]["cards"][0]["title"], "当前焦点")
|
||||
self.assertGreaterEqual(payload["result"]["execution_card"]["oneliner_profile_version"]["version_no"], 1)
|
||||
self.assertEqual(payload["result"]["execution_card"]["platform_agent_profile"]["platform"], "douyin")
|
||||
event_types = [item["event_type"] for item in payload["events"]]
|
||||
self.assertIn("run.progress", event_types)
|
||||
self.assertIn("run.done", event_types)
|
||||
|
||||
@@ -1131,6 +1131,7 @@ function renderOneLinerRunsHtml() {
|
||||
: "";
|
||||
const canRetryCurrentRun = ["blocked", "failed", "cancelled"].includes(currentRun.run_status);
|
||||
const currentRunConfigVersion = currentRun.governance?.oneliner_profile_version || currentRun.governance?.oneliner_profile?.current_version || {};
|
||||
const currentRunPlatformAgentProfile = currentRun.result?.execution_card?.platform_agent_profile || currentRun.governance?.platform_agent_profile || {};
|
||||
return `
|
||||
<div class="task-item compact" style="margin-bottom:10px;">
|
||||
<h4>近期运行概况</h4>
|
||||
@@ -1232,6 +1233,18 @@ function renderOneLinerRunsHtml() {
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${currentRunPlatformAgentProfile.platform ? `
|
||||
<div class="task-item compact" style="margin-top:10px;">
|
||||
<h4>本轮平台 Agent</h4>
|
||||
<p>${escapeHtml(currentRunPlatformAgentProfile.mission || currentRunPlatformAgentProfile.name || `${currentRunPlatformAgentProfile.platform_label || platformLabel(currentRunPlatformAgentProfile.platform)} Agent 正在承接这轮执行。`)}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag blue">${escapeHtml(currentRunPlatformAgentProfile.platform_label || platformLabel(currentRunPlatformAgentProfile.platform))}</span>
|
||||
${currentRunPlatformAgentProfile.name ? `<span class="tag">${escapeHtml(currentRunPlatformAgentProfile.name)}</span>` : ""}
|
||||
${currentRunPlatformAgentProfile.assistant_name ? `<span class="tag green">${escapeHtml(currentRunPlatformAgentProfile.assistant_name)}</span>` : ""}
|
||||
${currentRunPlatformAgentProfile.readiness_label ? `<span class="tag ${currentRunPlatformAgentProfile.readiness_score >= 75 ? "green" : currentRunPlatformAgentProfile.readiness_score >= 50 ? "blue" : "orange"}">${escapeHtml(currentRunPlatformAgentProfile.readiness_label)} ${escapeHtml(formatNumber(currentRunPlatformAgentProfile.readiness_score || 0))}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${!hasResultPayload && previewAction?.summary ? `<div class="panel-subtitle" style="margin-top:8px;">${escapeHtml(previewAction.summary)}</div>` : ""}
|
||||
${planSteps.length ? `
|
||||
<div class="list" style="margin-top:10px;">
|
||||
@@ -1969,6 +1982,7 @@ function renderOneLinerExecutionPayloadHtml(payload) {
|
||||
: {};
|
||||
const resultCards = safeArray(resultSections.cards).slice(0, 4);
|
||||
const configVersion = payload.execution_card?.oneliner_profile_version || payload.context?.oneliner_profile?.current_version || {};
|
||||
const platformAgentProfile = payload.execution_card?.platform_agent_profile || {};
|
||||
const landingAttrs = buildMainAgentLandingAttrs({
|
||||
runId: landingRunId,
|
||||
screen: landingScreen,
|
||||
@@ -1997,6 +2011,18 @@ function renderOneLinerExecutionPayloadHtml(payload) {
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${platformAgentProfile.platform ? `
|
||||
<div class="task-item compact" style="margin-top:12px;">
|
||||
<h4>本轮平台 Agent</h4>
|
||||
<p>${escapeHtml(platformAgentProfile.mission || platformAgentProfile.name || `${platformAgentProfile.platform_label || platformLabel(platformAgentProfile.platform)} Agent 正在承接这轮执行。`)}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag blue">${escapeHtml(platformAgentProfile.platform_label || platformLabel(platformAgentProfile.platform))}</span>
|
||||
${platformAgentProfile.name ? `<span class="tag">${escapeHtml(platformAgentProfile.name)}</span>` : ""}
|
||||
${platformAgentProfile.assistant_name ? `<span class="tag green">${escapeHtml(platformAgentProfile.assistant_name)}</span>` : ""}
|
||||
${platformAgentProfile.readiness_label ? `<span class="tag ${platformAgentProfile.readiness_score >= 75 ? "green" : platformAgentProfile.readiness_score >= 50 ? "blue" : "orange"}">${escapeHtml(platformAgentProfile.readiness_label)} ${escapeHtml(formatNumber(platformAgentProfile.readiness_score || 0))}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${resultCards.length ? `
|
||||
<div class="task-item compact" style="margin-top:12px;">
|
||||
<h4>${escapeHtml(resultSections.workstream_label || "执行结果分组")}</h4>
|
||||
|
||||
@@ -725,6 +725,8 @@ test("main agent result rendering offers a direct route back into the recommende
|
||||
assert.match(execution, /result_sections/);
|
||||
assert.match(execution, /当前焦点/);
|
||||
assert.match(execution, /detail-grid/);
|
||||
assert.match(execution, /本轮平台 Agent/);
|
||||
assert.match(execution, /platform_agent_profile/);
|
||||
assert.match(execution, /data-action="\$\{escapeHtml\(payload\.recommended_action\.action\)\}"/);
|
||||
assert.match(lastAction, /open-oneliner-run-result/);
|
||||
assert.match(lastAction, /recommended_action/);
|
||||
|
||||
Reference in New Issue
Block a user