feat: publish long-context deep dive

This commit is contained in:
wuyang
2026-07-28 22:49:04 +08:00
parent 8dc62bfaec
commit 761a75d5af
13 changed files with 2788 additions and 24 deletions
+981
View File
@@ -0,0 +1,981 @@
---
const strategies = [
{
id: "mha",
label: "Full MHA",
kicker: "全连接 · 全缓存",
summary: "每个 Query 精确比较全部历史 Token;每层为每个历史 Token 保存所有 KV 头。",
solves: "表达力基线",
leaves: "二次计算 + 最宽 KV",
},
{
id: "flash",
label: "Flash",
kicker: "全连接 · IO 优化",
summary: "连接关系与 Full MHA 完全相同,只是不再把完整注意力矩阵写进 HBM。",
solves: "显存读写与中间矩阵",
leaves: "理论 FLOPs 仍为二次",
},
{
id: "gqa",
label: "GQA",
kicker: "全连接 · 共享 KV",
summary: "Query 头仍可很多,但一组 Query 共享一组 K/V;历史条目不减少,缓存宽度变窄。",
solves: "KV 头数",
leaves: "序列仍逐 Token 增长",
},
{
id: "mla",
label: "MLA",
kicker: "全连接 · 潜变量 KV",
summary: "每个历史 Token 只缓存低维联合 latent;通过权重吸收避免在解码时展开完整多头 K/V。",
solves: "每 Token KV 宽度",
leaves: "全局两两计算仍在",
},
{
id: "dsa",
label: "DSA",
kicker: "索引 · Top-k 稀疏",
summary: "轻量 Indexer 从全部 latent KV 中选出少量候选,再让主注意力只读这些候选。",
solves: "主注意力连接数",
leaves: "仍需索引和保存历史 latent",
},
{
id: "kda",
label: "KDA",
kicker: "递推 · 固定状态",
summary: "历史被持续写入固定矩阵状态;Delta Rule 擦除旧关联,通道 gate 控制遗忘。",
solves: "长度相关 KV 与二次配对",
leaves: "有限状态容量",
},
{
id: "k3",
label: "K3 Hybrid",
kicker: "3×KDA + 1×MLA",
summary: "三层用固定状态做廉价混合,每四层用一次 NoPE MLA 重新获得全局内容寻址。",
solves: "效率与全局表达折中",
leaves: "MLA 层仍有增长缓存",
},
{
id: "v4",
label: "V4 Hybrid",
kicker: "CSA ↔ HCA",
summary: "CSA 先 4:1 压缩再 Top-kHCA 128:1 重压缩后读全部,并都保留局部滑窗。",
solves: "序列维缓存与连接数",
leaves: "压缩有损且系统复杂",
},
];
const size = 20;
const matrix = Array.from({ length: size * size }, (_, index) => ({
row: Math.floor(index / size),
col: index % size,
}));
const contexts = [
{ label: "4K", value: 4096 },
{ label: "32K", value: 32768 },
{ label: "128K", value: 131072 },
{ label: "1M", value: 1048576 },
];
---
<section class="context-lab" data-context-lab>
<div class="lab-head">
<div>
<p class="lab-kicker">INTERACTIVE / ATTENTION STRATEGY LAB</p>
<h3>“支持长上下文”到底改了哪一笔账?</h3>
</div>
<p>
上半区看信息连接,下半区看缓存。切换方法时,注意“连接变少”和“缓存变窄”不是一回事。
</p>
</div>
<div class="strategy-tabs" role="tablist" aria-label="选择注意力策略">
{strategies.map((strategy, index) => (
<button
type="button"
role="tab"
data-strategy-button={strategy.id}
aria-selected={index === 0 ? "true" : "false"}
>
<b>{strategy.label}</b>
<span>{strategy.kicker}</span>
</button>
))}
</div>
<div class="lab-stage">
<div class="pattern-panel">
<div class="panel-label">
<span>01 / INFORMATION ACCESS</span>
<b id="pattern-title">FULL MHA · 因果全连接</b>
</div>
<div class="matrix-wrap">
<div class="matrix-axis y"><span>QUERY</span></div>
<div class="attention-matrix" aria-label="简化的因果注意力连接矩阵">
{matrix.map(({ row, col }) => (
<i data-cell data-row={row} data-col={col} aria-hidden="true"></i>
))}
<div class="state-memory" data-state-memory>
<span>FIXED STATE</span>
<b>S<sub>t</sub></b>
<small>历史被压进固定矩阵,不再保留逐 Token 地址</small>
</div>
</div>
<div class="matrix-axis x"><span>HISTORICAL KEY / VALUE</span></div>
</div>
<label class="query-control">
<span>观察第几个 Query</span>
<input data-query type="range" min="0" max={size - 1} value={size - 1} />
<output data-query-output>{size}</output>
</label>
<div class="pattern-legend">
<div><i class="strong"></i><span>主算子直接读取</span></div>
<div><i class="soft"></i><span>压缩/全局补充路径</span></div>
<div><i class="local"></i><span>局部窗口</span></div>
</div>
</div>
<div class="strategy-story">
{strategies.map((strategy, index) => (
<article data-strategy-story={strategy.id} hidden={index !== 0}>
<span>{strategy.kicker}</span>
<h3>{strategy.label}</h3>
<p>{strategy.summary}</p>
<dl>
<div><dt>主要解决</dt><dd>{strategy.solves}</dd></div>
<div><dt>仍然留下</dt><dd>{strategy.leaves}</dd></div>
</dl>
</article>
))}
<div class="matrix-note">
<b>怎样读这张图</b>
<p>
横轴是可被读取的历史,纵轴是当前 Query。图只画 20 个位置,展示的是连接模式,不代表论文真实头数或窗口大小。
</p>
</div>
</div>
</div>
<div class="budget-panel">
<div class="budget-controls">
<div>
<p class="panel-kicker">02 / CACHE BUDGET</p>
<h3>把上下文拖到一百万 Token</h3>
<p>
统一教学模型:64 层、64 个 Query 头、head dim 128、BF16、GQA 8 个 KV 头、MLA latent 576。
数字用于比较增长规律,不是任何具体模型的线上显存报告。
</p>
</div>
<div class="context-control">
<div class="context-readout">
<span>CONTEXT LENGTH</span>
<strong data-context-label>4K</strong>
<small data-context-exact>4,096 tokens</small>
</div>
<input data-context type="range" min="0" max={contexts.length - 1} value="0" step="1" />
<div class="range-labels">
{contexts.map((context) => <span>{context.label}</span>)}
</div>
</div>
</div>
<div class="budget-summary">
<article>
<span>当前策略估算缓存</span>
<b data-cache-value>8.00 GB</b>
<small data-cache-note>逐 Token、逐层、全头 K/V</small>
</article>
<article>
<span>每 Query 直接读取</span>
<b data-access-value>4,096</b>
<small data-access-note>全部历史位置</small>
</article>
<article>
<span>随长度增长</span>
<b data-growth-value>线性 KV</b>
<small data-growth-note>注意力计算仍是二次</small>
</article>
</div>
<div class="budget-bars" aria-label="不同注意力机制的教学缓存估算">
{strategies.map((strategy) => (
<div data-budget-row={strategy.id}>
<span>{strategy.label}</span>
<div><i data-budget-bar={strategy.id}></i></div>
<b data-budget-value={strategy.id}>—</b>
</div>
))}
</div>
<div class="budget-caveat">
<span>ESTIMATE, NOT BENCHMARK</span>
<p>
KDA 行只计算固定递推状态;K3 行按 3:1 层比叠加 KDA 状态与 MLA latent
V4 行用 1:1 CSA/HCA、4×/128×压缩和 128 局部窗口做结构估算,未计 Indexer、未压缩尾部、分页和框架开销。
</p>
</div>
</div>
<script is:inline define:vars={{ contexts }}>
const root = document.querySelector("[data-context-lab]");
if (root) {
const buttons = [...root.querySelectorAll("[data-strategy-button]")];
const stories = [...root.querySelectorAll("[data-strategy-story]")];
const cells = [...root.querySelectorAll("[data-cell]")];
const queryInput = root.querySelector("[data-query]");
const queryOutput = root.querySelector("[data-query-output]");
const contextInput = root.querySelector("[data-context]");
const stateMemory = root.querySelector("[data-state-memory]");
let active = "mha";
const assumptions = {
layers: 64,
heads: 64,
kvHeads: 8,
headDim: 128,
latent: 576,
bytes: 2,
};
const formatBytes = (bytes) => {
const units = ["B", "KB", "MB", "GB", "TB"];
let value = bytes;
let unit = 0;
while (value >= 1024 && unit < units.length - 1) {
value /= 1024;
unit += 1;
}
const digits = value >= 100 ? 0 : value >= 10 ? 1 : 2;
return `${value.toFixed(digits)} ${units[unit]}`;
};
const formatCount = (value) => new Intl.NumberFormat("zh-CN").format(Math.round(value));
const cacheFor = (id, length) => {
const { layers, heads, kvHeads, headDim, latent, bytes } = assumptions;
const mha = 2 * length * layers * heads * headDim * bytes;
if (id === "mha" || id === "flash") return mha;
if (id === "gqa") return 2 * length * layers * kvHeads * headDim * bytes;
if (id === "mla" || id === "dsa") return length * layers * latent * bytes;
if (id === "kda") return layers * heads * headDim * headDim * bytes;
if (id === "k3") {
const kdaLayers = Math.round(layers * 0.75);
const mlaLayers = layers - kdaLayers;
const state = kdaLayers * heads * headDim * headDim * bytes;
const mlaCache = length * mlaLayers * latent * bytes;
return state + mlaCache;
}
if (id === "v4") {
const csaLayers = layers / 2;
const hcaLayers = layers / 2;
const window = Math.min(length, 128);
const csaEntries = Math.ceil(length / 4) + window;
const hcaEntries = Math.ceil(length / 128) + window;
return (csaLayers * csaEntries + hcaLayers * hcaEntries) * headDim * bytes;
}
return mha;
};
const accessFor = (id, length) => {
if (["mha", "flash", "gqa", "mla"].includes(id)) {
return { value: formatCount(length), note: "全部历史位置" };
}
if (id === "dsa") {
return { value: formatCount(Math.min(length, 2048)), note: "主注意力 Top-k;另有轻量索引" };
}
if (id === "kda") {
return { value: "固定状态", note: "不再逐 Token 内容寻址" };
}
if (id === "k3") {
return { value: "3×状态 + 1×全局", note: "沿深度周期性交替" };
}
const csa = Math.min(Math.ceil(length / 4), 1024) + Math.min(length, 128);
const hca = Math.ceil(length / 128) + Math.min(length, 128);
return { value: `${formatCount(csa)} / ${formatCount(hca)}`, note: "CSA / HCA 结构估算" };
};
const metaFor = (id) => {
const meta = {
mha: ["逐 Token、逐层、全头 K/V", "线性 KV", "注意力计算为二次"],
flash: ["缓存同 Full MHA;省的是中间矩阵", "线性 KV", "FLOPs 仍二次,HBM IO 更少"],
gqa: ["逐 Token、逐层、8 组 KV", "线性薄 KV", "连接数仍是全局"],
mla: ["逐 Token、逐层、576 维 latent", "线性 latent", "权重吸收不等于稀疏"],
dsa: ["保存全部 latent,主 attention 只读 Top-k", "线性 latent", "主连接约 O(Lk)"],
kda: ["每层每头一个 128×128 状态", "固定状态", "容量不随 L 增长"],
k3: ["3/4 固定状态 + 1/4 latent KV", "混合增长", "全局层缓存仍随 L 增长"],
v4: ["压缩 KV + 128 Token 局部窗口", "压缩线性", "CSA 稀疏,HCA 重压缩"],
};
return meta[id];
};
const updateMatrix = () => {
const query = Number(queryInput?.value ?? 19);
if (queryOutput) queryOutput.textContent = String(query + 1);
stateMemory?.toggleAttribute("data-visible", active === "kda" || active === "k3");
cells.forEach((cell) => {
const row = Number(cell.dataset.row);
const col = Number(cell.dataset.col);
const causal = col <= row;
const current = row === query;
let strength = "off";
if (causal && ["mha", "flash", "gqa", "mla"].includes(active)) strength = "strong";
if (causal && active === "dsa") {
const local = row - col <= 3;
const indexed = col === 0 || (col + row) % 5 === 1;
strength = local ? "local" : indexed ? "strong" : "off";
}
if (causal && active === "kda") strength = "state";
if (causal && active === "k3") {
const local = row - col <= 2;
strength = local ? "local" : "soft";
}
if (causal && active === "v4") {
const local = row - col <= 3;
const compressed = col % 4 === 0 || col === 0;
strength = local ? "local" : compressed ? "strong" : "soft";
}
cell.dataset.strength = strength;
cell.toggleAttribute("data-query-row", current);
});
const titles = {
mha: "FULL MHA · 因果全连接",
flash: "FLASH · 连接不变,只改变 IO",
gqa: "GQA · 全连接,共享 K/V",
mla: "MLA · 全连接,缓存潜变量",
dsa: "DSA · Indexer 选择稀疏历史",
kda: "KDA · 读取固定递推状态",
k3: "K3 · 固定状态与全局层交替",
v4: "V4 · 压缩、稀疏与局部窗口",
};
const title = root.querySelector("#pattern-title");
if (title) title.textContent = titles[active];
};
const updateBudget = () => {
const contextIndex = Number(contextInput?.value ?? 0);
const selected = contexts[contextIndex];
const length = selected.value;
const mhaCache = cacheFor("mha", length);
root.querySelector("[data-context-label]").textContent = selected.label;
root.querySelector("[data-context-exact]").textContent = `${formatCount(length)} tokens`;
const currentCache = cacheFor(active, length);
root.querySelector("[data-cache-value]").textContent = formatBytes(currentCache);
const access = accessFor(active, length);
root.querySelector("[data-access-value]").textContent = access.value;
root.querySelector("[data-access-note]").textContent = access.note;
const [cacheNote, growth, growthNote] = metaFor(active);
root.querySelector("[data-cache-note]").textContent = cacheNote;
root.querySelector("[data-growth-value]").textContent = growth;
root.querySelector("[data-growth-note]").textContent = growthNote;
buttons.forEach((button) => {
const id = button.dataset.strategyButton;
const bytes = cacheFor(id, length);
const ratio = Math.max(0.012, Math.min(1, bytes / mhaCache));
const bar = root.querySelector(`[data-budget-bar="${id}"]`);
const value = root.querySelector(`[data-budget-value="${id}"]`);
if (bar) bar.style.width = `${ratio * 100}%`;
if (value) value.textContent = formatBytes(bytes);
root.querySelector(`[data-budget-row="${id}"]`)?.toggleAttribute("data-active", id === active);
});
};
const select = (id) => {
active = id;
buttons.forEach((button) => button.setAttribute("aria-selected", String(button.dataset.strategyButton === id)));
stories.forEach((story) => {
story.hidden = story.dataset.strategyStory !== id;
});
updateMatrix();
updateBudget();
};
buttons.forEach((button) => button.addEventListener("click", () => select(button.dataset.strategyButton)));
queryInput?.addEventListener("input", updateMatrix);
contextInput?.addEventListener("input", updateBudget);
select(active);
}
</script>
</section>
<style>
.context-lab {
margin-block: 56px;
border: 1px solid var(--line-strong);
background: var(--paper-raised);
}
.lab-head {
display: grid;
grid-template-columns: 1.25fr 0.75fr;
gap: 60px;
align-items: end;
padding: clamp(28px, 4vw, 52px);
border-bottom: 1px solid var(--line);
}
.lab-kicker,
.panel-kicker {
color: var(--copper);
font: 0.62rem/1 var(--mono);
letter-spacing: 0.13em;
}
.lab-head h3 {
margin-top: 15px;
font-size: clamp(1.5rem, 2.5vw, 2.5rem);
}
.lab-head > p {
color: var(--muted);
font-family: var(--serif);
font-size: 0.96rem;
}
.strategy-tabs {
display: grid;
grid-template-columns: repeat(8, 1fr);
border-bottom: 1px solid var(--line);
}
.strategy-tabs button {
display: grid;
gap: 8px;
min-width: 0;
min-height: 78px;
padding: 16px 12px;
border: 0;
border-right: 1px solid var(--line);
background: transparent;
color: var(--muted);
cursor: pointer;
text-align: left;
}
.strategy-tabs button:last-child {
border-right: 0;
}
.strategy-tabs button:hover,
.strategy-tabs button[aria-selected="true"] {
background: var(--ink);
color: var(--paper-raised);
}
.strategy-tabs b {
font-size: 0.78rem;
}
.strategy-tabs span {
font-size: 0.58rem;
line-height: 1.35;
}
.lab-stage {
display: grid;
grid-template-columns: minmax(0, 1.55fr) minmax(260px, 0.45fr);
}
.pattern-panel {
padding: clamp(26px, 4vw, 48px);
border-right: 1px solid var(--line);
}
.panel-label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 30px;
}
.panel-label span {
color: var(--muted);
font: 0.58rem/1 var(--mono);
letter-spacing: 0.1em;
}
.panel-label b {
font: 0.68rem/1 var(--mono);
}
.matrix-wrap {
display: grid;
grid-template-columns: 25px minmax(0, 560px);
grid-template-rows: auto 25px;
gap: 9px;
justify-content: center;
}
.attention-matrix {
position: relative;
display: grid;
grid-template-columns: repeat(20, 1fr);
grid-template-rows: repeat(20, 1fr);
width: min(100%, 560px);
aspect-ratio: 1;
padding: 8px;
border: 1px solid var(--line-strong);
background: var(--paper);
}
[data-cell] {
margin: 1px;
border-radius: 1px;
background: rgba(39, 54, 74, 0.035);
transition: background 150ms ease, opacity 150ms ease, transform 150ms ease;
}
[data-cell][data-strength="strong"] {
background: var(--ink);
opacity: 0.52;
}
[data-cell][data-strength="soft"] {
background: var(--violet);
opacity: 0.18;
}
[data-cell][data-strength="local"] {
background: var(--copper);
opacity: 0.7;
}
[data-cell][data-strength="state"] {
background: var(--sage);
opacity: 0.08;
}
[data-cell][data-query-row] {
transform: scale(0.82);
opacity: 1;
}
.state-memory {
position: absolute;
inset: 23% 16%;
display: none;
place-content: center;
padding: 22px;
border: 1px solid rgba(85, 122, 114, 0.46);
background: color-mix(in srgb, var(--sage-pale) 92%, transparent);
text-align: center;
}
.state-memory[data-visible] {
display: grid;
}
.state-memory span {
color: var(--sage);
font: 0.56rem/1 var(--mono);
letter-spacing: 0.12em;
}
.state-memory b {
margin-top: 12px;
font-family: var(--serif);
font-size: clamp(2rem, 5vw, 4.5rem);
font-weight: 500;
}
.state-memory small {
max-width: 260px;
margin-top: 12px;
color: var(--muted);
font-size: 0.68rem;
}
.matrix-axis {
display: flex;
align-items: center;
justify-content: center;
color: var(--muted-light);
font: 0.5rem/1 var(--mono);
letter-spacing: 0.1em;
}
.matrix-axis.y span {
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.matrix-axis.x {
grid-column: 2;
}
.query-control {
display: grid;
grid-template-columns: auto 1fr 30px;
gap: 16px;
align-items: center;
max-width: 595px;
margin: 24px auto 0;
color: var(--muted);
font-size: 0.68rem;
}
.query-control input,
.context-control input {
width: 100%;
accent-color: var(--copper);
}
.query-control output {
font: 0.7rem/1 var(--mono);
text-align: right;
}
.pattern-legend {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
margin-top: 22px;
}
.pattern-legend div {
display: flex;
align-items: center;
gap: 7px;
color: var(--muted);
font-size: 0.62rem;
}
.pattern-legend i {
width: 10px;
height: 10px;
}
.pattern-legend .strong { background: var(--ink); opacity: 0.55; }
.pattern-legend .soft { background: var(--violet); opacity: 0.24; }
.pattern-legend .local { background: var(--copper); opacity: 0.7; }
.strategy-story {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: clamp(28px, 4vw, 46px);
background: var(--paper-deep);
}
.strategy-story article > span {
color: var(--copper);
font: 0.6rem/1 var(--mono);
letter-spacing: 0.1em;
}
.strategy-story article h3 {
margin-top: 18px;
font-family: var(--serif);
font-size: 2rem;
}
.strategy-story article p {
margin-top: 18px;
color: var(--muted);
font-family: var(--serif);
font-size: 0.93rem;
}
.strategy-story dl {
margin: 28px 0 0;
border-top: 1px solid var(--line);
}
.strategy-story dl div {
padding: 14px 0;
border-bottom: 1px solid var(--line);
}
.strategy-story dt {
color: var(--muted);
font: 0.58rem/1 var(--mono);
}
.strategy-story dd {
margin: 7px 0 0;
font-size: 0.78rem;
}
.matrix-note {
margin-top: 44px;
padding-top: 22px;
border-top: 1px solid var(--line-strong);
}
.matrix-note b {
font-size: 0.73rem;
}
.matrix-note p {
margin-top: 8px;
color: var(--muted);
font-size: 0.68rem;
}
.budget-panel {
border-top: 1px solid var(--line-strong);
}
.budget-controls {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 60px;
align-items: end;
padding: clamp(30px, 5vw, 60px);
}
.budget-controls h3 {
margin-top: 15px;
font-size: clamp(1.5rem, 2.5vw, 2.35rem);
}
.budget-controls > div:first-child > p:last-child {
max-width: 680px;
margin-top: 16px;
color: var(--muted);
font-size: 0.8rem;
}
.context-readout {
display: grid;
grid-template-columns: 1fr auto;
align-items: baseline;
padding-bottom: 16px;
border-bottom: 1px solid var(--line-strong);
}
.context-readout span {
color: var(--muted);
font: 0.58rem/1 var(--mono);
letter-spacing: 0.1em;
}
.context-readout strong {
font: 500 2.2rem/1 var(--serif);
}
.context-readout small {
grid-column: 1 / -1;
margin-top: 5px;
color: var(--muted);
font-size: 0.64rem;
text-align: right;
}
.context-control input {
margin-top: 20px;
}
.range-labels {
display: flex;
justify-content: space-between;
color: var(--muted);
font: 0.56rem/1 var(--mono);
}
.budget-summary {
display: grid;
grid-template-columns: repeat(3, 1fr);
border-block: 1px solid var(--line);
}
.budget-summary article {
min-height: 150px;
padding: 27px clamp(22px, 3vw, 40px);
border-right: 1px solid var(--line);
}
.budget-summary article:last-child {
border-right: 0;
}
.budget-summary span {
color: var(--muted);
font: 0.58rem/1 var(--mono);
}
.budget-summary b {
display: block;
margin-top: 22px;
font-family: var(--serif);
font-size: clamp(1.45rem, 2.3vw, 2.1rem);
font-weight: 500;
}
.budget-summary small {
display: block;
margin-top: 8px;
color: var(--muted);
font-size: 0.64rem;
}
.budget-bars {
padding: 34px clamp(24px, 5vw, 60px);
}
.budget-bars > div {
display: grid;
grid-template-columns: 110px 1fr 92px;
gap: 16px;
align-items: center;
min-height: 36px;
padding-inline: 8px;
}
.budget-bars > div[data-active] {
background: var(--paper-deep);
}
.budget-bars span,
.budget-bars b {
font-size: 0.67rem;
}
.budget-bars b {
font-family: var(--mono);
font-weight: 500;
text-align: right;
}
.budget-bars div > div {
height: 7px;
background: var(--paper-deep);
}
.budget-bars i {
display: block;
width: 0;
height: 100%;
background: var(--sage);
transition: width 260ms ease;
}
.budget-bars [data-active] i {
background: var(--copper);
}
.budget-caveat {
display: grid;
grid-template-columns: 190px 1fr;
gap: 34px;
padding: 24px clamp(24px, 5vw, 60px);
border-top: 1px solid var(--line);
background: var(--ink);
color: var(--paper-raised);
}
.budget-caveat span {
color: var(--copper-pale);
font: 0.58rem/1.4 var(--mono);
letter-spacing: 0.1em;
}
.budget-caveat p {
color: rgba(250, 248, 243, 0.72);
font-size: 0.67rem;
line-height: 1.7;
}
@media (max-width: 980px) {
.strategy-tabs {
grid-template-columns: repeat(4, 1fr);
}
.strategy-tabs button:nth-child(4) {
border-right: 0;
}
.strategy-tabs button:nth-child(-n + 4) {
border-bottom: 1px solid var(--line);
}
.lab-stage,
.budget-controls {
grid-template-columns: 1fr;
}
.pattern-panel {
border-right: 0;
border-bottom: 1px solid var(--line);
}
}
@media (max-width: 680px) {
.lab-head {
grid-template-columns: 1fr;
gap: 20px;
}
.strategy-tabs {
grid-template-columns: repeat(2, 1fr);
}
.strategy-tabs button:nth-child(2n) {
border-right: 0;
}
.strategy-tabs button:nth-child(n) {
border-bottom: 1px solid var(--line);
}
.panel-label {
align-items: flex-start;
flex-direction: column;
}
.pattern-panel {
padding-inline: 16px;
}
.attention-matrix {
padding: 4px;
}
[data-cell] {
margin: 0.5px;
}
.query-control {
grid-template-columns: 1fr 30px;
}
.query-control span {
grid-column: 1 / -1;
}
.budget-summary {
grid-template-columns: 1fr;
}
.budget-summary article {
min-height: 130px;
border-right: 0;
border-bottom: 1px solid var(--line);
}
.budget-bars > div {
grid-template-columns: 82px 1fr 72px;
gap: 9px;
}
.budget-caveat {
grid-template-columns: 1fr;
gap: 10px;
}
}
</style>
+1
View File
@@ -5,6 +5,7 @@
</div>
<div class="footer-links">
<a href="/roadmap/">学习地图</a>
<a href="/long-context/">长上下文专题</a>
<a href="/progress/">研究进度</a>
<a href="https://git.k1412.top/wuyang/llm-atlas" rel="noreferrer">开放源码</a>
<a href="https://github.com/MoonshotAI/Kimi-K3" rel="noreferrer">K3 官方报告</a>
+1
View File
@@ -10,6 +10,7 @@ const items = [
{ id: "k3", href: "/k3/", label: "K3 解剖" },
{ id: "deepseek", href: "/deepseek/", label: "DeepSeek" },
{ id: "foundations", href: "/foundations/", label: "基础原理" },
{ id: "long-context", href: "/long-context/", label: "长上下文" },
{ id: "papers", href: "/papers/", label: "论文库" },
{ id: "progress", href: "/progress/", label: "进度" },
];