"use client"; import { useEffect, useState, useCallback, useMemo } from "react"; import Link from "next/link"; import { GraduationCap, Trophy, Sparkles, RefreshCw, Play, Activity, Eye, Heart, MessageSquare, Users, AlertCircle, ChevronDown, ChevronUp, TrendingUp, } from "lucide-react"; import { cn } from "@/lib/utils"; import { LazyImage } from "@/components/ui/LazyImage"; interface SourceMetrics { replyCount: number; likeCount: number; viewCount: number; humanReplies: number; engagementScore: number; } interface Learning { id: number; botId: number; botUserId: number | null; botName: string; botAvatar: string | null; weekKey: string; sourceRefType: string; sourceRefId: number; sourceTitle: string | null; sourceUserName: string | null; sourceUserAvatar: string | null; sourceMetrics: SourceMetrics; relevanceScore: number; learnedInsight: string; learnCategories: string[]; status: string; usedCount: number; learnedAt: string; expiresAt: string | null; forumSlug: string | null; categoryName: string | null; } interface Summary { totalLearnings: number; activeCount: number; usedCount: number; avgRelevance: number; botCount: number; weeks: number; } const RANGE_OPTIONS = [ { value: 1, label: "近 1 周" }, { value: 2, label: "近 2 周" }, { value: 4, label: "近 4 周" }, { value: 8, label: "近 8 周" }, ]; const CATEGORY_META: Record = { hook: { label: "钩子", color: "text-violet-700", bg: "bg-violet-50" }, story: { label: "故事", color: "text-amber-700", bg: "bg-amber-50" }, data: { label: "数据", color: "text-sky-700", bg: "bg-sky-50" }, question: { label: "提问", color: "text-emerald-700", bg: "bg-emerald-50" }, contrarian: { label: "反主流", color: "text-rose-700", bg: "bg-rose-50" }, empathy: { label: "共情", color: "text-pink-700", bg: "bg-pink-50" }, practical: { label: "实用", color: "text-slate-700", bg: "bg-slate-100" }, }; const STATUS_META: Record = { active: { label: "生效中", color: "text-emerald-700", bg: "bg-emerald-50" }, used: { label: "已用", color: "text-sky-700", bg: "bg-sky-50" }, expired: { label: "已过期", color: "text-slate-500", bg: "bg-slate-50" }, }; export default function BotAdversarialLearningClient() { const [weeks, setWeeks] = useState(4); const [data, setData] = useState<{ summary: Summary; learnings: Learning[] } | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [running, setRunning] = useState(false); const [runResult, setRunResult] = useState(null); const [expanded, setExpanded] = useState>(new Set()); const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch(`/api/admin/bots/adversarial-learning?weeks=${weeks}`, { cache: "no-store" }); const json = await res.json(); if (!res.ok) throw new Error(json.error || "请求失败"); setData(json); } catch (e: unknown) { setError((e as Error).message); } finally { setLoading(false); } }, [weeks]); useEffect(() => { fetchData(); }, [fetchData]); const triggerRun = useCallback( async (mode: "week" | "force") => { setRunning(true); setRunResult(null); try { const res = await fetch("/api/admin/bots/adversarial-learning/run", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ lookback: 7, force: mode === "force" }), }); const json = await res.json(); if (!res.ok) throw new Error(json.error || "运行失败"); setRunResult(`✓ exitCode=${json.exitCode} duration=${json.duration}ms`); await fetchData(); } catch (e: unknown) { setRunResult(`✗ ${(e as Error).message}`); } finally { setRunning(false); } }, [fetchData] ); const toggle = useCallback((id: number) => { setExpanded((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }, []); const groupedByWeek = useMemo(() => { const map = new Map(); if (!data) return map; for (const l of data.learnings) { if (!map.has(l.weekKey)) map.set(l.weekKey, []); map.get(l.weekKey)!.push(l); } return map; }, [data]); return (

Bot 对抗学习看板

每周日 23:00 自动给每个 bot 选 1 篇过去 7 天真人发布的高互动帖,LLM 提取"为什么火"的洞察,下周 bot 发帖/回复时作为参考

{runResult && (
{runResult}
)} {error && (
{error}
)} {data && (
} label="学习总数" value={data.summary.totalLearnings} accent="text-violet-600" /> } label="生效中" value={data.summary.activeCount} accent="text-emerald-600" /> } label="引用次数" value={data.summary.usedCount} accent="text-amber-600" /> } label="平均关联度" value={data.summary.avgRelevance.toFixed(2)} accent="text-rose-600" /> } label="参与 bot" value={data.summary.botCount} accent="text-sky-600" />
)}
学习源:过去 7 天 真人(非 bot)发布的高互动 topic(reply+like+humanReplies 排序)
·
关联度 = 同板块(1.0) + 关键词重叠(0.6) + 时效衰减(0.3)
·
每周 1 条,unique(botId, weekKey),默认 7 天后过期
{/* 按周分组 */} {data && data.learnings.length === 0 && (
还没有对抗学习数据。运行 node scripts/bot-adversarial-learning-run.mjs 开始第一周,或点击"本周学习"按钮。
)} {Array.from(groupedByWeek.entries()).map(([weekKey, items]) => (

{weekKey.split("-W")[1]} 第 {weekKey.split("-W")[1]} 周 ({weekKey}) · {items.length} 条学习

{items.map((l) => { const isExpanded = expanded.has(l.id); const statusMeta = STATUS_META[l.status] || STATUS_META.active; return (
{isExpanded && (
📌 来源真人帖
{l.sourceTitle}
{l.sourceUserName && (
作者:{l.sourceUserName}
)}
🧠 LLM 提取的洞察
{l.learnedInsight}
{l.learnCategories.length > 0 && (
{l.learnCategories.map((c) => { const m = CATEGORY_META[c] || { label: c, color: "text-slate-600", bg: "bg-slate-100" }; return ( {m.label} ); })}
)}
生效窗口:{l.learnedAt.slice(0, 16)} → {l.expiresAt?.slice(0, 16) || "永久"}
)}
); })}
))}
← 返回 Bot 表现分析
); } function SummaryCard({ icon, label, value, accent }: { icon: React.ReactNode; label: string; value: number | string; accent: string }) { return (
{icon} {label}
{value}
); }