阿里巴巴 AI Infra 一面 (1)
Q: Agent的架构设计有哪些要素?
一个完整的Agent系统包含以下核心组件和设计要素:
核心组件:
1. 规划(Planning / Reasoning)
- 任务分解:将复杂任务拆分为可执行的子任务序列
- 递归分解:大任务→子任务→子子任务
- DAG分解:识别可并行的子任务
- 推理范式:
- ReAct(Reasoning + Acting):思考→行动→观察循环
- Chain-of-Thought:逐步推理后执行
- Tree-of-Thought:多路径探索后选最优
- Reflection:执行后自我反思并修正
- 多步决策:维护目标堆栈,根据中间结果动态调整后续计划
2. 记忆(Memory)
- 短期记忆:当前对话上下文(prompt中的messages),容量受context window限制
- 长期记忆:
- 向量数据库存储历史经验和知识(Episodic Memory)
- 结构化存储用户偏好和实体关系(Semantic Memory)
- 技能/程序记忆(Procedural Memory):学到的成功策略
- 记忆管理:写入(什么信息值得记住)、检索(什么时候调取什么记忆)、遗忘(清理过期/矛盾信息)
3. 工具(Tools / Actions)
- API调用:搜索引擎、数据库查询、第三方服务
- 代码执行:Python/Shell代码运行环境
- 文件操作:读写文件、创建/修改文档
- 通信:与其他Agent或人类交互
- 工具描述:用自然语言描述工具的功能、参数、返回值(function calling schema)
- 工具选择:Agent根据当前状态和目标选择合适的工具
4. 观察-行动循环(Observation-Action Loop)
1 | while not task_complete: |
架构模式:
| 模式 | 结构 | 适用场景 | 代表 |
|---|---|---|---|
| 单Agent(ReAct) | 单一LLM循环推理执行 | 简单任务、工具调用 | ChatGPT Plugins |
| 多Agent协作 | 多个Agent角色分工 | 复杂任务分解 | CrewAI, AutoGen |
| 分层Agent | Manager调度 + Worker执行 | 大规模任务编排 | MetaGPT |
| 辩论式 | 多Agent讨论达成共识 | 需要多角度分析 | ChatDev |
工程挑战:
- Token效率:每步推理消耗token→长链推理成本高
- 错误累积:多步执行中早期错误会传播放大
- 幻觉控制:Agent可能”编造”工具参数或虚构执行结果
- 安全边界:限制Agent的行动空间防止有害操作
Q: RAG的检索如何实现?
RAG(Retrieval-Augmented Generation)完整Pipeline:
Stage 1: 索引构建(Offline)
1 | 文档集合 → 预处理 → 分块(Chunking) → Embedding → 向量数据库 |
- 预处理:格式解析(PDF/HTML/Markdown)、清洗(去噪、去格式标记)
- 分块策略(直接影响检索质量):
- 固定大小:512-1024 token/chunk(简单但可能切断语义)
- 语义分块:按段落/章节/句子边界切分
- 递归分块:先按大块切,大块太大再递归细分
- 重叠分块:相邻chunk有20-50%重叠(避免边界信息丢失)
- 典型chunk size:500-1000 token + 50-100 token overlap
- Embedding编码:
- 模型选择:BGE-large/GTE-large/E5(中文),OpenAI text-embedding-3(英文)
- 维度:768-1536维
- 需要注意:query和document可能需要不同的编码策略(如添加instruction prefix)
- 向量数据库:FAISS(单机高性能)/ Milvus(分布式)/ Pinecone(托管服务)/ ChromaDB(轻量)
Stage 2: 检索(Online)
1 | 用户Query → Query处理 → 向量检索(ANN) → 重排序 → Top-K结果 |
- ANN(近似最近邻)搜索:
- HNSW算法:recall 95%+,延迟<10ms(百万级数据)
- IVF-PQ:适合十亿级数据,内存开销更小
- Hybrid Search(混合检索):
- 向量检索(语义相似性)+ BM25(关键词匹配)
- 互补:向量擅长语义理解(同义词/上下位词),BM25擅长精确匹配(实体名/数字)
- Reciprocal Rank Fusion(RRF)合并两路结果
Stage 3: 增强生成(Online)
1 | Retrieved Docs + Original Query → Prompt Construction → LLM → Answer |
- Prompt模板设计:
1
2
3
4
5
6基于以下参考文档回答用户问题。如果文档中没有相关信息,请说明无法回答。
参考文档:
{retrieved_chunks}
用户问题:{query}
优化技术:
| 技术 | 作用 | 适用场景 |
|---|---|---|
| Query Rewriting | 改写用户问题使其更适合检索 | 口语化/模糊查询 |
| HyDE | 先让LLM生成假设性答案,用答案做检索 | 问题和文档表述差异大 |
| Reranker | 用Cross-Encoder对初筛结果精排 | 需要高精度top-K |
| Multi-hop Retrieval | 多轮检索逐步逼近答案 | 复杂推理问题 |
| Contextual Compression | 从长文档中提取与问题相关的片段 | chunk太长 |
| Parent Document Retrieval | 检索小chunk但返回其父文档 | 需要上下文 |
Q: 预训练数据清洗方法有哪些?
预训练数据质量直接决定模型能力。数据清洗是一个多阶段的pipeline:
1. 去重(Deduplication)— 最重要的步骤之一
| 方法 | 粒度 | 原理 | 适用规模 |
|---|---|---|---|
| Exact Dedup | 文档/段落 | Hash完全匹配 | 万亿token |
| MinHash + LSH | 文档 | 多个hash函数估计Jaccard相似度 | 万亿token |
| SimHash | 文档 | 高维特征hash为64bit指纹 | 万亿token |
| Suffix Array | 子串 | 删除高频重复子串(>50字符) | 千亿token |
| Semantic Dedup | 语义 | Embedding相似度去重 | 百亿token |
- 训练数据重复的危害:模型记忆重复内容而非学习泛化→质量下降、隐私泄露风险增加
- 实际比例:Common Crawl去重后通常剩余30-50%的数据
2. 质量过滤(Quality Filtering)
规则基过滤:
- 长度过滤:删除过短(<50字)或过长的异常文档
- 语言检测:用fasttext语言检测器过滤非目标语言
- 特殊字符比例:删除HTML标签/特殊符号/emoji占比>30%的文档
- 重复行/段落比例:删除内部重复严重的文档(如广告/SEO spam)
- 关键词黑名单:过滤明显的低质内容(导航栏文本、cookie声明等)
分类器过滤:
- 训练一个二分类器:正例=高质量数据(Wikipedia/书籍/论文),负例=随机网页
- 用分类器打分,保留score>阈值的文档
- GPT-3使用类似方法,只保留了大约6%的Common Crawl数据
3. 有害内容过滤
- 毒性检测模型(Perspective API / 自训练分类器)
- 敏感词/正则匹配过滤明显有害内容
- NSFW分类器过滤成人内容
- 多层过滤:先规则粗筛(快)→再模型精筛(慢但准)
4. 个人信息移除(PII Removal)
- 正则匹配:邮箱、电话号码、身份证号、银行卡号、IP地址
- NER模型:识别人名、地址等实体并替换为占位符
- 注意:某些PII格式因地区而异(如中国手机号11位、美国10位)
5. 领域平衡(Domain Balancing)
- 不同来源的数据量极不均衡:网页>>书籍>>代码>>论文
- 需要有意上采样高质量稀缺数据(如数学/代码/科学论文)
- 下采样低质量过多的数据(如社交媒体评论)
- 典型配比(参考LLaMA):网页67% + 代码4.5% + Wikipedia 4.5% + 书籍4.5% + 论文2.5% + 其他
6. 数据清洗的质量验证
- 人工抽样检查(随机抽取1000条评估)
- 在小模型上做消融实验验证清洗策略的效果
- 监控模型训练指标(如特定benchmark的score)随数据调整的变化
Q: Group Query Attention的作用?
GQA(Grouped Query Attention)是MHA和MQA之间的折中方案,将原始的多个query head分组,每组共享一套KV head。
核心动机与原理:
大模型推理的decode阶段是memory-bound的——每生成一个token需要加载所有层的KV Cache,而计算量很小(只做一次向量-矩阵乘)。减少KV head数量直接减少decode阶段的内存访问量,从而加速生成。
1 | MHA: Q_heads: [h1,h2,h3,h4,h5,h6,h7,h8] |
具体收益:
1. KV Cache显存减少
- GQA-g(g组KV head):KV Cache = MHA的
g/num_heads倍 - LLaMA-2 70B(32 Q heads, 8 KV heads):KV Cache是MHA的 8/32 = 25%
- 意味着同等显存可以服务4倍的并发请求或4倍长的序列
2. Decode吞吐提升
- Decode阶段瓶颈是加载KV Cache的带宽
- KV head数减少→加载量减少→单token生成更快
- A100上LLaMA-70B实测:GQA-8比MHA decode快约3.5倍
3. 模型质量损失极小
- Google的研究:GQA-8质量只比MHA差0.1-0.3%(困惑度差异)
- MQA(只有1个KV head)质量损失更明显(0.5-1%)
- GQA在多数benchmark上与MHA几乎无差异
GQA在主流模型中的使用:
| 模型 | Q heads | KV heads | 分组比例 |
|---|---|---|---|
| LLaMA-2 70B | 64 | 8 | 8:1 |
| LLaMA-3 8B | 32 | 8 | 4:1 |
| LLaMA-3 70B | 64 | 8 | 8:1 |
| Qwen-2.5 72B | 64 | 8 | 8:1 |
| Mistral 7B | 32 | 8 | 4:1 |
从MHA转换到GQA的方法:
- 从头训练(最佳质量)
- 从MHA checkpoint继续训练为GQA:将同组内的KV head均值池化后合并,再微调少量steps恢复质量
Q: 手撕:实现LRU Cache?
(编程题)
Q: MoE架构的专家路由是对每个token路由还是对每个序列路由?
对每个token独立路由。
具体过程:
1 | # 对batch中每个token独立计算路由 |
为什么是token级而非序列级?
- 语义粒度:同一句话中不同token的语义属性差异大
- “The capital of France is Paris” → “capital”可能路由到地理专家,”France”路由到实体专家,”is”路由到语法专家
- 容量利用:token级路由使各专家的负载更均匀分布
- 模型能力:细粒度路由让模型学到更精确的专家-语义映射
- 实验证据:所有SOTA MoE模型(Switch Transformer、Mixtral、DeepSeek)都使用token级路由
序列级路由的问题:
- 一个序列通常包含多个主题/功能→一个专家组合无法覆盖
- 负载均衡更难保证(序列长度不一)
- 浪费专家容量
但也有变体:
- Segment-level routing:将序列分为固定大小的segment(如128 tokens),每个segment统一路由。是token级和序列级的折中
- Expert Choice中每个专家选择token时是跨序列的(但仍是token粒度的选择)
Q: KV Cache的优化方法有哪些?
KV Cache是LLM推理中最大的显存消耗源之一,优化方法涵盖结构设计、压缩、管理调度三个层面:
结构化优化(模型设计时决定):
1. GQA/MQA — 减少KV head数量
- 效果:KV Cache线性减少(8 KV heads → 1/4显存)
- 限制:需要预训练时确定,无法后期修改
- 推荐:GQA-8(4-8倍压缩,质量损失最小)
2. MLA — 低秩压缩
- DeepSeek-V2/V3的方案:将KV投影到低维latent空间
- 压缩比:5-20倍(latent_dim远小于原始KV维度)
- 代价:推理时需要额外的解压计算(但通常很快)
存储优化(部署时选择):
3. 量化 — 降低每个元素的精度
| 量化方案 | 压缩比 | 质量影响 | 实现 |
|---|---|---|---|
| FP16→FP8 | 2x | 极小 | H100原生支持 |
| FP16→INT8 | 2x | 小(per-channel) | CUDA kernel |
| FP16→INT4 | 4x | 中等 | 需要group quantization |
| FP16→INT2 | 8x | 较大 | 研究阶段 |
- 关键:KV Cache量化比权重量化更敏感(对attention score直接影响)
- 推荐:INT8 per-channel是目前生产可用的最佳平衡点
4. 前缀共享(Prefix Caching)
- 场景:多个请求有相同的system prompt或few-shot examples
- 实现:用Trie树/Hash表索引已计算的前缀KV Cache,新请求直接复用
- 效果:如system prompt占2K token,每个请求节省2K token的KV Cache计算和存储
- vLLM的Automatic Prefix Caching / SGLang的RadixAttention
5. Offload — 分层存储
- GPU显存存放活跃请求的KV Cache
- CPU内存存放暂停/等待的请求(如被preempt的请求)
- NVMe SSD存放长期不活跃的历史KV
- 需要时通过PCIe异步预取到GPU
- 延迟:GPU→CPU
10us,CPU→GPU10us,SSD→GPU~100us
调度优化(运行时管理):
6. PagedAttention — 消除内存碎片
- 问题:不同请求的KV Cache长度不同,连续分配造成严重碎片
- 方案:将KV Cache分为固定大小的page(如16 tokens/page),通过page table管理
- 类比:OS的虚拟内存管理(logical address → physical address)
- 效果:显存利用率从20-40%提升到>95%
- 代价:需要修改attention kernel支持非连续KV Cache(scatter/gather)
7. 驱逐策略 — 有选择地丢弃
- H2O(Heavy Hitter Oracle):
- 观察:少量token(~5%)贡献了大部分attention mass(Heavy Hitters)
- 策略:保留累计attention score最高的token + 最近的token,驱逐其余
- 效果:保留20%的KV Cache仍能维持>95%的模型质量
- Scissorhands:利用attention pattern在不同layer间的一致性
- StreamingLLM:保留attention sink(前4个token)+ 滑动窗口
- 发现:前几个token无论内容如何都积累大量attention(充当”垃圾桶”),删除会导致输出崩溃
- 适用:超长流式对话场景,固定显存运行无限长度
8. 投机解码中的KV Cache管理
- Draft model生成多个候选token的KV Cache
- Target model验证后,被拒绝的token对应的KV Cache需要回滚
- 实现:维护KV Cache的版本/checkpoint,验证失败时快速恢复