arXiv preprint 2026.09 技术报告 MoE · 长上下文 · KV 缓存

DeepSeek-V4.1-Flash:为什么读输入只激活 8B,写输出要 16B

答案是一个结构改动:Causal Encoder-Decoder(CED)。它让 prompt token 只走前一半网络,后一半网络需要的全局 KV 直接从中间层投影出来。围绕这个改动,这一代还把每 token 的 KV 缓存压到了前代的 1/4。

DeepSeek-AI · 素材:技术报告、Hugging Face 模型卡与参考推理代码

30 秒读懂

V4.1-Flash 是一个 552B 主干参数的多模态 MoE(每层 1 个共享专家 + 384 个路由专家,每 token 选 6 个),共 40 层。最反直觉的数字是 8B / 16B:处理输入(prefill)时每个 token 只激活 8B,生成输出(decode)时激活 16B。

差别不在专家路由,而在token 走过的层数。CED 把 40 层切成 20 层因果 encoder + 20 层 decoder,并让 decoder 的全局 KV 直接由 encoder 最后一层的 hidden state 投影得到。于是输入 token 跑完前 20 层,就已经把后 20 层要用的全局 KV 留好了;只有生成下一个 token 时才必须走完 40 层。

代价是:decoder 每层仍保留自己的滑窗注意力(SWA),prefill 末尾要把最后 128 个 token 再过一遍 decoder,做近似重建(SWA Bounded Replay)。再配上跨层共享 KV 的 CSA2 和 FP4 缓存,全局 KV 降到 890 字节/token(V4-Flash 的约 1/4),持久化 KV 降到约 1/8。

这篇报告究竟要回答什么问题?
在输入远多于输出的 agent 负载下,能否让「读输入」只付一半的模型计算,同时把长上下文 KV 缓存再压到前代的 1/4,而且不掉能力?
8B / 16B
每 token 激活参数
prefill / decode
552B
主干参数
另有 196B Engram 查表参数
890 B
每 token 全局 KV
V4-Flash 为 3,514 B
≈1/8
持久化 KV 缓存
相对 V4-Flash
01 · 问题

普通 decoder-only 模型,为什么输入和输出一样贵?

先对齐口径。激活参数指一个 token 前向时真正参与矩阵乘法的参数:每层的 attention 投影 + 1 个共享专家 + 路由选中的 6 个专家,再乘以它经过的层数。V4.1-Flash 在 prefill 和 decode 下的路由规则完全相同(都是 top-6),所以 8B 与 16B 的差别只能来自 token 经过的层数。Engram 的 196B 是按 n-gram 哈希查的记忆表,像 embedding 一样只做查表,不计入激活参数。

那么,普通模型里输入 token 为什么必须走完所有层?看看两类 token 在一次请求里各自要做什么:

输出 token 的任务

给出下一个 token 的预测

预测来自最后一层的 hidden state 经过输出头,所以必须走完全部层,无可省略。

输入 token 的任务

在每一层给后面的 token 留下 KV

除最后一个外,输入 token 的预测根本没人用。它存在的意义只是让后续 token 在每层的 attention 里能读到它。

问题在于,标准结构里第 l 层的 KV 由第 l 层自己的 hidden state 投影而来(KV_l = H_l · W_l)。想在第 40 层留下 KV,就得先把这个 token 一路算到第 40 层。于是「留 KV」和「做预测」一样贵,前代 V4-Flash 的 13B 激活对输入、输出一视同仁。

agent 负载把这个问题放大了:

报告的判断是:稀疏注意力已经把长序列的注意力计算压得很低,现在剩下的瓶颈是 prefill 本身的计算量,以及 KV 的存储和搬运。V4.1-Flash 的结构改动分别针对这两项。

03 · 核心答案

CED:让 decoder 的全局 KV 只依赖 encoder 的输出

CED 只改了一件事:decoder 各层全局注意力的 KV 从哪里来。先看全局结构,再逐步拆开。

