feat(web): surface awooop agent evidence chain
This commit is contained in:
@@ -3030,6 +3030,24 @@
|
||||
"km": "KM",
|
||||
"adr100": "ADR-100 Route"
|
||||
},
|
||||
"toolchain": {
|
||||
"title": "AI Agent Evidence Chain",
|
||||
"mcp": "MCP / Custom MCP",
|
||||
"mcpValue": "Gateway {success}/{total}, failed {failed}, blocked {blocked}",
|
||||
"mcpDetail": "top={topTool}; first-class={firstClass}; legacy={legacy}; policy={policy}",
|
||||
"source": "Sentry / SigNoz",
|
||||
"sourceValue": "{status}; direct {direct}, candidate {candidate}, applied {applied}",
|
||||
"sourceDetail": "{providers}",
|
||||
"execution": "Executor",
|
||||
"executionValue": "{executor} / {status}",
|
||||
"executionDetail": "operation={operation}; action={action}; ops={ops}",
|
||||
"playbook": "PlayBook / Ansible",
|
||||
"playbookValue": "{playbook}",
|
||||
"playbookDetail": "ansible={ansible}; candidates={candidates}; check-mode={checkMode}; status={status}",
|
||||
"learning": "KM / Learning",
|
||||
"learningValue": "KM {km}; AutoRepair {autoRepair}; Ops {ops}",
|
||||
"learningDetail": "verification={verification}; next={nextStep}"
|
||||
},
|
||||
"source": {
|
||||
"status": "Source Link",
|
||||
"verification": "Status-chain Verification",
|
||||
|
||||
@@ -3031,6 +3031,24 @@
|
||||
"km": "KM",
|
||||
"adr100": "ADR-100 Route"
|
||||
},
|
||||
"toolchain": {
|
||||
"title": "AI Agent 證據鏈",
|
||||
"mcp": "MCP / 自建 MCP",
|
||||
"mcpValue": "Gateway {success}/{total},失敗 {failed},阻擋 {blocked}",
|
||||
"mcpDetail": "top={topTool}; first-class={firstClass}; legacy={legacy}; policy={policy}",
|
||||
"source": "Sentry / SigNoz",
|
||||
"sourceValue": "{status}; direct {direct}, candidate {candidate}, applied {applied}",
|
||||
"sourceDetail": "{providers}",
|
||||
"execution": "Executor",
|
||||
"executionValue": "{executor} / {status}",
|
||||
"executionDetail": "operation={operation}; action={action}; ops={ops}",
|
||||
"playbook": "PlayBook / Ansible",
|
||||
"playbookValue": "{playbook}",
|
||||
"playbookDetail": "ansible={ansible}; candidates={candidates}; check-mode={checkMode}; status={status}",
|
||||
"learning": "KM / Learning",
|
||||
"learningValue": "KM {km}; AutoRepair {autoRepair}; Ops {ops}",
|
||||
"learningDetail": "verification={verification}; next={nextStep}"
|
||||
},
|
||||
"source": {
|
||||
"status": "來源關聯",
|
||||
"verification": "狀態鏈驗證",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Activity, CheckCircle2, Link2, RadioTower, Route, ShieldAlert, TriangleAlert } from "lucide-react";
|
||||
import { Activity, BookOpenCheck, CheckCircle2, Link2, RadioTower, Route, ShieldAlert, TriangleAlert, Wrench } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -316,6 +316,110 @@ export function AwoooPStatusChainPanel({
|
||||
}),
|
||||
},
|
||||
] : [];
|
||||
const mcpGateway = chain.mcp?.gateway ?? {};
|
||||
const legacyMcp = chain.mcp?.legacy ?? {};
|
||||
const topTool = chain.mcp?.top_tools?.[0];
|
||||
const execution = chain.execution ?? {};
|
||||
const ansible = execution.ansible ?? {};
|
||||
const candidatePlaybook = ansible.candidate_playbooks?.[0];
|
||||
const selectedPlaybook = execution.playbook_paths?.[0]
|
||||
?? execution.playbook_ids?.[0]
|
||||
?? ansible.latest_playbook_path
|
||||
?? candidatePlaybook?.playbook_path
|
||||
?? candidatePlaybook?.catalog_id
|
||||
?? emptyLabel;
|
||||
const mcpGatewayTotal = mcpGateway.total ?? 0;
|
||||
const mcpGatewayProblemTotal = (mcpGateway.failed ?? 0) + (mcpGateway.blocked ?? 0);
|
||||
const executionTotal = execution.operation_total ?? 0;
|
||||
const sourceToolchainTone: SourceFlowTone = sourceCorrelation
|
||||
? (sourceLinkVerified ? "success" : (sourceVerificationBlocked ? "blocked" : "warning"))
|
||||
: "neutral";
|
||||
const toolchainItems = [
|
||||
{
|
||||
key: "mcp",
|
||||
Icon: RadioTower,
|
||||
tone: (mcpGatewayTotal > 0
|
||||
? (mcpGatewayProblemTotal > 0 ? "warning" : "success")
|
||||
: "neutral") as SourceFlowTone,
|
||||
label: t("toolchain.mcp"),
|
||||
value: t("toolchain.mcpValue", {
|
||||
success: mcpGateway.success ?? 0,
|
||||
total: mcpGatewayTotal,
|
||||
failed: mcpGateway.failed ?? 0,
|
||||
blocked: mcpGateway.blocked ?? 0,
|
||||
}),
|
||||
detail: t("toolchain.mcpDetail", {
|
||||
topTool: topTool?.tool_name ?? emptyLabel,
|
||||
firstClass: mcpGateway.first_class_total ?? 0,
|
||||
legacy: legacyMcp.total ?? 0,
|
||||
policy: mcpGateway.policy_enforced_total ?? 0,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "source",
|
||||
Icon: Link2,
|
||||
tone: sourceToolchainTone,
|
||||
label: t("toolchain.source"),
|
||||
value: t("toolchain.sourceValue", {
|
||||
status: sourceStatusLabels[sourceStatus] ?? valueOrEmpty(sourceStatus, emptyLabel),
|
||||
direct: directRefTotal,
|
||||
candidate: candidateTotal,
|
||||
applied: appliedLinkTotal,
|
||||
}),
|
||||
detail: t("toolchain.sourceDetail", {
|
||||
providers: sourceProviderSummary || emptyLabel,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "execution",
|
||||
Icon: Activity,
|
||||
tone: (executionTotal > 0
|
||||
? (String(execution.latest_status ?? "").toLowerCase().includes("fail") ? "blocked" : "success")
|
||||
: "neutral") as SourceFlowTone,
|
||||
label: t("toolchain.execution"),
|
||||
value: t("toolchain.executionValue", {
|
||||
executor: execution.latest_executor ?? emptyLabel,
|
||||
status: execution.latest_status ?? emptyLabel,
|
||||
}),
|
||||
detail: t("toolchain.executionDetail", {
|
||||
operation: execution.latest_operation_type ?? emptyLabel,
|
||||
action: execution.latest_action ?? emptyLabel,
|
||||
ops: executionTotal,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "playbook",
|
||||
Icon: Wrench,
|
||||
tone: (ansible.considered || (ansible.candidate_count ?? 0) > 0
|
||||
? (ansible.latest_status === "applied" ? "success" : "warning")
|
||||
: "neutral") as SourceFlowTone,
|
||||
label: t("toolchain.playbook"),
|
||||
value: selectedPlaybook,
|
||||
detail: t("toolchain.playbookDetail", {
|
||||
ansible: boolValue(ansible.considered, emptyLabel),
|
||||
candidates: ansible.candidate_count ?? 0,
|
||||
checkMode: valueOrEmpty(ansible.latest_check_mode, emptyLabel),
|
||||
status: ansible.latest_status ?? emptyLabel,
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "learning",
|
||||
Icon: BookOpenCheck,
|
||||
tone: ((evidence.knowledge_entries ?? 0) > 0
|
||||
? ((evidence.auto_repair_records ?? 0) > 0 ? "success" : "warning")
|
||||
: "neutral") as SourceFlowTone,
|
||||
label: t("toolchain.learning"),
|
||||
value: t("toolchain.learningValue", {
|
||||
km: evidence.knowledge_entries ?? 0,
|
||||
autoRepair: evidence.auto_repair_records ?? 0,
|
||||
ops: evidence.operation_records ?? 0,
|
||||
}),
|
||||
detail: t("toolchain.learningDetail", {
|
||||
verification: valueOrEmpty(chain.verification, emptyLabel),
|
||||
nextStep: valueOrEmpty(chain.next_step, emptyLabel),
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className={cn("border border-[#e0ddd4] bg-white", className)}>
|
||||
@@ -388,6 +492,33 @@ export function AwoooPStatusChainPanel({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[#e0ddd4] bg-[#faf9f3] px-4 py-2">
|
||||
<p className="text-xs font-semibold text-[#141413]">{t("toolchain.title")}</p>
|
||||
</div>
|
||||
<div className={cn("grid gap-px bg-[#e0ddd4]", compact ? "md:grid-cols-2" : "md:grid-cols-5")}>
|
||||
{toolchainItems.map((item) => (
|
||||
<div key={item.key} className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
<span className={cn(
|
||||
"flex h-8 w-8 shrink-0 items-center justify-center border",
|
||||
sourceFlowToneClass(item.tone)
|
||||
)}>
|
||||
<item.Icon className="h-4 w-4" aria-hidden="true" />
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
|
||||
<p className="mt-1 truncate font-mono text-sm font-semibold text-[#141413]" title={item.value}>
|
||||
{item.value}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 truncate font-mono text-xs text-[#5f5b52]" title={item.detail}>
|
||||
{item.detail}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{sourceCorrelation && (
|
||||
<div>
|
||||
<div className={cn("grid gap-px bg-[#e0ddd4]", compact ? "grid-cols-1" : "md:grid-cols-3")}>
|
||||
|
||||
@@ -20034,3 +20034,49 @@ screenshot:
|
||||
- KM governance:約 84%。
|
||||
- AI Provider lane visibility:約 92%。
|
||||
- 完整 AI 自動化管理產品化:約 95.6%。
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-25 T181 — AwoooP 共用狀態鏈補 AI Agent 證據鏈矩陣
|
||||
|
||||
**背景**:
|
||||
|
||||
- 使用者要求 Telegram / 前端要能清楚回答:AI Agent 是否真的用了 MCP / 自建 MCP、是否關聯 Sentry / SigNoz、是否產生或匹配 PlayBook、Ansible 是否進入 check-mode / apply、KM / Learning 是否有寫入。
|
||||
- 盤點後確認 `/api/v1/platform/status-chain` 已回傳 `awooop_status_chain_v1`,內含 `mcp.gateway`、`mcp.legacy`、`mcp.top_tools`、`source_refs.correlation`、`execution`、`execution.ansible`、`evidence.knowledge_entries` 等欄位;本輪不需要新增後端 API,也不新增假資料。
|
||||
- production API sample(`INC-20260525-69E971`)顯示:MCP Gateway `17/19` success、top tool `error_logs_summary`、Sentry/SigNoz provider heartbeat 存在但未匹配、Executor `ansible / dry_run`、Ansible 候選 `110-devops.yml` / `188-ai-web.yml`、KM `1`、AutoRepair `1`、Ops `1`。
|
||||
|
||||
**本輪修正**:
|
||||
|
||||
- `AwoooPStatusChainPanel` 新增「AI Agent 證據鏈」矩陣,所有使用該共用面板的頁面同步受益(Work Items、Runs detail、callback history / persisted snapshot)。
|
||||
- 矩陣五格:
|
||||
- `MCP / 自建 MCP`:Gateway 成功/總數、失敗、阻擋、top tool、first-class / legacy / policy count。
|
||||
- `Sentry / SigNoz`:source correlation 狀態、direct / candidate / applied 數量、provider 摘要。
|
||||
- `Executor`:latest executor、operation status、operation type、action、ops count。
|
||||
- `PlayBook / Ansible`:已選 PlayBook 或候選 playbook path、Ansible considered、candidate count、check-mode、status。
|
||||
- `KM / Learning`:KM、AutoRepair、Ops、verification、next step。
|
||||
- 新增 zh-TW / en i18n key;未新增內網 IP、未新增 mock data、未新增新付費 provider。
|
||||
|
||||
**local validation(完成)**:
|
||||
|
||||
```text
|
||||
jq empty apps/web/messages/zh-TW.json apps/web/messages/en.json
|
||||
git diff --check
|
||||
pnpm --dir apps/web exec tsc --noEmit --tsBuildInfoFile /tmp/awoooi-t181-tsconfig.tsbuildinfo
|
||||
pnpm --dir apps/web lint -- --file src/components/awooop/status-chain.tsx
|
||||
NEXT_PUBLIC_API_URL=https://awoooi.wooo.work pnpm --dir apps/web run build
|
||||
```
|
||||
|
||||
**目前整體進度**:
|
||||
|
||||
- AwoooP 告警可觀測鏈:約 99.38%。
|
||||
- 低風險自動修復閉環:約 95.8%。
|
||||
- 前端 AI 自動化管理介面同步:約 98.6%。
|
||||
- 首頁 KPI / 小龍蝦流程 truth alignment:約 96.5%。
|
||||
- Telegram 詳情 / 歷史可追溯:約 95.5%。
|
||||
- callback / DB replayability:約 96.3%。
|
||||
- MCP / 自建 MCP 可視化:約 92%。
|
||||
- Sentry / SigNoz source correlation:約 91%。
|
||||
- Ansible / PlayBook 可視化:約 90%。
|
||||
- KM governance:約 84.5%。
|
||||
- AI Provider lane visibility:約 92%。
|
||||
- 完整 AI 自動化管理產品化:約 96.1%。
|
||||
|
||||
Reference in New Issue
Block a user