docs: 添加季度 AI 热点词云系统设计文档和实施计划

This commit is contained in:
2026-01-26 07:58:30 +08:00
parent 91a8a656ab
commit 114ae76abb
6 changed files with 3731 additions and 0 deletions
+323
View File
@@ -0,0 +1,323 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Agent Park: AI Keyword Evolution</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&amp;family=Work+Sans:wght@400;600;800&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#FFD700", // Bright MotherDuck Yellow
secondary: "#7FB5FF", // Soft Blue
accent: "#C39BD3", // Purple
"background-light": "#F7F5F0", // Warm off-white
"background-dark": "#1A1A1A", // Dark charcoal
"border-dark": "#000000",
},
fontFamily: {
display: ["'Space Mono'", "monospace"],
body: ["'Work Sans'", "sans-serif"],
},
borderRadius: {
DEFAULT: "4px",
},
boxShadow: {
'hard': '4px 4px 0px 0px rgba(0,0,0,1)',
'hard-sm': '2px 2px 0px 0px rgba(0,0,0,1)',
}
},
},
};
</script>
<style type="text/tailwindcss">
.geometric-bg {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
overflow: hidden;
opacity: 0.15;
}
.shape {
position: absolute;
border: 2px solid currentColor;
opacity: 0.3;
}
.shape-sq { width: 40px; height: 40px; }
.shape-tri {
width: 0; height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-bottom: 35px solid currentColor;
background: transparent;
border-top: none;
}
.shape-line { height: 2px; width: 60px; background: currentColor; border: none; }
.cloud-word {
@apply transition-all duration-200 cursor-pointer inline-block whitespace-nowrap relative;
}.popover {
@apply absolute bottom-[calc(100%+12px)] left-1/2 -translate-x-1/2 w-48 bg-white dark:bg-gray-800 border-2 border-black shadow-hard-sm opacity-0 pointer-events-none transition-all duration-300 translate-y-2 z-50 text-left;
}
.cloud-word:hover .popover {
@apply opacity-100 translate-y-0 pointer-events-auto;
}
.popover::after {
content: '';
@apply absolute -bottom-2 left-1/2 -translate-x-1/2 border-l-[8px] border-l-transparent border-r-[8px] border-r-transparent border-t-[8px] border-t-black;
}
.word-cluster {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: center;
justify-content: center;
max-width: 100%;
}
::-webkit-scrollbar { width: 12px }
::-webkit-scrollbar-track { background: #f1f1f1; border-left: 2px solid black }
::-webkit-scrollbar-thumb { background: #FFD700; border: 2px solid black }
.nav-button {
@apply flex items-center justify-center w-12 h-12 md:w-16 md:h-16 bg-white dark:bg-gray-800 border-4 border-black shadow-hard hover:translate-y-0.5 hover:shadow-none transition-all cursor-pointer;
}
.progress-step {
@apply h-3 flex-1 border-2 border-black transition-colors duration-300;
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark text-black dark:text-gray-100 font-body transition-colors duration-300 min-h-screen relative overflow-x-hidden">
<div class="geometric-bg text-black dark:text-white">
<div class="shape shape-sq top-[10%] left-[5%] rotate-12"></div>
<div class="shape shape-tri top-[25%] left-[85%] rotate-45"></div>
<div class="shape shape-line top-[45%] left-[15%] -rotate-12"></div>
<div class="shape shape-sq top-[65%] left-[90%] -rotate-6"></div>
<div class="shape shape-tri top-[80%] left-[10%] rotate-[160deg]"></div>
<div class="shape shape-line top-[15%] left-[50%] rotate-90"></div>
<div class="shape shape-sq top-[85%] left-[55%] rotate-45"></div>
<div class="shape shape-line top-[70%] left-[30%] rotate-45"></div>
<div class="shape shape-tri top-[5%] left-[70%] -rotate-12"></div>
</div>
<nav class="sticky top-0 z-[100] w-full border-b-2 border-black bg-primary dark:bg-yellow-600">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16 items-center">
<div class="flex items-center gap-3">
<div class="h-8 w-8 bg-black rounded-full flex items-center justify-center text-white font-bold border-2 border-white">AP</div>
<span class="font-display font-bold text-xl tracking-tighter uppercase">Agent Park</span>
</div>
<div class="hidden md:flex space-x-8 font-display font-bold text-sm">
<a class="underline decoration-4 underline-offset-4" href="#">Navigator</a>
<a class="hover:underline decoration-2 underline-offset-4" href="#">DuckDB Stats</a>
<a class="hover:underline decoration-2 underline-offset-4" href="#">Manifesto</a>
</div>
<button class="bg-white dark:bg-gray-800 border-2 border-black shadow-hard-sm px-4 py-1 font-display font-bold text-sm hover:translate-y-0.5 hover:shadow-none transition-all dark:text-white">
JOIN THE FLOCK
</button>
</div>
</div>
</nav>
<header class="relative z-10 pt-16 pb-8 text-center max-w-4xl mx-auto px-4">
<div class="inline-block bg-accent dark:bg-purple-700 border-2 border-black px-3 py-1 font-display font-bold text-xs mb-4 shadow-hard-sm rotate-[-2deg]">
EXPERIMENTAL DATA
</div>
<h1 class="font-display text-5xl md:text-7xl font-bold mb-6 tracking-tighter leading-tight">
THE EVOLUTION OF <br/>
<span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-purple-500" style="-webkit-text-stroke: 1.5px black;">AI DISCOURSE</span>
</h1>
<p class="text-lg md:text-xl max-w-2xl mx-auto font-medium text-gray-700 dark:text-gray-300 mb-8">
From "Large Language Models" to "Agentic Workflows". Use the arrows below to explore the shifting narrative.
</p>
</header>
<main class="relative z-10 max-w-6xl mx-auto px-4 pb-32 overflow-visible">
<div class="bg-white/50 dark:bg-black/20 backdrop-blur-sm border-4 border-black p-8 md:p-12 shadow-hard relative overflow-visible">
<div class="flex gap-2 mb-12 max-w-md mx-auto">
<div class="progress-step bg-primary"></div>
<div class="progress-step bg-gray-200 dark:bg-gray-700"></div>
<div class="progress-step bg-gray-200 dark:bg-gray-700"></div>
<div class="progress-step border-dashed bg-transparent border-gray-400"></div>
</div>
<div class="flex flex-col md:flex-row items-center justify-between gap-8 mb-12">
<button class="nav-button order-2 md:order-1 opacity-50 cursor-not-allowed">
<span class="material-symbols-outlined text-4xl">chevron_left</span>
</button>
<div class="text-center order-1 md:order-2 flex-1">
<div class="inline-block bg-primary border-4 border-black px-10 py-4 shadow-hard font-display font-bold text-4xl md:text-5xl mb-4 rotate-1">
Q1 2023
</div>
<p class="font-display text-lg text-gray-600 dark:text-gray-400 italic">"The dawn of the chat interface."</p>
</div>
<button class="nav-button order-3">
<span class="material-symbols-outlined text-4xl">chevron_right</span>
</button>
</div>
<div class="relative py-12 px-4">
<div class="word-cluster">
<span class="cloud-word border-4 border-black bg-secondary px-6 py-3 rounded-full text-4xl md:text-5xl font-black shadow-hard rotate-1">
ChatGPT
<div class="popover">
<div class="bg-primary text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">ChatGPT</div>
<div class="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
<div class="flex gap-2 items-start"><span>Mass consumer breakout</span></div>
<div class="flex gap-2 items-start"><span>GPT-3.5 architecture</span></div>
<div class="flex gap-2 items-start"><span>Conversational UI focus</span></div>
</div>
</div>
</span>
<span class="cloud-word border-2 border-black bg-white dark:bg-gray-700 px-4 py-2 rounded-lg text-xl md:text-2xl font-bold -rotate-2">
Parameters
<div class="popover">
<div class="bg-accent text-white font-display font-bold p-2 border-b-2 border-black text-sm uppercase">Parameters</div>
<div class="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
<div class="flex gap-2 items-start"><span>Internal model weights</span></div>
<div class="flex gap-2 items-start"><span>Scaling law metric</span></div>
</div>
</div>
</span>
<span class="cloud-word border-4 border-black bg-accent text-white px-7 py-3 rounded-full text-2xl md:text-3xl font-bold shadow-hard-sm rotate-3">
Hallucination
<div class="popover">
<div class="bg-white text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">Hallucination</div>
<div class="p-3 space-y-2 text-xs font-medium text-black">
<div class="flex gap-2 items-start"><span>Factually incorrect output</span></div>
<div class="flex gap-2 items-start"><span>Stochastic behavior</span></div>
<div class="flex gap-2 items-start"><span>Trust barrier</span></div>
</div>
</div>
</span>
<span class="cloud-word border-2 border-black bg-gray-100 dark:bg-gray-600 px-3 py-1 rounded text-lg">
Transformer
<div class="popover">
<div class="bg-secondary text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">Transformer</div>
<div class="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
<div class="flex gap-2 items-start"><span>Attention mechanism</span></div>
<div class="flex gap-2 items-start"><span>Parallel processing</span></div>
</div>
</div>
</span>
<span class="cloud-word border-4 border-black bg-white dark:bg-gray-700 px-6 py-3 rounded-xl text-3xl md:text-4xl font-semibold -rotate-1">
Prompt Engineering
<div class="popover">
<div class="bg-primary text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">Prompting</div>
<div class="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
<div class="flex gap-2 items-start"><span>Instruction crafting</span></div>
<div class="flex gap-2 items-start"><span>Few-shot prompting</span></div>
<div class="flex gap-2 items-start"><span>Iterative tuning</span></div>
</div>
</div>
</span>
<span class="cloud-word border-2 border-black bg-white dark:bg-gray-700 px-4 py-2 rounded-full text-xl">
OpenAI
<div class="popover w-40">
<div class="bg-black text-white font-display font-bold p-2 border-b-2 border-black text-xs uppercase">The Lab</div>
<div class="p-2 space-y-1 text-[10px] font-medium dark:text-gray-100">
<div>• Research pioneer</div>
<div>• Closed-source shift</div>
</div>
</div>
</span>
<span class="cloud-word border-2 border-black bg-primary px-5 py-2 rounded-md text-2xl font-bold rotate-6">
LLM
<div class="popover">
<div class="bg-white text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">LLM</div>
<div class="p-3 space-y-2 text-xs font-medium text-black">
<div class="flex gap-2 items-start"><span>Large Language Model</span></div>
<div class="flex gap-2 items-start"><span>Next token prediction</span></div>
</div>
</div>
</span>
<span class="cloud-word border-2 border-black bg-white dark:bg-gray-700 px-3 py-1 rounded text-base">RLHF</span>
<span class="cloud-word border-4 border-black bg-secondary px-5 py-2.5 rounded-full text-3xl font-bold -rotate-3">Bard</span>
<span class="cloud-word border-2 border-black bg-white dark:bg-gray-700 px-4 py-2 rounded-lg text-xl">Foundation Model</span>
<span class="cloud-word border-2 border-black bg-accent text-white px-5 py-2 rounded-full text-2xl shadow-hard-sm">Emergence</span>
<span class="cloud-word border-2 border-black bg-gray-100 px-6 py-2 rounded-xl text-2xl font-black rotate-1">Tokenization</span>
<span class="cloud-word border-2 border-black bg-white px-4 py-1.5 rounded text-lg -rotate-6">Context Window</span>
<span class="cloud-word border-2 border-black bg-primary px-5 py-2 rounded-full text-2xl font-bold">GPT-4</span>
<span class="cloud-word border-4 border-black bg-secondary px-6 py-3 rounded text-3xl font-bold shadow-hard-sm">Few-Shot</span>
<span class="cloud-word border-2 border-black bg-white px-3 py-1.5 rounded-md text-base">Zero-Shot</span>
<span class="cloud-word border-2 border-black bg-accent/20 px-4 py-2 rounded-full text-xl italic">Multi-turn</span>
<span class="cloud-word border-2 border-black bg-white px-4 py-2 rounded-lg text-xl font-bold rotate-2">Supervised Learning</span>
</div>
<div class="absolute -top-16 -right-8 md:right-0 bg-black text-white p-5 rounded-xl text-sm md:text-base font-display w-64 shadow-hard rotate-6 hidden lg:block">
"ChatGPT" search volume peaked +400% in March 2023!
<div class="absolute -bottom-2 left-1/2 -translate-x-1/2 w-4 h-4 bg-black transform rotate-45"></div>
</div>
<div class="absolute -bottom-10 -left-10 w-24 h-24 bg-primary rounded-full border-4 border-black flex items-center justify-center font-bold font-display text-xs text-center shadow-hard rotate-[-15deg] z-20">
GENESIS<br/>OF CHAT
</div>
</div>
</div>
<section class="relative mt-24">
<div class="text-center mb-10">
<div class="inline-block bg-white dark:bg-gray-700 border-4 border-black border-dashed px-8 py-4 font-display font-bold text-3xl text-gray-500 transform -rotate-1">
WHAT'S NEXT?
</div>
</div>
<div class="max-w-3xl mx-auto bg-gray-100 dark:bg-gray-900 border-4 border-black border-dashed p-10 rounded-3xl relative opacity-80 hover:opacity-100 transition-opacity shadow-xl">
<h3 class="font-display font-bold text-2xl text-center text-gray-500 uppercase tracking-widest mb-8">Future Prediction Zone</h3>
<div class="word-cluster text-gray-400 italic">
<span class="text-4xl">???</span>
<span class="text-xl">AGI?</span>
<span class="text-2xl">Small Language Models</span>
<span class="text-3xl">On-Device AI</span>
<span class="text-xl">Liquid Neural Nets</span>
<span class="text-2xl">Sovereign AI</span>
<span class="text-xl">World Models</span>
</div>
<div class="text-center mt-12">
<button class="bg-primary text-black border-4 border-black px-8 py-3 font-bold font-display text-lg hover:bg-yellow-500 shadow-hard transform hover:-translate-y-1 transition-all">
Submit a Prediction
</button>
</div>
</div>
</section>
</main>
<footer class="bg-black text-white border-t-4 border-primary py-16 relative overflow-hidden z-[100]">
<div class="absolute inset-0 opacity-10" style="background-image: radial-gradient(#ffffff 1px, transparent 1px); background-size: 20px 20px;"></div>
<div class="max-w-7xl mx-auto px-4 relative z-10">
<div class="grid md:grid-cols-4 gap-12 mb-16">
<div class="col-span-1 md:col-span-2">
<div class="flex items-center gap-3 mb-6">
<div class="h-10 w-10 bg-primary rounded-full flex items-center justify-center text-black font-bold border-2 border-white text-xl">AP</div>
<h2 class="font-display font-bold text-3xl">AGENT PARK</h2>
</div>
<p class="font-body text-gray-400 max-w-md text-lg leading-relaxed">
Curating the chaos of the AI revolution, one keyword at a time. Built with 💛 by the ducklings.
</p>
</div>
<div>
<h4 class="font-display font-bold text-primary mb-6 text-lg uppercase tracking-widest">EXPLORE</h4>
<ul class="space-y-3 font-body">
<li><a class="hover:text-primary transition-colors text-gray-300" href="#">The Archive</a></li>
<li><a class="hover:text-primary transition-colors text-gray-300" href="#">Trends Dashboard</a></li>
<li><a class="hover:text-primary transition-colors text-gray-300" href="#">Contributor Guide</a></li>
</ul>
</div>
<div>
<h4 class="font-display font-bold text-primary mb-6 text-lg uppercase tracking-widest">CONNECT</h4>
<div class="flex space-x-4">
<a class="w-10 h-10 bg-white text-black rounded-lg flex items-center justify-center hover:bg-primary transition-colors border-2 border-black shadow-hard-sm" href="#">
<span class="font-bold">X</span>
</a>
<a class="w-10 h-10 bg-white text-black rounded-lg flex items-center justify-center hover:bg-primary transition-colors border-2 border-black shadow-hard-sm" href="#">
<span class="font-bold">Li</span>
</a>
<a class="w-10 h-10 bg-white text-black rounded-lg flex items-center justify-center hover:bg-primary transition-colors border-2 border-black shadow-hard-sm" href="#">
<span class="font-bold">Gh</span>
</a>
</div>
</div>
</div>
<div class="border-t border-gray-800 pt-8 flex flex-col md:flex-row justify-between items-center text-sm font-mono text-gray-500">
<p>© 2024 Agent Park. All rights reserved.</p>
<div class="mt-4 md:mt-0 flex gap-8">
<span class="hover:text-white cursor-pointer transition-colors">Privacy Policy</span>
<span class="hover:text-white cursor-pointer transition-colors">Terms of Quack</span>
</div>
</div>
</div>
</footer>
</body></html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

+284
View File
@@ -0,0 +1,284 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Agent Park - AI History Timeline Pinboard</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,container-queries"></script>
<link href="https://fonts.googleapis.com" rel="preconnect"/>
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&amp;family=Syne:wght@400;700;800&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#FFDE03",
secondary: "#FF90E8",
"background-light": "#F9F7F2",
"background-dark": "#121212",
"surface-light": "#FFFFFF",
"surface-dark": "#1E1E1E",
},
fontFamily: {
display: ["'Syne'", "sans-serif"],
mono: ["'Space Mono'", "monospace"],
},
boxShadow: {
'hard': '4px 4px 0px 0px #000000',
'hard-hover': '8px 8px 0px 0px #000000',
'hard-dark': '4px 4px 0px 0px #FFFFFF',
'card-stack': '2px 2px 0px 0px #000, 4px 4px 0px 0px #000, 6px 6px 0px 0px #000',
},
backgroundImage: {
'grid-pattern': "linear-gradient(to right, #e5e5e5 1px, transparent 1px), linear-gradient(to bottom, #e5e5e5 1px, transparent 1px)",
'grid-pattern-dark': "linear-gradient(to right, #333 1px, transparent 1px), linear-gradient(to bottom, #333 1px, transparent 1px)",
}
},
},
};
</script>
<style type="text/tailwindcss">
@layer utilities {
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
}
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
background: #F9F7F2;
border-left: 2px solid black;
}
.dark ::-webkit-scrollbar-track {
background: #121212;
border-left: 2px solid white;
}
::-webkit-scrollbar-thumb {
background: #FFDE03;
border: 2px solid black;
}
.dark ::-webkit-scrollbar-thumb {
background: #FFDE03;
border: 2px solid white;
}
.tape {
background-color: rgba(255, 255, 255, 0.4);
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
backdrop-filter: blur(2px);
border: 1px solid rgba(255,255,255,0.6);
}.stack-card {
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}.stack-card:hover {
z-index: 50 !important;
transform: translateY(-20px) scale(1.05) rotate(0deg) !important;
margin-right: 20px !important;
margin-left: 20px !important;
}.timeline-path {
stroke-dasharray: 20 10;
animation: dash 60s linear infinite;
}
@keyframes dash {
to {
stroke-dashoffset: -1000;
}
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark text-black dark:text-gray-100 font-mono overflow-x-hidden selection:bg-secondary selection:text-black">
<div class="fixed top-0 left-0 w-full z-50 border-b-2 border-black dark:border-white">
<div class="bg-primary py-2 px-4 text-center border-b-2 border-black">
<p class="text-xs font-bold tracking-widest uppercase flex items-center justify-center gap-2">
<span class="material-icons-round text-sm">auto_awesome</span>
Welcome to Agent Park: The Evolution of Intelligence
<span class="material-icons-round text-sm">arrow_forward</span>
</p>
</div>
<nav class="bg-background-light dark:bg-background-dark px-6 py-4 flex justify-between items-center transition-colors duration-300">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-black dark:bg-white text-white dark:text-black flex items-center justify-center border-2 border-transparent">
<span class="material-icons-round text-2xl">smart_toy</span>
</div>
<span class="font-display font-black text-2xl tracking-tighter uppercase">Agent Park</span>
</div>
<div class="hidden md:flex gap-8 text-sm font-bold">
<a class="hover:text-primary transition-colors uppercase decoration-2 underline-offset-4 hover:underline" href="#">Timeline</a>
<a class="hover:text-primary transition-colors uppercase decoration-2 underline-offset-4 hover:underline" href="#">Agents</a>
<a class="hover:text-primary transition-colors uppercase decoration-2 underline-offset-4 hover:underline" href="#">About</a>
</div>
<button class="w-10 h-10 border-2 border-black dark:border-white flex items-center justify-center hover:bg-black hover:text-white dark:hover:bg-white dark:hover:text-black transition-all shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,1)] active:translate-y-[2px] active:shadow-none" onclick="document.documentElement.classList.toggle('dark')">
<span class="material-icons-round text-lg">light_mode</span>
</button>
</nav>
</div>
<div class="fixed inset-0 pointer-events-none z-0 opacity-10 dark:opacity-20 overflow-hidden">
<div class="absolute inset-0 bg-[size:40px_40px] bg-grid-pattern dark:bg-grid-pattern-dark"></div>
</div>
<div class="relative z-10 pt-32 pb-20 px-4 max-w-[1600px] mx-auto">
<header class="text-center mb-20 relative">
<div class="inline-block relative">
<svg class="absolute -top-6 -left-8 w-[120%] h-[150%] text-primary opacity-80 -z-10 animate-pulse" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path d="M44.7,-51.2C57.1,-41.5,66.1,-27.6,68.9,-12.8C71.7,2,68.3,17.7,60.4,30.9C52.5,44.1,40.1,54.8,26.4,60.1C12.7,65.4,-2.3,65.3,-17.1,61.1C-31.9,56.9,-46.5,48.6,-56.3,36.4C-66.1,24.2,-71.1,8.1,-67.3,-5.7C-63.5,-19.5,-50.9,-31,-38.7,-40.8C-26.5,-50.6,-14.7,-58.7,0.1,-58.8C14.9,-59,29.8,-51.2,32.3,-60.9" fill="currentColor" transform="translate(100 100)"></path>
</svg>
<h1 class="font-display font-black text-6xl md:text-8xl tracking-tight leading-none text-black dark:text-white drop-shadow-sm">
THE STORY <br/> OF A.I.
</h1>
</div>
<p class="mt-6 text-lg md:text-xl font-mono max-w-2xl mx-auto bg-white dark:bg-black border border-black dark:border-white p-2 rotate-1 inline-block shadow-[4px_4px_0px_0px_#000] dark:shadow-[4px_4px_0px_0px_#fff]">
Pinned. Stacked. Zigzagged.
</p>
</header>
<main class="relative px-4 md:px-12">
<div class="relative w-full">
<div class="absolute inset-0 pointer-events-none hidden md:block -z-10" style="top: 15rem; bottom: 15rem;">
<div class="absolute top-0 left-0 right-[4rem] h-4 bg-black dark:bg-white"></div>
<div class="absolute top-0 right-[4rem] bottom-0 w-4 bg-black dark:bg-white"></div>
<div class="absolute bottom-0 right-[4rem] left-0 h-4 bg-black dark:bg-white"></div>
</div>
<div class="relative min-h-[500px] mb-32 flex flex-col md:flex-row items-center md:items-start md:justify-start pt-20">
<div class="absolute left-0 -top-8 z-20">
<div class="bg-primary border-4 border-black px-6 py-2 -rotate-2 shadow-hard">
<span class="font-display font-black text-3xl md:text-5xl">2024 — 2017</span>
</div>
</div>
<div class="w-full pl-0 md:pl-8 pr-0 md:pr-32 flex flex-nowrap overflow-x-visible items-center justify-start py-10 perspective-1000">
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-surface-light dark:bg-surface-dark border-4 border-black dark:border-white p-4 shadow-hard -mr-48 md:-mr-56 z-30 rotate-[-2deg]">
<div class="tape absolute -top-3 left-1/2 -translate-x-1/2 w-20 h-6 rotate-1"></div>
<div class="h-40 bg-primary border-2 border-black dark:border-white mb-4 flex items-center justify-center overflow-hidden">
<span class="material-icons-round text-6xl opacity-50">smart_toy</span>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">The Agent Era</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">Autonomous agents begin to populate the web. They plan, execute, and iterate. It's no longer just chat; it's action.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 1.2</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-secondary border-4 border-black dark:border-white p-4 shadow-hard -mr-48 md:-mr-56 z-20 rotate-[3deg]">
<div class="tape absolute -top-4 right-8 w-24 h-8 rotate-3"></div>
<div class="h-40 bg-white dark:bg-black border-2 border-black dark:border-white mb-4 flex items-center justify-center">
<div class="grid grid-cols-2 gap-2 p-2 w-full h-full opacity-60">
<div class="bg-black/10 dark:bg-white/10 rounded-full"></div>
<div class="bg-black/10 dark:bg-white/10"></div>
<div class="bg-black/10 dark:bg-white/10"></div>
<div class="bg-black/10 dark:bg-white/10 rounded-full"></div>
</div>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">Multimodal</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">Vision, audio, and text merge into single unified models. AI now perceives the world as humans do.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 1.1</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-white dark:bg-gray-800 border-4 border-black dark:border-white p-4 shadow-hard -mr-48 md:-mr-56 z-10 rotate-[-1deg]">
<div class="tape absolute -top-4 left-4 w-24 h-8 -rotate-2 bg-yellow-200/50"></div>
<div class="relative border-2 border-black dark:border-white bg-gray-100 h-40 mb-4 overflow-hidden group">
<img alt="Abstract AI Art" class="w-full h-full object-cover grayscale mix-blend-multiply group-hover:scale-110 transition-transform duration-500" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBd0bidYXReahZeHaO8_otbsOwtu_ky-BZEt5qAwkvMWUZayFj_lhA0WO4mW5rAg-BkCOnkGlBQMl_Vmce-cXrqLih3nxlly6yB7NHzSWgS_TiujzG79dYtRIdbhizZCFbTGu8fLVCwSrBmjr7ockX90-T8EA5xYet5tJf5Ip0xucPUUyusI0GmF6d99lc6csGj3Puqk2P8QSRwZEDgOvskQ2I_60FS9ZXGIK9tlhTzajIdPup4Kaz9d04o7-4MQpDMSOjbFR-rkUw"/>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">Generative Explosion</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">ChatGPT launches. The world changes overnight. LLMs become household utilities.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 1.0</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-primary border-4 border-black dark:border-white p-4 shadow-hard -mr-48 md:-mr-56 z-0 rotate-[2deg]">
<div class="tape absolute -top-3 right-1/2 translate-x-1/2 w-16 h-8 rotate-0"></div>
<div class="h-40 bg-surface-light dark:bg-surface-dark border-2 border-black dark:border-white mb-4 flex items-center justify-center">
<span class="font-mono text-4xl font-black tracking-tighter">T</span>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">The Transformer</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">"Attention Is All You Need". The paper that killed RNNs and birthed the GPT architecture.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 0.9</div>
</div>
</div>
</div>
<div class="relative min-h-[500px] flex flex-col md:flex-row-reverse items-center md:items-start md:justify-start pt-20">
<div class="absolute right-0 -top-8 z-20">
<div class="bg-secondary border-4 border-black px-6 py-2 rotate-2 shadow-hard">
<span class="font-display font-black text-3xl md:text-5xl">2016 — 1950</span>
</div>
</div>
<div class="w-full pl-0 md:pl-32 pr-0 md:pr-8 flex flex-nowrap flex-row-reverse overflow-x-visible items-center justify-start py-10 perspective-1000">
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-surface-light dark:bg-surface-dark border-4 border-black dark:border-white p-4 shadow-hard -ml-48 md:-ml-56 z-30 rotate-[2deg]">
<div class="tape absolute -top-4 right-4 w-16 h-8 rotate-12 bg-red-400/30"></div>
<div class="relative border-2 border-black dark:border-white h-40 mb-4 overflow-hidden">
<img alt="Go Board Game" class="w-full h-full object-cover grayscale contrast-125" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD-xRSKl7qyBPowfXI_vdiJvvkVuLPFBQNsKpb0-4D_hlXpGHzNYv08vInYTw3Gt56PR_iHA6_ZbDL6a_veUQk4UZpwMa3_LnVJKVujkxpgKUVQCpfXmSImXaWqJuPHbmXGQF5yPiA00cYl3tHGrjOHekWDBGFJo7HboDYGfhne8CKhvcJhzfoAqqydkxOrvE6Jv_sfIVeI9rB_StLXI_4BmkAPGBgo4Dj4-x3Kignm-pBGEORawu_taKEhLLftAQ1ospTyZb3ouN4"/>
<div class="absolute inset-0 bg-primary mix-blend-multiply opacity-30"></div>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">Move 37</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">AlphaGo defeats Lee Sedol. A move of "inhuman" intuition that shocked the world.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 0.8</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-black border-4 border-black dark:border-white p-4 shadow-hard -ml-48 md:-ml-56 z-20 rotate-[-3deg]">
<div class="tape absolute -top-4 left-8 w-20 h-6 -rotate-3 bg-white/20"></div>
<div class="h-40 bg-blue-800 border-2 border-white mb-4 flex items-center justify-center">
<span class="material-icons-round text-white text-6xl">grid_on</span>
</div>
<h3 class="font-display font-bold text-xl text-white leading-none mb-2 uppercase">Deep Blue</h3>
<p class="text-xs leading-snug text-gray-300 line-clamp-4">IBM's machine defeats Kasparov. Brute force calculation triumphs over human strategy.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-white text-black px-2">FIG 0.5</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-gray-200 dark:bg-gray-800 border-4 border-black dark:border-white p-4 shadow-hard -ml-48 md:-ml-56 z-10 rotate-[1deg]">
<div class="tape absolute -top-3 right-1/2 translate-x-1/2 w-24 h-8 rotate-2 bg-blue-200/40"></div>
<div class="h-40 border-2 border-black dark:border-white border-dashed mb-4 flex items-center justify-center opacity-50">
<span class="material-icons-round text-5xl">ac_unit</span>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">The Winter</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">Funding dries up. The promises of early AI fail to materialize. Research goes underground.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 0.2</div>
</div>
<div class="stack-card relative w-72 h-96 flex-shrink-0 bg-secondary border-4 border-black dark:border-white p-4 shadow-hard -ml-48 md:-ml-56 z-0 rotate-[-2deg]">
<div class="tape absolute -top-4 right-4 w-20 h-6 -rotate-6"></div>
<div class="relative border-2 border-dashed border-black dark:border-white mb-4 h-40 bg-white dark:bg-black flex items-center justify-center">
<svg class="w-16 h-16 text-black dark:text-white" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" viewBox="0 0 24 24">
<rect height="10" rx="2" width="18" x="3" y="11"></rect>
<circle cx="12" cy="5" r="2"></circle>
<path d="M12 7v4"></path>
</svg>
</div>
<h3 class="font-display font-bold text-xl leading-none mb-2 uppercase">Imitation Game</h3>
<p class="text-xs leading-snug opacity-80 line-clamp-4">Turing's question: "Can machines think?" The philosophical start of it all.</p>
<div class="absolute bottom-4 right-4 text-[10px] font-bold bg-black text-white px-2">FIG 0.1</div>
</div>
</div>
</div>
</div>
</main>
<div class="mt-40 max-w-3xl mx-auto px-4">
<div class="bg-primary border-4 border-black p-8 relative shadow-[12px_12px_0px_0px_#000] dark:shadow-[12px_12px_0px_0px_#fff]">
<div class="absolute -top-10 -right-6 w-20 h-20 bg-white dark:bg-gray-800 border-4 border-black flex items-center justify-center rounded-full animate-bounce">
<span class="material-icons-round text-4xl text-black dark:text-white">mail</span>
</div>
<h2 class="font-display font-black text-3xl md:text-5xl mb-4 text-black uppercase tracking-tight">Join the Park</h2>
<p class="font-mono text-black mb-8 text-base font-bold">
Subscribe to the Agent Park weekly zine. No spam, just ducks and data.
</p>
<form class="flex flex-col md:flex-row gap-4">
<input class="flex-1 bg-white border-4 border-black px-6 py-4 font-mono focus:ring-0 focus:border-black focus:shadow-[4px_4px_0px_0px_#000] transition-all placeholder:text-gray-500 text-black text-lg" placeholder="Your email here..." type="email"/>
<button class="bg-black text-white px-10 py-4 font-black border-4 border-transparent hover:bg-white hover:text-black hover:border-black transition-all hover:shadow-[6px_6px_0px_0px_#000] uppercase text-lg" type="button">
SUBSCRIBE
</button>
</form>
<div class="mt-6 flex items-center gap-3">
<input class="w-6 h-6 border-4 border-black text-black focus:ring-0 rounded-none bg-white checked:bg-black" id="check" type="checkbox"/>
<label class="text-sm font-black text-black uppercase" for="check">I agree to be cool.</label>
</div>
</div>
</div>
</div>
<div class="fixed bottom-6 right-6 z-50">
<div class="bg-white dark:bg-black border-4 border-black dark:border-white p-3 shadow-hard dark:shadow-hard-dark cursor-pointer hover:-translate-y-2 transition-transform">
<span class="material-icons-round text-3xl text-black dark:text-white">arrow_upward</span>
</div>
</div>
<footer class="bg-black dark:bg-white text-white dark:text-black py-16 mt-20 border-t-8 border-primary">
<div class="max-w-7xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-12">
<div class="text-center md:text-left">
<h4 class="font-display font-black text-4xl uppercase tracking-tighter">Agent Park</h4>
<p class="font-mono text-sm opacity-60 mt-3">© 2025 Synthetic History Project — Zigzagged and Zined.</p>
</div>
<div class="flex gap-10">
<a class="font-black text-xl hover:text-primary dark:hover:text-primary transition-colors underline decoration-4 underline-offset-8" href="#">Twitter</a>
<a class="font-black text-xl hover:text-primary dark:hover:text-primary transition-colors underline decoration-4 underline-offset-8" href="#">GitHub</a>
<a class="font-black text-xl hover:text-primary dark:hover:text-primary transition-colors underline decoration-4 underline-offset-8" href="#">Discord</a>
</div>
</div>
</footer>
</body></html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

File diff suppressed because it is too large Load Diff
@@ -0,0 +1,716 @@
# 季度 AI 热点词云系统设计文档
**创建日期**: 2026-01-25
**功能类型**: 数据驱动可视化
**技术栈**: n8n + Next.js + PostgreSQL + Prisma
---
## 一、功能概述
季度 AI 热点词云是一个**自动化数据驱动的可视化词云系统**,通过 n8n 工作流从 Google Trends 采集 AI 相关热点词汇,经 AI 清洗和规则引擎处理后,自动入库并在前端展示。
### 核心目标
1. **内容营销**: 吸引访客回访查看每季度更新,提供社交分享素材
2. **教育参考**: 帮助新手理解 AI 技术演进历程
3. **数据洞察**: 展示 AI 领域热点变化趋势
### 用户旅程
1. 用户访问 `/keyword-cloud` 页面
2. 看到当前季度的 AI 热点词云(如 2024-Q1
3. 鼠标悬停在词汇上,查看详细描述和要点
4. 点击左右箭头切换不同季度,浏览历史热点
5. 可视化展示:词汇大小代表搜索热度,颜色代表分类
---
## 二、系统架构
### 架构图
```
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Google │ │ n8n │ │ PostgreSQL │
│ Trends API │───▶│ Workflow │───▶│ Database │
└─────────────┘ └─────────────┘ └──────────────┘
┌─────────────┐
│ Next.js │
│ Frontend │
└─────────────┘
```
### 数据流向
1. **定时触发**: n8n Schedule 每季度末自动执行
2. **数据采集**: Google Trends API 获取热门搜索词
3. **AI 清洗**: 过滤无关词汇,生成描述和要点
4. **规则匹配**: 根据热度分数分配视觉样式
5. **数据入库**: 写入 PostgreSQL 数据库
6. **前端展示**: Next.js 从数据库读取并渲染词云
---
## 三、数据库模型
### 3.1 Quarter 表(季度元数据)
```prisma
model Quarter {
id Int @id @default(autoincrement())
quarter String @unique // "2023-Q1", "2023-Q2"
title String @db.Text // "2023年第一季度"
titleEn String? @db.Text // "Q1 2023"
subtitle String? @db.Text // "聊天界面的黎明"
subtitleEn String? @db.Text // "The dawn of chat interface"
displayOrder Int @default(0) // 前端排序
isActive Boolean @default(true) // 是否显示
keywords Keyword[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([quarter])
@@index([displayOrder])
}
```
### 3.2 Keyword 表(关键词核心数据)
```prisma
model Keyword {
id Int @id @default(autoincrement())
word String // "ChatGPT"
trendScore Int // 0-100, 从 Google Trends 获取
// 外键关联
quarterId Int
quarter Quarter @relation(fields: [quarterId], references: [id], onDelete: Cascade)
// 内容字段(支持中英双语)
description String @db.Text // AI 生成的一句话描述
descriptionEn String? @db.Text // 英文描述
detailPoints Json // JSON 数组: ["要点1", "要点2", "要点3"]
detailPointsEn Json? // 英文版要点
// 视觉样式配置
visualConfig Json // {color, size, rotation, border}
// 元数据
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([quarterId])
@@index([trendScore])
@@index([word])
}
```
**visualConfig 字段结构示例**:
```json
{
"color": "secondary",
"size": "text-5xl",
"rotation": "rotate-1",
"border": "border-4"
}
```
### 3.3 VisualStyleRule 表(视觉样式规则配置)
```prisma
model VisualStyleRule {
id Int @id @default(autoincrement())
name String @unique // "热门大词-金色"
// 分数区间
minScore Int // 90
maxScore Int // 100
// 视觉属性
color String // "primary", "secondary", "accent"
size String // "text-5xl", "text-3xl", "text-xl"
border String // "border-4", "border-2"
rotation String? // "rotate-1", "rotate-2", null
// 控制
priority Int @default(0) // 优先级,分数重叠时按优先级
enabled Boolean @default(true) // 是否启用
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([enabled])
@@index([minScore, maxScore])
}
```
### 3.4 KeywordCloudErrorLog 表(错误日志)
```prisma
model KeywordCloudErrorLog {
id Int @id @default(autoincrement())
quarter String // "2023-Q1"
keyword String? // "ChatGPT"
errorType String // "INVALID_DATA", "API_ERROR", "DB_ERROR"
errorMessage String @db.Text // 详细错误信息
rawData Json? // 原始数据便于调试
createdAt DateTime @default(now())
@@index([quarter])
@@index([errorType])
}
```
---
## 四、n8n 工作流设计
### 4.1 工作流概览
5 个核心节点实现从数据采集到入库的完整流程。
### 4.2 节点详细配置
#### 节点 1: Schedule Trigger(定时触发)
- **类型**: `n8n-nodes-base.scheduleTrigger`
- **Cron 表达式**: `0 0 23 28-31 * *` (每季度末最后一天的 23:00)
- **月份判断**: Function 节点检查当前月份(3/6/9/12),如果不是则跳过
- **输出**: 当前季度的标识(如 "2024-Q1"
#### 节点 2: Google Trends 采集
- **类型**: `@gamal.dev/n8n-nodes-google-trends`
- **配置参数**:
- `keywords`: ["AI", "artificial intelligence", "machine learning", "GPT", "LLM"]
- `timeRange`: 当前季度的 3 个月(如 2024-01-01 to 2024-03-31
- `category`: "Science > Computer Science > AI"
- `geo`: "GB"
- **输出示例**:
```json
{
"keyword": "ChatGPT",
"trendScore": 95,
"rising": true
}
```
#### 节点 3: AI 清洗和内容生成
- **类型**: `@n8n/n8n-nodes-langchain.lmChatChain`
- **Prompt 模板**:
```
你是一个 AI 领域专家。以下是 Google Trends 采集的热门关键词列表:
{{ $json.all() }}
任务:
1. 过滤掉与 AI/机器学习无关的关键词
2. 为每个关键词生成中文描述(10-50字)
3. 生成 3 条详细要点(每条 10-30 字,客观描述,避免营销用语)
输出格式(JSON 数组):
[
{
"word": "ChatGPT",
"trendScore": 95,
"description": "OpenAI 开发的对话式人工智能助手",
"detailPoints": ["支持多轮对话", "基于 GPT-3.5 架构", "2023年用户突破1亿"]
}
]
```
#### 节点 4: 规则引擎匹配
- **类型**: `n8n-nodes-base.code`
- **逻辑**:
1. HTTP Request 获取 `VisualStyleRule` 表数据
- 方法: GET
- URL: `{{ $env.API_URL }}/api/keyword-cloud/rules`
2. 按 `priority` 排序规则
3. 遍历每个关键词,匹配第一个符合的规则(`minScore <= trendScore <= maxScore`
4. 将视觉配置注入数据
#### 节点 5: 批量写入数据库
- **类型**: `n8n-nodes-base.httpRequest`
- **方法**: POST
- **URL**: `{{ $env.API_URL }}/api/keyword-cloud/keywords`
- **认证**: Bearer Token(环境变量 `API_KEY`
- **Body**:
```json
{
"quarter": "2024-Q1",
"keywords": [
{
"word": "ChatGPT",
"trendScore": 95,
"description": "...",
"detailPoints": ["...", "...", "..."],
"visualConfig": {...}
}
]
}
```
- **批量处理**: 超过 20 个关键词时分批提交
### 4.3 错误处理
- 每个节点设置 `continueOnFail: true`
- 错误日志写入 `KeywordCloudErrorLog` 表
- 关键错误发送通知(Email/Slack
---
## 五、API 端点设计
### 5.1 GET /api/keyword-cloud/quarters
获取季度列表。
**查询参数**:
- `isActive` (可选): 只返回激活的季度
**响应示例**:
```json
{
"quarters": [
{
"id": 1,
"quarter": "2024-Q1",
"title": "2024年第一季度",
"titleEn": "Q1 2024",
"subtitle": "聊天界面的黎明",
"subtitleEn": "The dawn of chat interface",
"displayOrder": 0,
"keywordCount": 15
}
]
}
```
**排序**: 按 `displayOrder` ASC
### 5.2 GET /api/keyword-cloud/keywords/[quarter]
获取指定季度的关键词列表。
**路径参数**:
- `quarter`: 季度标识(如 "2024-Q1"
**响应示例**:
```json
{
"quarter": "2024-Q1",
"title": "2024年第一季度",
"keywords": [
{
"id": 1,
"word": "ChatGPT",
"trendScore": 95,
"description": "OpenAI 开发的对话式 AI 助手",
"detailPoints": ["支持多轮对话", "基于 GPT-3.5", "2023用户破亿"],
"visualConfig": {
"color": "secondary",
"size": "text-5xl",
"border": "border-4",
"rotation": "rotate-1"
}
}
]
}
```
**排序**: 按 `trendScore` DESC
### 5.3 POST /api/keyword-cloud/keywords
n8n 工作流写入关键词数据。
**认证**: Bearer Token (WEBHOOK_API_KEY)
**请求体**:
```json
{
"quarter": "2024-Q1",
"keywords": [
{
"word": "ChatGPT",
"trendScore": 95,
"description": "...",
"detailPoints": ["...", "...", "..."],
"visualConfig": {...}
}
]
}
```
**响应**:
```json
{
"success": true,
"created": 15,
"failed": 2,
"errors": [
{ "word": "Invalid", "error": "trendScore out of range" }
]
}
```
**逻辑**:
1. 验证 API Key
2. 查找或创建 `Quarter` 记录
3. 批量创建 `Keyword` 记录(Prisma `createMany`
4. 失败记录写入 `KeywordCloudErrorLog` 表
5. 返回成功/失败统计
### 5.4 GET /api/keyword-cloud/rules
获取视觉样式规则配置(n8n 规则引擎使用)。
**查询参数**:
- `enabled` (可选): 只返回启用的规则
**响应示例**:
```json
{
"rules": [
{
"id": 1,
"name": "热门大词-金色",
"minScore": 90,
"maxScore": 100,
"visualConfig": {
"color": "primary",
"size": "text-5xl",
"border": "border-4",
"rotation": "rotate-1"
},
"priority": 0,
"enabled": true
}
]
}
```
**排序**: 按 `priority` ASC
### 5.5 POST /api/keyword-cloud/rules
创建新的视觉样式规则(管理员功能)。
**请求体**: 同单个规则对象
**验证**:
- `minScore < maxScore`
- 必填字段检查
- 颜色值必须是预定义的颜色类别
---
## 六、前端组件设计
### 6.1 路由结构
```
src/app/[locale]/keyword-cloud/
├── page.tsx # 主页面
└── components/
├── KeywordCloud.tsx # 词云容器组件
├── CloudWord.tsx # 单个词汇组件
├── QuarterNavigator.tsx # 季度切换导航
├── WordPopover.tsx # 弹出框详情
└── ProgressIndicator.tsx # 进度条
```
### 6.2 核心组件
#### KeywordCloud.tsx
词云容器组件,负责数据获取和布局。
```typescript
interface KeywordCloudProps {
quarter: string;
}
function KeywordCloud({ quarter }: KeywordCloudProps) {
const { data, isLoading } = useKeywordData(quarter);
return (
<div className="bg-white/50 dark:bg-black/20 backdrop-blur-sm border-4 border-black p-8 md:p-12 shadow-hard">
<ProgressIndicator currentQuarter={quarter} totalQuarters={4} />
<QuarterNavigator current={quarter} />
<div className="word-cluster py-12">
{data?.keywords.map((keyword) => (
<CloudWord key={keyword.id} data={keyword} />
))}
</div>
<FunFactBubble fact={data.funFact} />
</div>
);
}
```
#### CloudWord.tsx
单个词汇组件,应用视觉样式和悬停交互。
```typescript
interface CloudWordProps {
data: Keyword;
}
function CloudWord({ data }: CloudWordProps) {
const { word, visualConfig, description, detailPoints } = data;
const { color, size, border, rotation } = visualConfig;
const colorClass = colorMap[color];
return (
<span className={cn(
"cloud-word",
border,
"border-black",
colorClass,
"px-6 py-3",
"rounded-full",
size,
"font-black",
"shadow-hard",
rotation,
"transition-all",
"hover:scale-105",
"cursor-pointer"
)}>
{word}
<WordPopover title={word} description={description} points={detailPoints} />
</span>
);
}
```
#### WordPopover.tsx
弹出框组件,显示词汇的详细信息。
```typescript
interface WordPopoverProps {
title: string;
description: string;
points: string[];
}
function WordPopover({ title, description, points }: WordPopoverProps) {
return (
<div className="popover">
<div className="bg-primary text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">
{title}
</div>
<div className="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
<div className="flex gap-2 items-start">
<span>•</span>
<span>{description}</span>
</div>
{points.map((point, i) => (
<div key={i} className="flex gap-2 items-start">
<span>•</span>
<span>{point}</span>
</div>
))}
</div>
</div>
);
}
```
#### QuarterNavigator.tsx
季度切换导航组件。
```typescript
function QuarterNavigator({ current }: { current: string }) {
const quarters = ["2023-Q1", "2023-Q2", "2023-Q3", "2023-Q4"];
const currentIndex = quarters.indexOf(current);
return (
<div className="flex items-center justify-between gap-8">
<button
disabled={currentIndex === 0}
className="nav-button"
onClick={() => navigate(quarters[currentIndex - 1])}
>
<span className="material-symbols-outlined text-4xl">chevron_left</span>
</button>
<div className="text-center">
<div className="bg-primary border-4 border-black px-10 py-4 shadow-hard font-display font-bold text-5xl">
{current}
</div>
</div>
<button
disabled={currentIndex === quarters.length - 1}
className="nav-button"
onClick={() => navigate(quarters[currentIndex + 1])}
>
<span className="material-symbols-outlined text-4xl">chevron_right</span>
</button>
</div>
);
}
```
### 6.3 数据获取
```typescript
// src/hooks/useKeywordCloud.ts
export async function getKeywordData(quarter: string) {
const response = await fetch(`${API_URL}/api/keyword-cloud/keywords/${quarter}`);
return response.json();
}
```
### 6.4 样式系统
复用项目现有的 Tailwind 配置和样式类:
- `.cloud-word`: 词云词汇的基础样式
- `.popover`: 弹出框样式(包括箭头)
- `.word-cluster`: 词汇容器布局
- `.nav-button`: 导航按钮样式
响应式断点:`md:`, `lg:`
---
## 七、错误处理和监控
### 7.1 分层错误处理
**n8n 工作流层**:
- 每个节点 `continueOnFail: true`
- 失败记录写入 `KeywordCloudErrorLog` 表
- 关键错误发送通知
**API 层**:
- Zod schema 验证
- 部分成功响应模式
- HTTP 状态码规范
**前端层**:
- 友好的错误提示
- Error Boundary
- 重试机制
### 7.2 监控策略
- n8n 执行日志监控
- 定期检查 `KeywordCloudErrorLog` 表
- API 响应时间监控
- 前端错误追踪
---
## 八、测试策略
### 8.1 单元测试(Vitest
- API 路由处理逻辑
- 规则引擎匹配算法
- 数据验证 schemas
### 8.2 E2E 测试
- 季度切换功能
- 弹出框交互
- 响应式布局
### 8.3 n8n 工作流测试
- 使用测试环境 API 手动触发
- 验证生成的数据质量
- 检查规则匹配结果
---
## 九、部署指南
### 9.1 环境变量
```bash
# .env.local
DATABASE_URL="..."
WEBHOOK_API_KEY="..."
N8N_WEBHOOK_URL="https://your-n8n-instance.com/..."
```
### 9.2 数据库迁移
```bash
pnpm prisma migrate dev --name add_keyword_cloud_tables
```
### 9.3 n8n 部署
1. 使用自托管 n8n 或 n8n Cloud
2. 配置环境变量(API_URL, API_KEY
3. 设置 Cron 定时任务
4. 测试工作流执行
### 9.4 初始化数据
1. 创建第一个 `Quarter` 记录(2023-Q1
2. 配置 3-5 条 `VisualStyleRule`:
- 90-100: 热门大词(金色, text-5xl, border-4
- 70-89: 中等词汇(蓝色, text-3xl, border-2
- 50-69: 小词汇(紫色, text-xl, border-2
- 0-49: 长尾词(灰色, text-base, border-2
### 9.5 监控设置
- n8n 执行日志告警
- 错误日志定期检查
- API 性能监控
---
## 十、未来扩展
1. **预测功能**: 基于历史数据预测下一个热点词汇
2. **趋势分析**: 展示词汇热度的季度变化曲线
3. **用户贡献**: 允许用户提交词汇建议
4. **多维度**: 按技术栈、应用领域等维度分类
5. **导出功能**: 导出季度报告(PDF/图片)
---
## 附录
### A. 颜色系统
```typescript
const colorMap = {
primary: "bg-primary", // Gold (#FFD700)
secondary: "bg-secondary", // Blue (#7FB5FF)
accent: "bg-accent", // Purple (#C39BD3)
gray: "bg-gray-100", // Gray
};
```
### B. 字体大小映射
```typescript
const sizeMap = {
hot: "text-5xl", // 90-100 分
medium: "text-3xl", // 70-89 分
small: "text-xl", // 50-69 分
tiny: "text-base", // 0-49 分
};
```
### C. 参考资源
- Google Trends API: https://trends.google.com/
- n8n 文档: https://docs.n8n.io/
- Tailwind CSS: https://tailwindcss.com/