DeepSeek-V4.1-Flash 整体架构:左侧 20 层 Causal Encoder,右侧 20 层 Decoder,encoder 隐状态经 CED 连到 decoder 的 Full 层
原论文 Figure 3:40 层被切成左侧 20 层因果 encoder 与右侧 20 层 decoder;encoder 最后的隐状态经标注 CED 的连线,进入 decoder 唯一的 Full 层生成全局 KV。
  • 左框 Causal Encoder(第 1–20 层):底部的文本 / 视觉 embedding 经 Single-Pass mHC 展开为 4 路残差流。前 2 层(×2)只有 SWA + MoE;之后 3 组(×3),每组 1 个 CSA2(2, Full) + 5 个 CSA2(2, Reuse)。括号里的 2 是压缩比:每 2 个 token 合成一个全局 KV 条目。
  • Engram(左下):两处箭头接入 encoder。报告把它放在第 1、14 层(从 0 计数),即第 2 个 SWA 层和第 3 组的 Full 层,按 token 的 n-gram 查表,把记忆向量加进残差流。
  • Enc. Hidden States → 右框底部:一条线作为 decoder 的输入残差;另一条标着 CED 的线进入 CSA2(1, Full)。这就是「decoder 全局 KV 由 H_20 投影」。
  • 右框 Decoder(第 21–40 层):第一组是 1 个 CSA2(1, Full) + 3 个 Reuse;之后 4 组(×4),每组 1 个 CSA2(1, Reindex) + 3 个 Reuse。压缩比 1 即不压缩。
  • Hierarchical Sparse Indexer → Candidate Pool:Full 层打分时顺带圈出一个候选池,回送给每个 Reindex 层,让它们只在池内重选 Top-K。
  • 顶部 DSpark:投机解码的 drafter,读取主干 hidden 生成草稿 token,取代了 V3 / V4 的 MTP。
  • 一句话:prefill 时,除最后 128 个 token 外,数据流在左框顶部就结束了;右框只在 decode(以及末尾 replay)时运行。

用公式写出来,区别只在 decoder 那一行:

标准 decoder-only
KV_l = H_l · W_l^KV  l = 1 … 40,每层用本层 hidden
CED · encoder 层
KV_l = H_l · W_l^KV  l = 1 … 20,照旧
CED · decoder 层
C_l = H_20 · W_l^KV, Z_l = H_20 · W_l^Z  l = 21 … 40

C_l 是全局 KV 条目,Z_l 是对应的压缩权重(CSA2 用它把相邻 m 个 token 加权合成一个条目;decoder 的 m = 1)。关键在于右边只出现 H_20:一个 token 只要算到第 20 层,它在第 21–40 层的全局 KV 就能直接投影出来。结合 CSA2 的配置,decoder 里只有第 21 层是 Full 层,真正执行这次投影,其余 19 层都复用它,所以 decoder 实际只存一份全局 KV。

三个容易误读的点:

走一遍:一轮 agent 交互在 CED 里怎么流动

设想一个 coding agent:上下文里已经缓存了约 200K token(系统提示、仓库文件、历史对话);这一轮 pytest 返回了 4,000 token 的报错日志,模型要据此写出约 400 token 的分析和下一步工具调用。逐步点「下一步」,再切到对照模式,看看同样 40 层但不用 CED 时有什么不同。

每列代表一段 token,每格代表这段 token 在这一部分网络上的计算
已计算 近似重放(只补 SWA KV) 已在缓存,本轮不算 跳过,不计算 描边 = 本步新发生
—每个输入 token 激活
—每个输出 token 激活
—本轮 prefill 的「token × 层」(相对不用 CED)

8B 和 16B 的账是怎么算出来的

报告没有给出参数拆分,但用 §4.2.1 的超参(hidden 5120,每个专家中间维 2304,64 个 query head × 512 维等)可以粗算,结果和官方数字吻合:

项目计算约值
单个专家(SwiGLU 三个矩阵)3 × 5120 × 230435.4M
每层激活的 MoE(1 共享 + 6 路由) × 35.4M0.25B
每层 attention 投影Q 低秩投影 + KV + 分组输出投影 + indexer0.13B
每层激活合计≈ 0.38B
prefill:每个输入 token20 层 × 0.38B≈ 7.6B → 8B
decode:每个输出 token40 层 × 0.38B + 输出头 0.66B≈ 15.8B → 16B
校验:主干总参数40 × (384 × 35.4M + 0.17B) + 词表矩阵 2 × 0.66B≈ 552B ✓

