fix(eslint): reduce warnings in GitHub Actions deployment

- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
sanbuphy
2026-02-18 17:38:10 +08:00
parent 8b01686e68
commit 0eba9e87e9
456 changed files with 28450 additions and 9677 deletions
@@ -43,12 +43,15 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
<span class="value">{{ round }}</span>
<span class="label">当前轮次</span>
</div>
<div class="stat-divider"></div>
<div class="stat-divider" />
<div class="stat-item">
<span class="value" :class="{ error: isOverflow }">{{ totalTokens }}</span>
<span
class="value"
:class="{ error: isOverflow }"
>{{ totalTokens }}</span>
<span class="label">Token 占用</span>
</div>
<div class="stat-divider"></div>
<div class="stat-divider" />
<div class="stat-item">
<span class="value">${{ currentCost }}</span>
<span class="label">本轮成本</span>
@@ -61,11 +64,17 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
<!-- 上方预留空间给溢出提示 -->
<div class="overflow-zone">
<transition name="fade">
<div v-if="isOverflow" class="overflow-badge">
<div
v-if="isOverflow"
class="overflow-badge"
>
<span class="icon">🗑</span>
<span>溢出截断 {{ forgottenRounds }} 轮对话已被遗忘</span>
</div>
<div v-else class="safe-badge">
<div
v-else
class="safe-badge"
>
<span class="icon"></span>
<span>记忆完整</span>
</div>
@@ -81,15 +90,23 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
<!-- 堆叠内容容器 -->
<!-- 使用 flex-direction: column-reverse 让底部对齐 -->
<div class="stack-container">
<!-- System (基座) -->
<div class="block system" :style="{ height: `${systemHeight}%` }">
<div
class="block system"
:style="{ height: `${systemHeight}%` }"
>
<span class="block-text">System Prompt ({{ systemPromptTokens }})</span>
</div>
<!-- History (中间) -->
<div class="block history" :style="{ height: `${historyHeight}%` }">
<span class="block-text" v-if="historyHeight > 10">
<div
class="block history"
:style="{ height: `${historyHeight}%` }"
>
<span
v-if="historyHeight > 10"
class="block-text"
>
History ({{ round - 1 }} rounds)
</span>
<!-- 溢出遮罩当溢出时History 的底部实际上是被挤出去 -->
@@ -103,10 +120,12 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
</div>
<!-- Input (最新) -->
<div class="block input" :style="{ height: `${inputHeight}%` }">
<div
class="block input"
:style="{ height: `${inputHeight}%` }"
>
<span class="block-text">New Input</span>
</div>
</div>
<!-- 溢出遮罩层如果 totalHeight > 100%显示一个红色的遮罩在顶部表示这部分虽然生成了但塞不进去/或者旧的被挤走了 -->
@@ -121,12 +140,12 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
<div class="slider-wrapper">
<span class="slider-hint">拖动滑块增加对话轮次</span>
<input
v-model.number="round"
type="range"
min="1"
:max="maxRound"
v-model.number="round"
class="custom-slider"
/>
>
<div class="slider-labels">
<span> 1 </span>
<span> {{ maxRound }} </span>
@@ -137,7 +156,10 @@ const totalHeight = computed(() => systemHeight.value + historyHeight.value + in
<p v-if="!isOverflow">
💡 <strong>一切正常</strong>当前 Token ({{ totalTokens }}) 未超过窗口限制模型能完美回忆起所有对话细节
</p>
<p v-else class="warning-text">
<p
v-else
class="warning-text"
>
<strong>发生遗忘</strong>Token 总量 ({{ totalTokens }}) 已超过窗口限制 ({{ windowLimit }})
为了放入新对话系统被迫丢弃了最早的 <strong>{{ forgottenRounds }}</strong> 轮历史记录
</p>
@@ -57,7 +57,9 @@ const compress = async (mode) => {
<div class="context-compression-demo">
<!-- 1. Strategy Selection -->
<div class="section control-panel">
<div class="section-label">1. 选择压缩策略</div>
<div class="section-label">
1. 选择压缩策略
</div>
<div class="strategy-group">
<button
v-for="s in strategies"
@@ -66,8 +68,12 @@ const compress = async (mode) => {
:class="{ active: currentMode === s.id }"
@click="compress(s.id)"
>
<div class="btn-label">{{ s.label }}</div>
<div class="btn-desc">{{ s.desc }}</div>
<div class="btn-label">
{{ s.label }}
</div>
<div class="btn-desc">
{{ s.desc }}
</div>
</button>
</div>
</div>
@@ -82,49 +88,72 @@ const compress = async (mode) => {
v-model="originalText"
class="text-content original-input"
placeholder="在此输入长文本..."
></textarea>
/>
</div>
<!-- Connector / Process -->
<div class="flow-connector">
<div class="line"></div>
<div class="process-icon" :class="{ spinning: isCompressing }">
<div class="line" />
<div
class="process-icon"
:class="{ spinning: isCompressing }"
>
{{ isCompressing ? '⚙️' : '⬇️' }}
</div>
<div class="badge-container" v-if="compressedText && !isCompressing">
<div
v-if="compressedText && !isCompressing"
class="badge-container"
>
<span class="ratio-badge">-{{ compressionRatio }}%</span>
</div>
</div>
<!-- 3. Output Area -->
<div class="section output-area" :class="{ 'has-result': compressedText }">
<div
class="section output-area"
:class="{ 'has-result': compressedText }"
>
<div class="section-header">
<span class="label">压缩后 (Compressed)</span>
<span class="token-count" v-if="compressedText">{{ compressedTokens }} tokens</span>
<span
v-if="compressedText"
class="token-count"
>{{ compressedTokens }} tokens</span>
</div>
<div class="text-content result-box">
<div v-if="isCompressing" class="loading-state">
<span class="spinner"></span> 正在压缩...
<div
v-if="isCompressing"
class="loading-state"
>
<span class="spinner" /> 正在压缩...
</div>
<pre v-else-if="compressedText">{{ compressedText }}</pre>
<div v-else class="placeholder">
<div
v-else
class="placeholder"
>
请点击上方按钮开始压缩
</div>
</div>
<!-- Mini Metrics (Inside Output Area) -->
<div class="mini-metrics" v-if="compressedText && !isCompressing">
<div
v-if="compressedText && !isCompressing"
class="mini-metrics"
>
<div class="metric-item">
<span class="metric-label">节省空间</span>
<span class="metric-val highlight">{{ compressionRatio }}%</span>
</div>
<div class="metric-bar">
<div class="bar-fill" :style="{ width: (100 - compressionRatio) + '%' }"></div>
<div
class="bar-fill"
:style="{ width: (100 - compressionRatio) + '%' }"
/>
</div>
</div>
</div>
</div>
</template>
@@ -17,10 +17,15 @@
<div class="control-panel">
<div class="stat-group">
<div class="stat-item">
<span class="value" :class="{ error: isOverflow }">{{ usedTokens }}</span>
<span
class="value"
:class="{ error: isOverflow }"
>{{ usedTokens }}</span>
<span class="label">已经写了多少个 token</span>
</div>
<div class="stat-divider">/</div>
<div class="stat-divider">
/
</div>
<div class="stat-item">
<span class="value">{{ maxTokens }}</span>
<span class="label">黑板最多能写几个 token</span>
@@ -35,14 +40,19 @@
width: `${Math.min(usagePercentage, 100)}%`,
backgroundColor: progressBarColor
}"
></div>
/>
</div>
<div class="percentage-label">
{{ usagePercentage.toFixed(1) }}%
</div>
<div class="percentage-label">{{ usagePercentage.toFixed(1) }}%</div>
</div>
</div>
<div class="visualization-area">
<div class="window-frame" :class="{ overflow: isOverflow }">
<div
class="window-frame"
:class="{ overflow: isOverflow }"
>
<div class="window-header">
<span class="icon">🧠</span>
<span>模型能看到的小黑板上下文窗口</span>
@@ -61,8 +71,11 @@
</transition-group>
</div>
<div v-if="isOverflow" class="overflow-indicator">
<div class="overflow-line"></div>
<div
v-if="isOverflow"
class="overflow-indicator"
>
<div class="overflow-line" />
<span class="overflow-text"> 达到上下文上限 (已截断)</span>
</div>
</div>
@@ -72,16 +85,31 @@
<div class="input-header">
<label>输入内容看黑板怎么被一点点写满</label>
<div class="actions">
<button class="action-btn" @click="fillLorem(10)">填一段短文本</button>
<button class="action-btn" @click="fillLorem(60)">一下子塞满黑板</button>
<button class="action-btn outline" @click="clear">清空</button>
<button
class="action-btn"
@click="fillLorem(10)"
>
填一段短文本
</button>
<button
class="action-btn"
@click="fillLorem(60)"
>
一下子塞满黑板
</button>
<button
class="action-btn outline"
@click="clear"
>
清空
</button>
</div>
</div>
<textarea
v-model="inputText"
placeholder="在这里输入几句话,看看小黑板是怎么逐渐被写满的..."
rows="4"
></textarea>
/>
</div>
<div class="info-box">
@@ -1,7 +1,9 @@
<template>
<div class="intro-prs">
<div class="prs-item">
<div class="prs-title">问题</div>
<div class="prs-title">
问题
</div>
<ul>
<li><strong>上下文难以保持一致</strong>对话一长前后语义容易脱节</li>
<li><strong>关键事实容易丢失</strong>早期给出的信息在后续轮次中难以被准确引用</li>
@@ -9,7 +11,9 @@
</ul>
</div>
<div class="prs-item">
<div class="prs-title">可能的成因</div>
<div class="prs-title">
可能的成因
</div>
<ul>
<li><strong>视野仅限当前调用</strong>模型只能依赖这一轮提供的上下文</li>
<li><strong>信息缺乏结构化组织</strong>重要信息与次要细节混在一起难以形成稳定记忆</li>
@@ -17,7 +21,9 @@
</ul>
</div>
<div class="prs-item">
<div class="prs-title">带来的影响</div>
<div class="prs-title">
带来的影响
</div>
<ul>
<li><strong>回答质量不稳定</strong>对话越长模型越难保持一致性和可追溯性</li>
<li><strong>成本难以预估</strong>每轮上下文大小高度波动调用费用不可控</li>
@@ -3,8 +3,12 @@
<div class="control-panel">
<div class="control-group">
<label class="toggle-switch">
<input type="checkbox" v-model="isCacheEnabled" :disabled="isProcessing">
<span class="slider"></span>
<input
v-model="isCacheEnabled"
type="checkbox"
:disabled="isProcessing"
>
<span class="slider" />
</label>
<span class="label">开启背课文加速前缀复用 / KV Cache</span>
</div>
@@ -27,12 +31,18 @@
<div class="block-header">
<span class="icon"></span>
<span>固定开场白System Prompt</span>
<span class="badge" v-if="isCacheEnabled && hasCache">已背过</span>
<span
v-if="isCacheEnabled && hasCache"
class="badge"
>已背过</span>
</div>
<div class="block-content">
你是一个乐于助人的 AI 助手... 大约 500 token
</div>
<div class="process-indicator" v-if="processingStep === 'system'">
<div
v-if="processingStep === 'system'"
class="process-indicator"
>
计算中...
</div>
</div>
@@ -49,7 +59,10 @@
<div class="block-content">
用户你好... 大约 200 token
</div>
<div class="process-indicator" v-if="processingStep === 'history'">
<div
v-if="processingStep === 'history'"
class="process-indicator"
>
计算中...
</div>
</div>
@@ -66,7 +79,10 @@
<div class="block-content">
{{ currentQuery }} 大约 50 token
</div>
<div class="process-indicator" v-if="processingStep === 'query'">
<div
v-if="processingStep === 'query'"
class="process-indicator"
>
计算中...
</div>
</div>
@@ -75,19 +91,35 @@
<div class="metrics-panel">
<div class="metric-card">
<div class="metric-value">{{ metrics.ttft }}ms</div>
<div class="metric-label">开口速度首字延迟 TTFT</div>
<div class="metric-diff" :class="{ 'good': metrics.savedTime > 0 }" v-if="metrics.savedTime > 0">
<div class="metric-value">
{{ metrics.ttft }}ms
</div>
<div class="metric-label">
开口速度首字延迟 TTFT
</div>
<div
v-if="metrics.savedTime > 0"
class="metric-diff"
:class="{ 'good': metrics.savedTime > 0 }"
>
节省 {{ metrics.savedTime }}ms
</div>
</div>
<div class="metric-card">
<div class="metric-value">{{ metrics.processedTokens }}</div>
<div class="metric-label">这次一共算了多少个 token</div>
<div class="metric-value">
{{ metrics.processedTokens }}
</div>
<div class="metric-label">
这次一共算了多少个 token
</div>
</div>
<div class="metric-card">
<div class="metric-value">{{ metrics.cost }}</div>
<div class="metric-label">大致算力消耗越少越省钱</div>
<div class="metric-value">
{{ metrics.cost }}
</div>
<div class="metric-label">
大致算力消耗越少越省钱
</div>
</div>
</div>
@@ -4,8 +4,8 @@
<div class="control-group">
<label>关键信息大概在整段话的哪个位置{{ needlePosition }}%</label>
<input
type="range"
v-model.number="needlePosition"
type="range"
min="0"
max="100"
step="1"
@@ -17,25 +17,37 @@
<div class="visualization-area">
<!-- Context Window Bar -->
<div class="context-bar">
<div class="context-label start">Start (System)</div>
<div class="context-label end">End (Query)</div>
<div class="context-label start">
Start (System)
</div>
<div class="context-label end">
End (Query)
</div>
<!-- Attention Heatmap Background -->
<div class="attention-heatmap"></div>
<div class="attention-heatmap" />
<!-- Needle Marker -->
<div
class="needle-marker"
:style="{ left: `${needlePosition}%` }"
>
<div class="needle-icon">📍</div>
<div class="needle-tooltip">关键事实</div>
<div class="needle-icon">
📍
</div>
<div class="needle-tooltip">
关键事实
</div>
</div>
</div>
<!-- Probability Curve Chart -->
<div class="chart-container">
<svg viewBox="0 0 100 60" preserveAspectRatio="none" class="chart-svg">
<svg
viewBox="0 0 100 60"
preserveAspectRatio="none"
class="chart-svg"
>
<!-- U-Curve Path -->
<path
d="M 0 5 Q 50 55 100 5"
@@ -52,21 +64,34 @@
fill="var(--vp-c-brand)"
/>
</svg>
<div class="chart-label y-axis">被记住的概率</div>
<div class="chart-label x-axis">在上下文里的位置</div>
<div class="chart-label y-axis">
被记住的概率
</div>
<div class="chart-label x-axis">
在上下文里的位置
</div>
</div>
</div>
<div class="metrics-panel">
<div class="metric-card">
<div class="metric-value" :class="getScoreClass(retrievalProb)">
<div
class="metric-value"
:class="getScoreClass(retrievalProb)"
>
{{ retrievalProb.toFixed(1) }}%
</div>
<div class="metric-label">检索成功率</div>
<div class="metric-label">
检索成功率
</div>
</div>
<div class="metric-card">
<div class="metric-value">{{ positionLabel }}</div>
<div class="metric-label">位置描述</div>
<div class="metric-value">
{{ positionLabel }}
</div>
<div class="metric-label">
位置描述
</div>
</div>
</div>
@@ -172,23 +172,47 @@ const getLayerStyle = (layerId) => {
<div class="demo-grid">
<!-- Left: Chat Simulator -->
<div class="chat-panel">
<div class="panel-header">📱 用户视角 (Chat)</div>
<div class="panel-header">
📱 用户视角 (Chat)
</div>
<div class="chat-window">
<div v-for="(msg, idx) in currentStep.layers.chat" :key="idx" class="chat-bubble" :class="msg.startsWith('User') ? 'user' : 'ai'">
<div
v-for="(msg, idx) in currentStep.layers.chat"
:key="idx"
class="chat-bubble"
:class="msg.startsWith('User') ? 'user' : 'ai'"
>
{{ msg.split(': ')[1] || msg }}
</div>
<div v-if="currentStep.user && !currentStep.layers.chat.some(m => m.includes(currentStep.user))" class="chat-bubble user pending">
<div
v-if="currentStep.user && !currentStep.layers.chat.some(m => m.includes(currentStep.user))"
class="chat-bubble user pending"
>
{{ currentStep.user }}...
</div>
<div v-if="currentStep.ai_thinking" class="chat-bubble thinking">
<div
v-if="currentStep.ai_thinking"
class="chat-bubble thinking"
>
💭 {{ currentStep.ai_thinking }}
</div>
</div>
<div class="controls">
<div class="step-info">步骤 {{ currentStepIndex + 1 }} / {{ currentScenario.steps.length }}</div>
<div class="step-info">
步骤 {{ currentStepIndex + 1 }} / {{ currentScenario.steps.length }}
</div>
<div class="btn-group">
<button class="nav-btn" @click="prevStep" :disabled="currentStepIndex === 0"> 上一步</button>
<button class="nav-btn primary" @click="nextStep">
<button
class="nav-btn"
:disabled="currentStepIndex === 0"
@click="prevStep"
>
上一步
</button>
<button
class="nav-btn primary"
@click="nextStep"
>
{{ isLastStep ? '🔄 重新演示' : '下一步 ➡️' }}
</button>
</div>
@@ -197,9 +221,10 @@ const getLayerStyle = (layerId) => {
<!-- Right: Memory Palace Internals -->
<div class="palace-panel">
<div class="panel-header">🧠 AI 视角 (Context Construction)</div>
<div class="panel-header">
🧠 AI 视角 (Context Construction)
</div>
<div class="context-visualizer">
<!-- Layer 1: Base -->
<div class="layer-box base">
<div class="layer-label">
@@ -207,7 +232,9 @@ const getLayerStyle = (layerId) => {
<span class="title">Layer 1: 地基 (System)</span>
<span class="badge">KV Cached</span>
</div>
<div class="layer-content">{{ currentStep.layers.base }}</div>
<div class="layer-content">
{{ currentStep.layers.base }}
</div>
</div>
<!-- Layer 2: Task -->
@@ -217,7 +244,9 @@ const getLayerStyle = (layerId) => {
<span class="title">Layer 2: 支柱 (Task)</span>
<span class="badge">Pinned</span>
</div>
<div class="layer-content">{{ currentStep.layers.task }}</div>
<div class="layer-content">
{{ currentStep.layers.task }}
</div>
</div>
<!-- Layer 3: Chat -->
@@ -228,24 +257,48 @@ const getLayerStyle = (layerId) => {
<span class="badge">Sliding</span>
</div>
<div class="layer-content">
<div v-for="(m, i) in currentStep.layers.chat" :key="i" class="mini-line">{{ m }}</div>
<div v-if="currentStep.layers.chat.length === 0" class="empty-hint">(暂无对话历史)</div>
<div
v-for="(m, i) in currentStep.layers.chat"
:key="i"
class="mini-line"
>
{{ m }}
</div>
<div
v-if="currentStep.layers.chat.length === 0"
class="empty-hint"
>
(暂无对话历史)
</div>
</div>
</div>
<!-- Layer 4: RAG -->
<div class="layer-box rag" :class="{ active: currentStep.layers.rag.length > 0 }">
<div
class="layer-box rag"
:class="{ active: currentStep.layers.rag.length > 0 }"
>
<div class="layer-label">
<span class="icon">📚</span>
<span class="title">Layer 4: 图书馆 (RAG)</span>
<span class="badge ephemeral">Temp</span>
</div>
<div class="layer-content">
<div v-for="(r, i) in currentStep.layers.rag" :key="i" class="rag-item">{{ r }}</div>
<div v-if="currentStep.layers.rag.length === 0" class="empty-hint">(当前无需检索)</div>
<div
v-for="(r, i) in currentStep.layers.rag"
:key="i"
class="rag-item"
>
{{ r }}
</div>
<div
v-if="currentStep.layers.rag.length === 0"
class="empty-hint"
>
(当前无需检索)
</div>
</div>
</div>
</div>
<!-- Explanation Footer -->
@@ -59,7 +59,6 @@ const isComplete = computed(() => currentStep.value === 4)
<template>
<div class="memory-palace-demo">
<!-- Visual Area -->
<div class="palace-container">
<div class="palace-stack">
@@ -71,11 +70,20 @@ const isComplete = computed(() => currentStep.value === 4)
<div class="layer-content">
<span class="icon">{{ steps[3].icon }}</span>
<div class="text">
<div class="layer-title">{{ steps[3].title }}</div>
<div class="layer-desc">{{ steps[3].desc }}</div>
<div class="layer-title">
{{ steps[3].title }}
</div>
<div class="layer-desc">
{{ steps[3].desc }}
</div>
</div>
</div>
<div class="layer-detail" v-if="currentStep >= 4">{{ steps[3].detail }}</div>
<div
v-if="currentStep >= 4"
class="layer-detail"
>
{{ steps[3].detail }}
</div>
</div>
<!-- Layer 3: Chat -->
@@ -86,11 +94,20 @@ const isComplete = computed(() => currentStep.value === 4)
<div class="layer-content">
<span class="icon">{{ steps[2].icon }}</span>
<div class="text">
<div class="layer-title">{{ steps[2].title }}</div>
<div class="layer-desc">{{ steps[2].desc }}</div>
<div class="layer-title">
{{ steps[2].title }}
</div>
<div class="layer-desc">
{{ steps[2].desc }}
</div>
</div>
</div>
<div class="layer-detail" v-if="currentStep >= 3">{{ steps[2].detail }}</div>
<div
v-if="currentStep >= 3"
class="layer-detail"
>
{{ steps[2].detail }}
</div>
</div>
<!-- Layer 2: Task -->
@@ -101,11 +118,20 @@ const isComplete = computed(() => currentStep.value === 4)
<div class="layer-content">
<span class="icon">{{ steps[1].icon }}</span>
<div class="text">
<div class="layer-title">{{ steps[1].title }}</div>
<div class="layer-desc">{{ steps[1].desc }}</div>
<div class="layer-title">
{{ steps[1].title }}
</div>
<div class="layer-desc">
{{ steps[1].desc }}
</div>
</div>
</div>
<div class="layer-detail" v-if="currentStep >= 2">{{ steps[1].detail }}</div>
<div
v-if="currentStep >= 2"
class="layer-detail"
>
{{ steps[1].detail }}
</div>
</div>
<!-- Layer 1: Base -->
@@ -116,15 +142,27 @@ const isComplete = computed(() => currentStep.value === 4)
<div class="layer-content">
<span class="icon">{{ steps[0].icon }}</span>
<div class="text">
<div class="layer-title">{{ steps[0].title }}</div>
<div class="layer-desc">{{ steps[0].desc }}</div>
<div class="layer-title">
{{ steps[0].title }}
</div>
<div class="layer-desc">
{{ steps[0].desc }}
</div>
</div>
</div>
<div class="layer-detail" v-if="currentStep >= 1">{{ steps[0].detail }}</div>
<div
v-if="currentStep >= 1"
class="layer-detail"
>
{{ steps[0].detail }}
</div>
</div>
<!-- Empty State Placeholder -->
<div class="empty-placeholder" v-if="currentStep === 0">
<div
v-if="currentStep === 0"
class="empty-placeholder"
>
🚧 空地点击下方按钮开始建造记忆宫殿
</div>
</div>
@@ -135,28 +173,48 @@ const isComplete = computed(() => currentStep.value === 4)
<div class="step-indicator">
当前进度: {{ currentStep }}/4
</div>
<button class="build-btn" @click="nextStep" :class="{ 'reset-mode': isComplete }">
<button
class="build-btn"
:class="{ 'reset-mode': isComplete }"
@click="nextStep"
>
{{ isComplete ? '🔄 重置重建' : (currentStep === 0 ? '🏗️ 开始建造' : ' 添加下一层') }}
</button>
</div>
<!-- Explanation Box -->
<div class="explanation-box" v-if="currentStep > 0">
<div class="exp-title">为什么这样设计</div>
<div class="exp-content" v-if="currentStep === 1">
<div
v-if="currentStep > 0"
class="explanation-box"
>
<div class="exp-title">
为什么这样设计
</div>
<div
v-if="currentStep === 1"
class="exp-content"
>
**地基最稳** System Prompt 放在最前面利用 KV Cache 机制 AI "背下来"后续请求**速度快且免费**
</div>
<div class="exp-content" v-if="currentStep === 2">
<div
v-if="currentStep === 2"
class="exp-content"
>
**目标明确**无论聊得多嗨任务目标写一个 Python 爬虫必须**钉死**防止 AI 聊偏了
</div>
<div class="exp-content" v-if="currentStep === 3">
<div
v-if="currentStep === 3"
class="exp-content"
>
**保持鲜活**最近的对话最重要用滑动窗口保留**旧的自动忘掉**给新信息腾地方
</div>
<div class="exp-content" v-if="currentStep === 4">
<div
v-if="currentStep === 4"
class="exp-content"
>
**无限外脑**遇到不懂的不要瞎编图书馆查资料**用完即走**不占宝贵的脑容量
</div>
</div>
</div>
</template>
@@ -86,7 +86,6 @@ const search = async () => {
<template>
<div class="rag-demo">
<!-- Step 1: User Input -->
<div class="step-section input-section">
<div class="step-label">
@@ -98,13 +97,13 @@ const search = async () => {
v-model="query"
type="text"
placeholder="输入问题..."
@keyup.enter="search"
:disabled="isSearching"
/>
@keyup.enter="search"
>
<button
class="action-btn"
@click="search"
:disabled="isSearching || !query"
@click="search"
>
{{ isSearching ? '检索中...' : '🚀 开始检索' }}
</button>
@@ -112,18 +111,32 @@ const search = async () => {
</div>
<!-- Arrow Connection -->
<div class="flow-arrow" :class="{ active: currentStep >= 1 }">
<div class="line"></div>
<div class="icon">🔍</div>
<div
class="flow-arrow"
:class="{ active: currentStep >= 1 }"
>
<div class="line" />
<div class="icon">
🔍
</div>
</div>
<!-- Step 2: Library Scanning -->
<div class="step-section library-section" :class="{ 'is-scanning': currentStep === 1 }">
<div
class="step-section library-section"
:class="{ 'is-scanning': currentStep === 1 }"
>
<div class="step-label">
<span class="step-num">2</span>
<span class="step-text">图书馆检索 (Retrieval)</span>
<span class="status-badge" v-if="currentStep === 1">正在扫描...</span>
<span class="status-badge success" v-if="currentStep >= 2">命中 {{ retrievedDocs.length }} </span>
<span
v-if="currentStep === 1"
class="status-badge"
>正在扫描...</span>
<span
v-if="currentStep >= 2"
class="status-badge success"
>命中 {{ retrievedDocs.length }} </span>
</div>
<div class="docs-grid">
@@ -139,26 +152,42 @@ const search = async () => {
<div class="doc-header">
<span class="doc-icon">📄</span>
<span class="doc-title">{{ doc.title }}</span>
<span class="doc-score" v-if="currentStep >= 2 && doc.score > 0.6">
<span
v-if="currentStep >= 2 && doc.score > 0.6"
class="doc-score"
>
{{ (doc.score * 100).toFixed(0) }}% 相关
</span>
</div>
<div class="doc-content">{{ doc.content }}</div>
<div class="doc-content">
{{ doc.content }}
</div>
<!-- Visual effect for scanning -->
<div class="scan-line" v-if="currentStep === 1"></div>
<div
v-if="currentStep === 1"
class="scan-line"
/>
</div>
</div>
</div>
<!-- Arrow Connection -->
<div class="flow-arrow" :class="{ active: currentStep >= 2 }">
<div class="line"></div>
<div class="icon"> 复制粘贴</div>
<div
class="flow-arrow"
:class="{ active: currentStep >= 2 }"
>
<div class="line" />
<div class="icon">
复制粘贴
</div>
</div>
<!-- Step 3: Context Assembly -->
<div class="step-section context-section" :class="{ active: currentStep >= 3 }">
<div
class="step-section context-section"
:class="{ active: currentStep >= 3 }"
>
<div class="step-label">
<span class="step-num">3</span>
<span class="step-text">最终上下文 (Final Prompt)</span>
@@ -170,14 +199,26 @@ const search = async () => {
你是一个专业的 AI 助手请基于下方检索到的资料回答用户的提问
</div>
<div class="retrieved-block" v-if="currentStep >= 2">
<div class="block-header">📚 检索到的资料 (Context)</div>
<div v-if="retrievedDocs.length > 0">
<div v-for="doc in retrievedDocs" :key="doc.id" class="retrieved-item">
{{ doc.content }}
</div>
<div
v-if="currentStep >= 2"
class="retrieved-block"
>
<div class="block-header">
📚 检索到的资料 (Context)
</div>
<div v-else class="empty-state">
<div v-if="retrievedDocs.length > 0">
<div
v-for="doc in retrievedDocs"
:key="doc.id"
class="retrieved-item"
>
{{ doc.content }}
</div>
</div>
<div
v-else
class="empty-state"
>
(未找到相关资料)
</div>
</div>
@@ -188,7 +229,6 @@ const search = async () => {
</div>
</div>
</div>
</div>
</template>
@@ -19,7 +19,9 @@
<span class="value">{{ totalMessages }}</span>
<span class="label">现在一共记了几条</span>
</div>
<div class="stat-divider">/</div>
<div class="stat-divider">
/
</div>
<div class="stat-item">
<span class="value">{{ maxSlots }}</span>
<span class="label">黑板最多能记几条</span>
@@ -30,7 +32,7 @@
class="usage-fill"
:style="{ width: `${(totalMessages / maxSlots) * 100}%` }"
:class="{ full: totalMessages >= maxSlots }"
></div>
/>
</div>
</div>
@@ -54,15 +56,17 @@
<span class="role-badge">{{ msg.role }}</span>
<button
class="pin-btn active"
@click="togglePin(msg)"
:disabled="msg.role === 'System'"
title="取消钉住"
@click="togglePin(msg)"
>
<span v-if="msg.role === 'System'">🔒 系统信息固定在这</span>
<span v-else>📌 取消钉住</span>
</button>
</div>
<div class="card-content">{{ msg.content }}</div>
<div class="card-content">
{{ msg.content }}
</div>
</div>
</transition-group>
</div>
@@ -85,14 +89,23 @@
>
<div class="card-header">
<span class="role-badge">{{ msg.role }}</span>
<button class="pin-btn" @click="togglePin(msg)" title="把这条钉在黑板上">
<button
class="pin-btn"
title="把这条钉在黑板上"
@click="togglePin(msg)"
>
📌 钉住这条
</button>
</div>
<div class="card-content">{{ msg.content }}</div>
<div class="card-content">
{{ msg.content }}
</div>
</div>
</transition-group>
<div v-if="scrollingMessages.length === 0" class="empty-state">
<div
v-if="scrollingMessages.length === 0"
class="empty-state"
>
这里是普通对话区暂时还空着
</div>
</div>
@@ -103,16 +116,30 @@
<div class="input-group">
<input
v-model="newMessage"
@keyup.enter="sendMessage"
placeholder="在这里输入一条新的信息,比如“我叫小明”"
/>
<button class="send-btn" @click="sendMessage" :disabled="!newMessage.trim()">
@keyup.enter="sendMessage"
>
<button
class="send-btn"
:disabled="!newMessage.trim()"
@click="sendMessage"
>
添加到黑板
</button>
</div>
<div class="presets">
<button class="preset-btn" @click="addPreset('我的名字叫 Alice。')">用户我的名字叫 Alice</button>
<button class="preset-btn" @click="addPreset('系统密码是 1234。')">用户系统密码是 1234</button>
<button
class="preset-btn"
@click="addPreset('我的名字叫 Alice。')"
>
用户我的名字叫 Alice
</button>
<button
class="preset-btn"
@click="addPreset('系统密码是 1234。')"
>
用户系统密码是 1234
</button>
</div>
</div>
@@ -19,10 +19,17 @@
<span class="value">最多 {{ windowSize }} </span>
</div>
<div class="actions">
<button class="action-btn" @click="autoPlay" :disabled="isAutoPlaying">
<button
class="action-btn"
:disabled="isAutoPlaying"
@click="autoPlay"
>
自动演示
</button>
<button class="action-btn outline" @click="reset">
<button
class="action-btn outline"
@click="reset"
>
重新开始
</button>
</div>
@@ -42,14 +49,23 @@
class="message-bubble history"
:class="msg.role.toLowerCase()"
>
<div class="avatar">{{ msg.role === 'User' ? '👤' : '🤖' }}</div>
<div class="avatar">
{{ msg.role === 'User' ? '👤' : '🤖' }}
</div>
<div class="content">
<div class="role-name">{{ msg.role }}</div>
<div class="text">{{ msg.content }}</div>
<div class="role-name">
{{ msg.role }}
</div>
<div class="text">
{{ msg.content }}
</div>
</div>
</div>
</transition-group>
<div v-if="historyMessages.length === 0" class="empty-placeholder">
<div
v-if="historyMessages.length === 0"
class="empty-placeholder"
>
这里暂时还没有被挤出去的对话
</div>
</div>
@@ -57,7 +73,7 @@
<!-- Divider -->
<div class="window-divider">
<span> 窗口外模型已经看不到</span>
<div class="divider-line"></div>
<div class="divider-line" />
<span> 窗口内模型还能看到</span>
</div>
@@ -73,14 +89,23 @@
class="message-bubble active"
:class="msg.role.toLowerCase()"
>
<div class="avatar">{{ msg.role === 'User' ? '👤' : '🤖' }}</div>
<div class="avatar">
{{ msg.role === 'User' ? '👤' : '🤖' }}
</div>
<div class="content">
<div class="role-name">{{ msg.role }}</div>
<div class="text">{{ msg.content }}</div>
<div class="role-name">
{{ msg.role }}
</div>
<div class="text">
{{ msg.content }}
</div>
</div>
</div>
</transition-group>
<div v-if="activeMessages.length === 0" class="empty-placeholder">
<div
v-if="activeMessages.length === 0"
class="empty-placeholder"
>
从这里开始聊天看看旧对话是怎么被挤出去
</div>
</div>
@@ -90,11 +115,15 @@
<div class="input-section">
<input
v-model="newMessage"
@keyup.enter="sendMessage"
placeholder="在这里输入一条消息,然后点发送"
:disabled="isAutoPlaying"
/>
<button class="send-btn" @click="sendMessage" :disabled="!newMessage.trim() || isAutoPlaying">
@keyup.enter="sendMessage"
>
<button
class="send-btn"
:disabled="!newMessage.trim() || isAutoPlaying"
@click="sendMessage"
>
发送消息
</button>
</div>