结论很直接:8B / 16B 的差异就是 20 层与 40 层的差异,路由、专家数、每层宽度在两个阶段完全相同。prefill 时 decoder 唯一多出的工作,是第 21 层把 H_20 投影成全局 KV(不到 0.01B),以及末尾 128 个 token 的 replay。

attention 投影 0.13B 的拆分按报告超参逐项相乘;norm、router、mHC 系数等小项未计
投影形状参数
Q 压缩(hidden → query latent)5120 × 12806.6M
Q 展开(latent → 64 head × 512)1280 × 3276841.9M
KV(hidden → 512 维共享 latent)5120 × 5122.6M
分组输出投影 A(8 组,每组 4096 → 1024)8 × 4096 × 102433.6M
输出投影 B(8192 → hidden)8192 × 512041.9M
indexer Q(latent → 32 head × 128)1280 × 40965.2M
合计≈ 132M

词表约 129K(参考代码中图像 token id 为 129264),词表矩阵 ≈ 129K × 5120 ≈ 0.66B。是否把输入 embedding 计入激活参数会让结果差 0.66B 左右,不影响 20 层对 40 层的比例。

「接近减半」有个前提:新输入要足够长

报告给出的 prefill 复杂度是从 O(N·L) 降到 O(N·L/2 + n_win·L/2),其中 N 是本轮新输入的 token 数,n_win = 128 是滑窗大小。第二项就是末尾 replay 那 128 个 token 过 decoder 的固定开销,所以相对不用 CED 的比例约为 1/2 + 64/N:

本轮新输入 N1281,0004,00032,000≥ 128K
prefill「token × 层」相对不用 CED≈ 100%≈ 56%≈ 52%≈ 50.2%≈ 50%

按报告公式推算,只计 Transformer 层的 token 数,不含注意力随上下文增长的部分;若前缀的 encoder SWA KV 已过期,还要多重放 128 个 token 的 encoder。

这也解释了为什么 replay 必须有界。decoder 的 SWA 依赖逐层叠加:第 40 层看前 128 个位置的第 39 层状态,而这些状态又各自看前 128 个位置的第 38 层……精确重建需要 20 × 128 = 2,560 个 token 过 decoder。多轮对话里每轮新输入常常只有几百 token,这笔固定开销会吃掉大部分收益。报告引用 PowerAttention 的观察:SWA 的有效感受野远小于理论值,于是只重放最后 128 个 token、把 SWA 截断在重放段内,接受近似结果,并在 post-training 中模拟同样的 replay 做训练适配。

04 · 配套改动

把 KV 再压到 1/4:CSA2、FP4 与有界重放

CED 省下的是 prefill 计算;报告标题里的「KV 缓存压缩」主要靠另外三项。KV 的开销可以沿三个相乘的维度压缩:每个条目的大小(GQA、MLA、量化)、序列维(每 m 个 token 合成一个条目)、层维(多层共用一份)。V4 主要压了前两维,V4.1 补上了层维,并把条目大小再减半。

CSA2:40 层里只有 4 层生产全局 KV

CSA2 保留了 CSA 的骨架(压缩 KV + indexer 选 Top-K + SWA 分支),但给每一层静态指定三种模式之一:

三种模式下,每层都保留自己的 query 和 SWA KV,所以每层的注意力输出仍然不同。共享 KV 省的是存储,复用索引省的是 indexer 计算,两者解耦。

CSA2 三种模式:Full、Reindex、Reuse 分别本层计算或复用 Main KV、Indexer K 与 Top-K 索引
原论文 Figure 4:三种模式的差别只在主 KV、indexer K 和 Top-K 索引是本层算的还是复用的。
  • 共同骨架:三个面板里,Main Q 和 SWA KV 都由本层计算(白色块)。选出的 Selected Main KV 与 SWA KV 拼接(Concatenation)后,和 Main Q 一起做 Core Attention。
  • 绿色 = 本层计算:Full Mode 自己算 Main KV、Indexer K、Indexer Q,由 Indexer 打分得到 Top-K Indices,再去 Selection。
  • 黄色 = 从最近的 Full 层复用的主 KV 和 indexer K:Reindex Mode 只算自己的 Indexer Q(绿色),对共享的 Indexer K 重新打分,得到新的 Top-K。
  • 红色 = 从最近一个产生索引的层(Full 或 Reindex)复用的 Top-K:Reuse Mode 不跑 indexer,直接按别人的选择从共享 KV 里取条目。
  • 一句话:越往右,本层要算、要存的东西越少;但三者的注意力输出都由本层的 query 和 SWA KV 决定,层与层之间并不完全相同。

V4.1-Flash 的具体排布如下。把它和 CED 叠在一起看,就能看出哪些层生产 KV、prefill 在哪里停:

相对 V4 的 CSA,CSA2 还做了两处简化:压缩器去掉了相邻条目的重叠窗口和绝对位置编码(CSA 用 2m 个原始条目生成一个压缩条目,相邻条目有重叠);indexer K 直接由主 KV 投影,不再从 hidden 单独压缩一路。另外,V4.1 全部使用 CSA2,不再混用 HCA。

Hierarchical Sparse Indexer:decoder 里后面的 indexer 只在候选池里找第 21 层圈出最多 16,384 个候选位置,后续 Reindex 层的打分量不再随上下文长度增长
Hierarchical Sparse Indexer:Full 层打分后选出候选块形成共享候选池,后续 Reindex 层只在池中选 Top-512
原论文 Figure 5:第一个 Full 层全量打分并圈出候选池,后续 Reindex 层只在池内各选 Top-512。
  • 左:Full Mode Indexer:对所有因果可见的位置打分,选出自己的 Top-512(绿色格);同时把每个块的得分记为块内最高分,挑出得分最高的若干块(蓝色条)。
  • 中:Shared Candidate Pool:被选中块覆盖的全部位置组成候选池。配置为最多 2,048 块 × 每块 8 个位置 = 16,384 个候选,比最终的 Top-512 大得多。
  • 右:Reindex Mode Indexer:后续每个 Reindex 层只对候选池里的位置打分,各自选 Top-512。搜索范围相同,最终选择可以不同。
  • 底部长箭头:同一个候选池被所有 Reindex 层共享,不新增任何缓存状态。
  • 一句话:除第一个 Full 层外,indexer 的每 query 打分量与上下文长度无关。该机制在 post-training 引入,训练和推理用同样的候选限制。

FP4 主 KV 缓存

V4 已经用量化感知训练(QAT)把 indexer 的 Q、K 做成 FP4;V4.1 在 post-training 中把 QAT 扩展到主 KV 缓存:

相比 V4 的 FP8 主 KV,存储几乎减半,HBM 和 SSD 上都一样。CSA2 跨层共享叠加 FP4,正好把每 token 全局 KV 算到 890 字节:

890 字节/token 是怎么来的按报告配置推算:4 份全局 KV(encoder 3 份压缩比 2,decoder 1 份不压缩),每份含主 KV 与 indexer K
组成每个条目每 token 条目数字节 / token
encoder 主 KV(3 个 Full 层,压缩比 2)512 维 × FP4 = 256 B + 32 个 E4M3 缩放 = 288 B3 × 0.5 = 1.5432
encoder indexer K128 维 × FP4 = 64 B + 4 个缩放(每 32 维一个)= 68 B1.5102
decoder 主 KV(1 个 Full 层,压缩比 1)288 B1288
decoder indexer K68 B168
合计890

indexer 的分组大小(32 维、E8M0 缩放)取自参考推理代码中的注释;推算结果与报告的 890 字节完全一致。SWA KV 有窗口上限、不随长度增长,不计入全局 KV。

四代 DeepSeek 模型每 token 全局 KV 字节数:V1 389120、V3.2 48068、V4-Flash 3514、V4.1-Flash 890
原论文 Figure 1(b):每 token 全局 KV 字节数,从 V4-Flash 的 3,514 降到 V4.1-Flash 的 890(3.9×)。
  • 纵向四代模型,横条长度是每 token 全局 KV 的字节数,也就是常驻 HBM、随上下文线性增长的那部分。
  • V1 → V3.2(2023.11 → 2025.12):389,120 → 48,068,主要来自 MLA 的共享 latent,缩小 8.1×。
  • V3.2 → V4-Flash(2026.04):→ 3,514,CSA / HCA 沿序列维压缩,缩小 13.7×。
  • V4-Flash → V4.1-Flash(2026.09):→ 890,CSA2 沿层维共享加 FP4,缩小 3.9×;相对 V1 累计约 437×。
  • 注意口径:图中只统计全局 KV;SWA KV 有窗口上限,不在此列,它的持久化问题由下一节的有界重放解决。

SWA Bounded Replay:持久化缓存里不再存 SWA KV

有界重放在 CED 那里用来省 decoder 的 prefill;在部署层面,同一个技巧还解决了 V4 遗留的持久化缓存问题。SWA KV 的访问模式和持久化缓存不匹配:全局 KV 有长尾复用(几天后同一前缀还可能被命中),SWA KV 只在一个活跃会话的分钟级窗口内有用,下一轮开始就作废了。

V4 的持久化缓存
  • SSD 上同时存全局 KV(完整前缀)和 SWA KV(在 prompt 结尾、输出结尾各存 128 个条目)
  • SWA KV 占了近一半容量,短轮次的多轮对话里尤其浪费
  • 不存 SWA 的方案(Zero SWA Caching)需要重放 L × 128 个 token,生产上太贵
V4.1 的做法
  • 持久化缓存只存全局 KV,保证至少 72 小时
  • SWA KV 放进每台机器 10% 主机内存组成的分布式池,TTL 仅几分钟
  • 命中全局 KV 但 SWA KV 已过期时,用 Encoder SWA Bounded Replay 重放前缀最后 128 个 token,只补 SWA KV

两个因子相乘:持久化缓存不再存 SWA KV(约减半),剩下的全局 KV 又是 V4 的 1/4,合起来约为 V4-Flash 的 1/8。代价是重放得到的状态是近似的:从不同位置续接缓存时,后续算出的 KV 在数学上并不完全相同。报告称实验中对回答质量的影响可以忽略。

05 · 其他调整

其他架构调整一览

下面这些和「8B / 16B」没有直接关系,但都是这一代相对 V4 的结构变化。按需展开:

V4 引入的 mHC 在相邻 block 之间维持 n = 4 路残差流,每个 block 根据当前残差 X_l 预测混合系数 (A_l, B_l, C_l)。问题在于输入混合 A_l·X_l 要等 A_l 算完,而 A_l 需要对整个 X_l 做归约,所以必须读两遍 X_l。

Single-Pass mHC 让每个 block 使用上一个 block 算出的 A_{l-1} 做输入混合,依赖消失。部署时残差更新、输入混合、系数预测融合成一个 Mega-mHC kernel,残差只读一次、写一次,激活访存相对原四 kernel 实现减半。报告称这一错位带来的效果损失可以忽略。

196B 参数平均分成两个模块,放在第 1、14 层(从 0 计数)。每个模块使用 2、3、4 阶 n-gram,8 个哈希头,每个头一张约 16M 行的表(行数取互不相同的素数),表和投影都用 FP8。

因为地址只由 token 序列决定,推理时可以提前通过 RDMA 从主机内存预取,与第一个 Transformer block 的计算重叠。相对原版 Engram 去掉了短因果卷积,因为收益抵不上推理栈的复杂度。

drafter 由 3 个 Transformer block 组成(滑窗 128),一次前向并行给出 5 个草稿位置的 logits,再用一个轻量 Markov head 建模草稿 token 之间的依赖。置信度 head 预测每个位置被接受的概率,调度器结合引擎吞吐曲线,为每个请求动态选择验证长度。

与 V3 的 MTP 不同,主干预训练时不带 MTP。DSpark 在预训练之后冻结主干单独训练;post-training 时随主干一起更新,但不把梯度传回主干,这样它能持续对齐策略,同时加速在线服务和 RL rollout。

DeepSeek-ViT(32 层,1024 维)用 2D-RoPE 支持任意分辨率,把 patch embedding 的卷积换成线性投影以适配 Muon。图像特征经 3×3 pixel-unshuffle,视觉 token 数降到 1/9,支持约 1344×1344 输入,再由 2 层 MLP 投影到 5120 维。

多模态数据从语言模型预训练一开始就加入,纯文本与多模态 token 比例约 7 : 1。MoE 的无辅助损失负载均衡为图像 token 和文本 token 各维护一套偏置,避免两种模态的不均衡在总体统计里互相掩盖。

head-wise Muon:Query / Key 权重按 head 切开后各自做 Muon 更新,相当于给每个 head 不同的预条件器,以适应 head 之间的异质性。

Sinkhorn 平衡更新:对 196B 的 Engram 表用 Adam 会让优化器状态暴涨。V4.1 改为 Nesterov 动量后,交替对更新矩阵做行、列归一化(Sinkhorn),使行、列 RMS 近似为 1。它和 Muon 一样只需一个动量缓冲,报告称效果优于 Adam。

  • 主干:40 层(20 encoder + 20 decoder),hidden 5120,每层都是 MoE
  • MoE:1 共享 + 384 路由专家,每 token 选 6 个,专家中间维 2304,SwiGLU 截断阈值 10
  • 注意力:64 个 query head × 512 维,query 压缩维 1280;indexer 32 head × 128 维;稀疏注意力 Top-512;SWA 窗口 128
  • 训练:45T 多模态 token,从零开始用 64K 序列训练稀疏注意力(无 dense warmup),34T token 时扩展到 1M
  • 后训练:SFT → RL → on-policy distillation,报告明确说算法无创新,收益几乎全部来自数据与环境合成
06 · 证据与代价

效果如何,代价在哪

base 模型对比里,V4.1-Flash 以 V4-Pro 约 1/3 的总参数、约 1/4 的激活参数,在知识、推理和代码上接近或超过 V4-Pro,并全面优于 V4-Flash:

Base 模型V4-FlashV4-ProV4.1-Flash
激活参数 / 主干参数13B / 284B49B / 1.6T8B·16B / 552B
MMLU-Pro68.373.574.1
HumanEval69.576.879.4
MATH57.464.561.1
SimpleQA-Verified30.155.242.3
LongBench-V244.751.545.2

读这张表要注意:这是整模型对比,同时变了数据(45T 多模态语料、更强的清洗)、规模和多处结构,不是 CED 的消融。对于 CED 本身,报告只定性地说它「在减少近一半 prefill 计算的同时,性能与 baseline 相当」,没有给出同规模有无 CED 的对比数字。

输出侧其实更贵了

不对称是一种取舍,不是白赚。每个输出 token 激活 16B,比 V4-Flash 的 13B 还多:

单 token decode FLOPs 随上下文长度变化:V4.1-Flash 短上下文略高于 V4-Flash,长上下文几乎持平不增
原论文 Figure 2:单 token decode 计算量随上下文长度的变化。短上下文时 V4.1-Flash 略高于 V4-Flash,长上下文时几乎不增长。
  • 坐标:横轴是上下文长度 4K → 1M(对数),纵轴是单 token decode FLOPs(对数,按精度加权:BF16 = 1,FP8 = 0.5,FP4 = 0.25)。
  • 短上下文:V4.1-Flash(深蓝实线)在 V4-Flash(浅蓝)之上。每个输出 token 要过 40 层、激活 16B,线性层计算比 13B 的前代多。
  • 交叉点:两条线大约在 100K 量级相交;之后 V4-Flash 随长度明显上升,V4.1-Flash 几乎是平的。
  • 为什么平:注意力只读 Top-512 个条目加 128 窗口;分层索引让后续 indexer 的打分量与长度无关,只剩第一个 Full 层的全量打分随长度增长。从 4K 到 1M(256 倍),decode FLOPs 只增加约 1/4。
  • 一句话:V4.1 把成本从「输入」和「长上下文」挪向「每个输出 token 的固定开销」,这正是为 agent 负载做的取舍。

粗略感受一下这笔账(只比较激活参数 × token 数,近似线性层计算量,忽略注意力、replay 和投机解码):

输入为主:一轮 agent 工具调用

输入 4,000 / 输出 400

V4.1:4,000 × 8 + 400 × 16 = 38,400
V4-Flash:4,400 × 13 = 57,200
约为前代的 2/3。

输出为主:短问题、长回答

输入 200 / 输出 2,000

V4.1:200 × 8 + 2,000 × 16 = 33,600
V4-Flash:2,200 × 13 = 28,600
V4.1 反而更贵(短输入时 replay 开销占比还更高)。

这是按激活参数做的示意,不是报告给出的实测;两代模型能力不同,长上下文下 V4.1 的注意力开销也明显更低。

报告自己承认的边界

CED 让模型在 prefill 时每个 token 只激活 8B 参数,decode 时才激活 16B,这对输入密集型的 agent 场景尤其划算。 "This design enables the model to activate 8B parameters per token during prefill and 16B during decode, which is particularly cost-effective for input-heavy agentic scenarios."